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 | |
Steven Valdez | cb96654 | 2016-08-17 16:56:14 -0400 | [diff] [blame] | 88 | #include "../internal.h" |
| 89 | #include "../test/file_test.h" |
| 90 | #include "../test/scoped_types.h" |
| 91 | #include "../test/test_util.h" |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 92 | |
| 93 | |
David Benjamin | e701f16 | 2015-12-03 11:04:24 -0500 | [diff] [blame] | 94 | static int HexToBIGNUM(ScopedBIGNUM *out, const char *in) { |
| 95 | BIGNUM *raw = NULL; |
| 96 | int ret = BN_hex2bn(&raw, in); |
| 97 | out->reset(raw); |
| 98 | return ret; |
| 99 | } |
| 100 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 101 | static ScopedBIGNUM GetBIGNUM(FileTest *t, const char *attribute) { |
| 102 | std::string hex; |
| 103 | if (!t->GetAttribute(&hex, attribute)) { |
| 104 | return nullptr; |
| 105 | } |
| 106 | |
| 107 | ScopedBIGNUM ret; |
| 108 | if (HexToBIGNUM(&ret, hex.c_str()) != static_cast<int>(hex.size())) { |
| 109 | t->PrintLine("Could not decode '%s'.", hex.c_str()); |
| 110 | return nullptr; |
| 111 | } |
| 112 | return ret; |
| 113 | } |
| 114 | |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 115 | static bool GetInt(FileTest *t, int *out, const char *attribute) { |
| 116 | ScopedBIGNUM ret = GetBIGNUM(t, attribute); |
| 117 | if (!ret) { |
| 118 | return false; |
| 119 | } |
| 120 | |
| 121 | BN_ULONG word = BN_get_word(ret.get()); |
| 122 | if (word > INT_MAX) { |
| 123 | return false; |
| 124 | } |
| 125 | |
| 126 | *out = static_cast<int>(word); |
| 127 | return true; |
| 128 | } |
| 129 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 130 | static bool ExpectBIGNUMsEqual(FileTest *t, const char *operation, |
| 131 | const BIGNUM *expected, const BIGNUM *actual) { |
| 132 | if (BN_cmp(expected, actual) == 0) { |
| 133 | return true; |
| 134 | } |
| 135 | |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame] | 136 | ScopedOpenSSLString expected_str(BN_bn2hex(expected)); |
| 137 | ScopedOpenSSLString actual_str(BN_bn2hex(actual)); |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 138 | if (!expected_str || !actual_str) { |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 139 | return false; |
| 140 | } |
| 141 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 142 | t->PrintLine("Got %s =", operation); |
| 143 | t->PrintLine("\t%s", actual_str.get()); |
| 144 | t->PrintLine("wanted:"); |
| 145 | t->PrintLine("\t%s", expected_str.get()); |
| 146 | return false; |
| 147 | } |
| 148 | |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 149 | static bool TestSum(FileTest *t, BN_CTX *ctx) { |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 150 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 151 | ScopedBIGNUM b = GetBIGNUM(t, "B"); |
| 152 | ScopedBIGNUM sum = GetBIGNUM(t, "Sum"); |
| 153 | if (!a || !b || !sum) { |
| 154 | return false; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 155 | } |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 156 | |
| 157 | ScopedBIGNUM ret(BN_new()); |
| 158 | if (!ret || |
| 159 | !BN_add(ret.get(), a.get(), b.get()) || |
| 160 | !ExpectBIGNUMsEqual(t, "A + B", sum.get(), ret.get()) || |
| 161 | !BN_sub(ret.get(), sum.get(), a.get()) || |
| 162 | !ExpectBIGNUMsEqual(t, "Sum - A", b.get(), ret.get()) || |
| 163 | !BN_sub(ret.get(), sum.get(), b.get()) || |
| 164 | !ExpectBIGNUMsEqual(t, "Sum - B", a.get(), ret.get())) { |
| 165 | return false; |
| 166 | } |
| 167 | |
Brian Smith | e4bf8b3 | 2016-07-01 13:36:06 -1000 | [diff] [blame] | 168 | // Test that the functions work when |r| and |a| point to the same |BIGNUM|, |
| 169 | // or when |r| and |b| point to the same |BIGNUM|. TODO: Test the case where |
| 170 | // all of |r|, |a|, and |b| point to the same |BIGNUM|. |
| 171 | if (!BN_copy(ret.get(), a.get()) || |
| 172 | !BN_add(ret.get(), ret.get(), b.get()) || |
| 173 | !ExpectBIGNUMsEqual(t, "A + B (r is a)", sum.get(), ret.get()) || |
| 174 | !BN_copy(ret.get(), b.get()) || |
| 175 | !BN_add(ret.get(), a.get(), ret.get()) || |
| 176 | !ExpectBIGNUMsEqual(t, "A + B (r is b)", sum.get(), ret.get()) || |
| 177 | !BN_copy(ret.get(), sum.get()) || |
| 178 | !BN_sub(ret.get(), ret.get(), a.get()) || |
| 179 | !ExpectBIGNUMsEqual(t, "Sum - A (r is a)", b.get(), ret.get()) || |
| 180 | !BN_copy(ret.get(), a.get()) || |
| 181 | !BN_sub(ret.get(), sum.get(), ret.get()) || |
| 182 | !ExpectBIGNUMsEqual(t, "Sum - A (r is b)", b.get(), ret.get()) || |
| 183 | !BN_copy(ret.get(), sum.get()) || |
| 184 | !BN_sub(ret.get(), ret.get(), b.get()) || |
| 185 | !ExpectBIGNUMsEqual(t, "Sum - B (r is a)", a.get(), ret.get()) || |
| 186 | !BN_copy(ret.get(), b.get()) || |
| 187 | !BN_sub(ret.get(), sum.get(), ret.get()) || |
| 188 | !ExpectBIGNUMsEqual(t, "Sum - B (r is b)", a.get(), ret.get())) { |
| 189 | return false; |
| 190 | } |
| 191 | |
Brian Smith | 3d4030b | 2016-07-01 14:12:40 -1000 | [diff] [blame] | 192 | // Test |BN_uadd| and |BN_usub| with the prerequisites they are documented as |
| 193 | // having. Note that these functions are frequently used when the |
| 194 | // prerequisites don't hold. In those cases, they are supposed to work as if |
| 195 | // the prerequisite hold, but we don't test that yet. TODO: test that. |
| 196 | if (!BN_is_negative(a.get()) && |
| 197 | !BN_is_negative(b.get()) && BN_cmp(a.get(), b.get()) >= 0) { |
| 198 | if (!BN_uadd(ret.get(), a.get(), b.get()) || |
| 199 | !ExpectBIGNUMsEqual(t, "A +u B", sum.get(), ret.get()) || |
| 200 | !BN_usub(ret.get(), sum.get(), a.get()) || |
| 201 | !ExpectBIGNUMsEqual(t, "Sum -u A", b.get(), ret.get()) || |
| 202 | !BN_usub(ret.get(), sum.get(), b.get()) || |
| 203 | !ExpectBIGNUMsEqual(t, "Sum -u B", a.get(), ret.get())) { |
| 204 | return false; |
| 205 | } |
| 206 | |
| 207 | // Test that the functions work when |r| and |a| point to the same |BIGNUM|, |
| 208 | // or when |r| and |b| point to the same |BIGNUM|. TODO: Test the case where |
| 209 | // all of |r|, |a|, and |b| point to the same |BIGNUM|. |
| 210 | if (!BN_copy(ret.get(), a.get()) || |
| 211 | !BN_uadd(ret.get(), ret.get(), b.get()) || |
| 212 | !ExpectBIGNUMsEqual(t, "A +u B (r is a)", sum.get(), ret.get()) || |
| 213 | !BN_copy(ret.get(), b.get()) || |
| 214 | !BN_uadd(ret.get(), a.get(), ret.get()) || |
| 215 | !ExpectBIGNUMsEqual(t, "A +u B (r is b)", sum.get(), ret.get()) || |
| 216 | !BN_copy(ret.get(), sum.get()) || |
| 217 | !BN_usub(ret.get(), ret.get(), a.get()) || |
| 218 | !ExpectBIGNUMsEqual(t, "Sum -u A (r is a)", b.get(), ret.get()) || |
| 219 | !BN_copy(ret.get(), a.get()) || |
| 220 | !BN_usub(ret.get(), sum.get(), ret.get()) || |
| 221 | !ExpectBIGNUMsEqual(t, "Sum -u A (r is b)", b.get(), ret.get()) || |
| 222 | !BN_copy(ret.get(), sum.get()) || |
| 223 | !BN_usub(ret.get(), ret.get(), b.get()) || |
| 224 | !ExpectBIGNUMsEqual(t, "Sum -u B (r is a)", a.get(), ret.get()) || |
| 225 | !BN_copy(ret.get(), b.get()) || |
| 226 | !BN_usub(ret.get(), sum.get(), ret.get()) || |
| 227 | !ExpectBIGNUMsEqual(t, "Sum -u B (r is b)", a.get(), ret.get())) { |
| 228 | return false; |
| 229 | } |
| 230 | } |
| 231 | |
Brian Smith | fe47ba2 | 2016-07-01 11:46:21 -1000 | [diff] [blame] | 232 | // Test with |BN_add_word| and |BN_sub_word| if |b| is small enough. |
| 233 | BN_ULONG b_word = BN_get_word(b.get()); |
| 234 | if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) { |
| 235 | if (!BN_copy(ret.get(), a.get()) || |
| 236 | !BN_add_word(ret.get(), b_word) || |
| 237 | !ExpectBIGNUMsEqual(t, "A + B (word)", sum.get(), ret.get()) || |
| 238 | !BN_copy(ret.get(), sum.get()) || |
| 239 | !BN_sub_word(ret.get(), b_word) || |
| 240 | !ExpectBIGNUMsEqual(t, "Sum - B (word)", a.get(), ret.get())) { |
| 241 | return false; |
| 242 | } |
| 243 | } |
| 244 | |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 245 | return true; |
| 246 | } |
| 247 | |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 248 | static bool TestLShift1(FileTest *t, BN_CTX *ctx) { |
| 249 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 250 | ScopedBIGNUM lshift1 = GetBIGNUM(t, "LShift1"); |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 251 | ScopedBIGNUM zero(BN_new()); |
| 252 | if (!a || !lshift1 || !zero) { |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 253 | return false; |
| 254 | } |
| 255 | |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 256 | BN_zero(zero.get()); |
| 257 | |
| 258 | ScopedBIGNUM ret(BN_new()), two(BN_new()), remainder(BN_new()); |
| 259 | if (!ret || !two || !remainder || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 260 | !BN_set_word(two.get(), 2) || |
| 261 | !BN_add(ret.get(), a.get(), a.get()) || |
| 262 | !ExpectBIGNUMsEqual(t, "A + A", lshift1.get(), ret.get()) || |
| 263 | !BN_mul(ret.get(), a.get(), two.get(), ctx) || |
| 264 | !ExpectBIGNUMsEqual(t, "A * 2", lshift1.get(), ret.get()) || |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 265 | !BN_div(ret.get(), remainder.get(), lshift1.get(), two.get(), ctx) || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 266 | !ExpectBIGNUMsEqual(t, "LShift1 / 2", a.get(), ret.get()) || |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 267 | !ExpectBIGNUMsEqual(t, "LShift1 % 2", zero.get(), remainder.get()) || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 268 | !BN_lshift1(ret.get(), a.get()) || |
| 269 | !ExpectBIGNUMsEqual(t, "A << 1", lshift1.get(), ret.get()) || |
| 270 | !BN_rshift1(ret.get(), lshift1.get()) || |
| 271 | !ExpectBIGNUMsEqual(t, "LShift >> 1", a.get(), ret.get()) || |
| 272 | !BN_rshift1(ret.get(), lshift1.get()) || |
| 273 | !ExpectBIGNUMsEqual(t, "LShift >> 1", a.get(), ret.get())) { |
| 274 | return false; |
| 275 | } |
| 276 | |
| 277 | // Set the LSB to 1 and test rshift1 again. |
David Benjamin | 56cbbe5b | 2016-07-02 14:24:30 -0400 | [diff] [blame] | 278 | if (!BN_set_bit(lshift1.get(), 0) || |
| 279 | !BN_div(ret.get(), nullptr /* rem */, lshift1.get(), two.get(), ctx) || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 280 | !ExpectBIGNUMsEqual(t, "(LShift1 | 1) / 2", a.get(), ret.get()) || |
| 281 | !BN_rshift1(ret.get(), lshift1.get()) || |
| 282 | !ExpectBIGNUMsEqual(t, "(LShift | 1) >> 1", a.get(), ret.get())) { |
| 283 | return false; |
| 284 | } |
| 285 | |
| 286 | return true; |
| 287 | } |
| 288 | |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 289 | static bool TestLShift(FileTest *t, BN_CTX *ctx) { |
| 290 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 291 | ScopedBIGNUM lshift = GetBIGNUM(t, "LShift"); |
Adam Langley | d42e4b2 | 2016-06-27 15:57:57 -0700 | [diff] [blame] | 292 | int n = 0; |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 293 | if (!a || !lshift || !GetInt(t, &n, "N")) { |
| 294 | return false; |
| 295 | } |
| 296 | |
| 297 | ScopedBIGNUM ret(BN_new()); |
| 298 | if (!ret || |
| 299 | !BN_lshift(ret.get(), a.get(), n) || |
| 300 | !ExpectBIGNUMsEqual(t, "A << N", lshift.get(), ret.get()) || |
| 301 | !BN_rshift(ret.get(), lshift.get(), n) || |
| 302 | !ExpectBIGNUMsEqual(t, "A >> N", a.get(), ret.get())) { |
| 303 | return false; |
| 304 | } |
| 305 | |
| 306 | return true; |
| 307 | } |
| 308 | |
| 309 | static bool TestRShift(FileTest *t, BN_CTX *ctx) { |
| 310 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 311 | ScopedBIGNUM rshift = GetBIGNUM(t, "RShift"); |
Adam Langley | d42e4b2 | 2016-06-27 15:57:57 -0700 | [diff] [blame] | 312 | int n = 0; |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 313 | if (!a || !rshift || !GetInt(t, &n, "N")) { |
| 314 | return false; |
| 315 | } |
| 316 | |
| 317 | ScopedBIGNUM ret(BN_new()); |
| 318 | if (!ret || |
| 319 | !BN_rshift(ret.get(), a.get(), n) || |
| 320 | !ExpectBIGNUMsEqual(t, "A >> N", rshift.get(), ret.get())) { |
| 321 | return false; |
| 322 | } |
| 323 | |
| 324 | return true; |
| 325 | } |
| 326 | |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 327 | static bool TestSquare(FileTest *t, BN_CTX *ctx) { |
| 328 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 329 | ScopedBIGNUM square = GetBIGNUM(t, "Square"); |
| 330 | ScopedBIGNUM zero(BN_new()); |
| 331 | if (!a || !square || !zero) { |
| 332 | return false; |
| 333 | } |
| 334 | |
| 335 | BN_zero(zero.get()); |
| 336 | |
| 337 | ScopedBIGNUM ret(BN_new()), remainder(BN_new()); |
| 338 | if (!ret || |
| 339 | !BN_sqr(ret.get(), a.get(), ctx) || |
| 340 | !ExpectBIGNUMsEqual(t, "A^2", square.get(), ret.get()) || |
| 341 | !BN_mul(ret.get(), a.get(), a.get(), ctx) || |
| 342 | !ExpectBIGNUMsEqual(t, "A * A", square.get(), ret.get()) || |
| 343 | !BN_div(ret.get(), remainder.get(), square.get(), a.get(), ctx) || |
| 344 | !ExpectBIGNUMsEqual(t, "Square / A", a.get(), ret.get()) || |
| 345 | !ExpectBIGNUMsEqual(t, "Square % A", zero.get(), remainder.get())) { |
| 346 | return false; |
| 347 | } |
| 348 | |
| 349 | BN_set_negative(a.get(), 0); |
| 350 | if (!BN_sqrt(ret.get(), square.get(), ctx) || |
| 351 | !ExpectBIGNUMsEqual(t, "sqrt(Square)", a.get(), ret.get())) { |
| 352 | return false; |
| 353 | } |
| 354 | |
David Benjamin | 28a8c2f | 2016-07-02 20:48:45 -0400 | [diff] [blame] | 355 | // BN_sqrt should fail on non-squares and negative numbers. |
| 356 | if (!BN_is_zero(square.get())) { |
| 357 | ScopedBIGNUM tmp(BN_new()); |
| 358 | if (!tmp || !BN_copy(tmp.get(), square.get())) { |
| 359 | return false; |
| 360 | } |
| 361 | BN_set_negative(tmp.get(), 1); |
| 362 | |
| 363 | if (BN_sqrt(ret.get(), tmp.get(), ctx)) { |
| 364 | t->PrintLine("BN_sqrt succeeded on a negative number"); |
| 365 | return false; |
| 366 | } |
| 367 | ERR_clear_error(); |
| 368 | |
| 369 | BN_set_negative(tmp.get(), 0); |
| 370 | if (!BN_add(tmp.get(), tmp.get(), BN_value_one())) { |
| 371 | return false; |
| 372 | } |
| 373 | if (BN_sqrt(ret.get(), tmp.get(), ctx)) { |
| 374 | t->PrintLine("BN_sqrt succeeded on a non-square"); |
| 375 | return false; |
| 376 | } |
| 377 | ERR_clear_error(); |
| 378 | } |
| 379 | |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 380 | return true; |
| 381 | } |
| 382 | |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 383 | static bool TestProduct(FileTest *t, BN_CTX *ctx) { |
| 384 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 385 | ScopedBIGNUM b = GetBIGNUM(t, "B"); |
| 386 | ScopedBIGNUM product = GetBIGNUM(t, "Product"); |
| 387 | ScopedBIGNUM zero(BN_new()); |
| 388 | if (!a || !b || !product || !zero) { |
| 389 | return false; |
| 390 | } |
| 391 | |
| 392 | BN_zero(zero.get()); |
| 393 | |
| 394 | ScopedBIGNUM ret(BN_new()), remainder(BN_new()); |
| 395 | if (!ret || !remainder || |
| 396 | !BN_mul(ret.get(), a.get(), b.get(), ctx) || |
| 397 | !ExpectBIGNUMsEqual(t, "A * B", product.get(), ret.get()) || |
| 398 | !BN_div(ret.get(), remainder.get(), product.get(), a.get(), ctx) || |
| 399 | !ExpectBIGNUMsEqual(t, "Product / A", b.get(), ret.get()) || |
| 400 | !ExpectBIGNUMsEqual(t, "Product % A", zero.get(), remainder.get()) || |
| 401 | !BN_div(ret.get(), remainder.get(), product.get(), b.get(), ctx) || |
| 402 | !ExpectBIGNUMsEqual(t, "Product / B", a.get(), ret.get()) || |
| 403 | !ExpectBIGNUMsEqual(t, "Product % B", zero.get(), remainder.get())) { |
| 404 | return false; |
| 405 | } |
| 406 | |
| 407 | return true; |
| 408 | } |
| 409 | |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 410 | static bool TestQuotient(FileTest *t, BN_CTX *ctx) { |
| 411 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 412 | ScopedBIGNUM b = GetBIGNUM(t, "B"); |
| 413 | ScopedBIGNUM quotient = GetBIGNUM(t, "Quotient"); |
| 414 | ScopedBIGNUM remainder = GetBIGNUM(t, "Remainder"); |
| 415 | if (!a || !b || !quotient || !remainder) { |
| 416 | return false; |
| 417 | } |
| 418 | |
| 419 | ScopedBIGNUM ret(BN_new()), ret2(BN_new()); |
| 420 | if (!ret || !ret2 || |
| 421 | !BN_div(ret.get(), ret2.get(), a.get(), b.get(), ctx) || |
| 422 | !ExpectBIGNUMsEqual(t, "A / B", quotient.get(), ret.get()) || |
| 423 | !ExpectBIGNUMsEqual(t, "A % B", remainder.get(), ret2.get()) || |
| 424 | !BN_mul(ret.get(), quotient.get(), b.get(), ctx) || |
| 425 | !BN_add(ret.get(), ret.get(), remainder.get()) || |
| 426 | !ExpectBIGNUMsEqual(t, "Quotient * B + Remainder", a.get(), ret.get())) { |
| 427 | return false; |
| 428 | } |
| 429 | |
| 430 | // Test with |BN_mod_word| and |BN_div_word| if the divisor is small enough. |
| 431 | BN_ULONG b_word = BN_get_word(b.get()); |
| 432 | if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) { |
| 433 | BN_ULONG remainder_word = BN_get_word(remainder.get()); |
| 434 | assert(remainder_word != (BN_ULONG)-1); |
| 435 | if (!BN_copy(ret.get(), a.get())) { |
| 436 | return false; |
| 437 | } |
| 438 | BN_ULONG ret_word = BN_div_word(ret.get(), b_word); |
| 439 | if (ret_word != remainder_word) { |
| 440 | t->PrintLine("Got A %% B (word) = " BN_HEX_FMT1 ", wanted " BN_HEX_FMT1 |
| 441 | "\n", |
| 442 | ret_word, remainder_word); |
| 443 | return false; |
| 444 | } |
| 445 | if (!ExpectBIGNUMsEqual(t, "A / B (word)", quotient.get(), ret.get())) { |
| 446 | return false; |
| 447 | } |
| 448 | |
| 449 | ret_word = BN_mod_word(a.get(), b_word); |
| 450 | if (ret_word != remainder_word) { |
| 451 | t->PrintLine("Got A %% B (word) = " BN_HEX_FMT1 ", wanted " BN_HEX_FMT1 |
| 452 | "\n", |
| 453 | ret_word, remainder_word); |
| 454 | return false; |
| 455 | } |
| 456 | } |
| 457 | |
David Benjamin | e8317a5 | 2016-07-02 20:14:30 -0400 | [diff] [blame] | 458 | // Test BN_nnmod. |
| 459 | if (!BN_is_negative(b.get())) { |
| 460 | ScopedBIGNUM nnmod(BN_new()); |
| 461 | if (!nnmod || |
| 462 | !BN_copy(nnmod.get(), remainder.get()) || |
| 463 | (BN_is_negative(nnmod.get()) && |
| 464 | !BN_add(nnmod.get(), nnmod.get(), b.get())) || |
| 465 | !BN_nnmod(ret.get(), a.get(), b.get(), ctx) || |
| 466 | !ExpectBIGNUMsEqual(t, "A % B (non-negative)", nnmod.get(), |
| 467 | ret.get())) { |
| 468 | return false; |
| 469 | } |
| 470 | } |
| 471 | |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 472 | return true; |
| 473 | } |
| 474 | |
David Benjamin | 5a13e40 | 2016-07-02 20:33:07 -0400 | [diff] [blame] | 475 | static bool TestModMul(FileTest *t, BN_CTX *ctx) { |
| 476 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 477 | ScopedBIGNUM b = GetBIGNUM(t, "B"); |
| 478 | ScopedBIGNUM m = GetBIGNUM(t, "M"); |
| 479 | ScopedBIGNUM mod_mul = GetBIGNUM(t, "ModMul"); |
| 480 | if (!a || !b || !m || !mod_mul) { |
| 481 | return false; |
| 482 | } |
| 483 | |
| 484 | ScopedBIGNUM ret(BN_new()); |
| 485 | if (!ret || |
| 486 | !BN_mod_mul(ret.get(), a.get(), b.get(), m.get(), ctx) || |
| 487 | !ExpectBIGNUMsEqual(t, "A * B (mod M)", mod_mul.get(), ret.get())) { |
| 488 | return false; |
| 489 | } |
| 490 | |
| 491 | if (BN_is_odd(m.get())) { |
| 492 | // Reduce |a| and |b| and test the Montgomery version. |
| 493 | ScopedBN_MONT_CTX mont(BN_MONT_CTX_new()); |
| 494 | ScopedBIGNUM a_tmp(BN_new()), b_tmp(BN_new()); |
| 495 | if (!mont || !a_tmp || !b_tmp || |
| 496 | !BN_MONT_CTX_set(mont.get(), m.get(), ctx) || |
| 497 | !BN_nnmod(a_tmp.get(), a.get(), m.get(), ctx) || |
| 498 | !BN_nnmod(b_tmp.get(), b.get(), m.get(), ctx) || |
| 499 | !BN_to_montgomery(a_tmp.get(), a_tmp.get(), mont.get(), ctx) || |
| 500 | !BN_to_montgomery(b_tmp.get(), b_tmp.get(), mont.get(), ctx) || |
| 501 | !BN_mod_mul_montgomery(ret.get(), a_tmp.get(), b_tmp.get(), mont.get(), |
| 502 | ctx) || |
| 503 | !BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx) || |
| 504 | !ExpectBIGNUMsEqual(t, "A * B (mod M) (Montgomery)", |
| 505 | mod_mul.get(), ret.get())) { |
| 506 | return false; |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | return true; |
| 511 | } |
| 512 | |
David Benjamin | 45a8c8a | 2016-07-03 10:05:56 -0400 | [diff] [blame] | 513 | static bool TestModExp(FileTest *t, BN_CTX *ctx) { |
| 514 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 515 | ScopedBIGNUM e = GetBIGNUM(t, "E"); |
| 516 | ScopedBIGNUM m = GetBIGNUM(t, "M"); |
| 517 | ScopedBIGNUM mod_exp = GetBIGNUM(t, "ModExp"); |
| 518 | if (!a || !e || !m || !mod_exp) { |
| 519 | return false; |
| 520 | } |
| 521 | |
| 522 | ScopedBIGNUM ret(BN_new()); |
| 523 | if (!ret || |
| 524 | !BN_mod_exp(ret.get(), a.get(), e.get(), m.get(), ctx) || |
| 525 | !ExpectBIGNUMsEqual(t, "A ^ E (mod M)", mod_exp.get(), ret.get())) { |
| 526 | return false; |
| 527 | } |
| 528 | |
| 529 | if (BN_is_odd(m.get())) { |
| 530 | if (!BN_mod_exp_mont(ret.get(), a.get(), e.get(), m.get(), ctx, NULL) || |
| 531 | !ExpectBIGNUMsEqual(t, "A ^ E (mod M) (Montgomery)", mod_exp.get(), |
| 532 | ret.get()) || |
| 533 | !BN_mod_exp_mont_consttime(ret.get(), a.get(), e.get(), m.get(), ctx, |
| 534 | NULL) || |
| 535 | !ExpectBIGNUMsEqual(t, "A ^ E (mod M) (constant-time)", mod_exp.get(), |
| 536 | ret.get())) { |
| 537 | return false; |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | return true; |
| 542 | } |
| 543 | |
David Benjamin | 4cb00ba | 2016-07-03 11:52:58 -0400 | [diff] [blame] | 544 | static bool TestExp(FileTest *t, BN_CTX *ctx) { |
| 545 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 546 | ScopedBIGNUM e = GetBIGNUM(t, "E"); |
| 547 | ScopedBIGNUM exp = GetBIGNUM(t, "Exp"); |
| 548 | if (!a || !e || !exp) { |
| 549 | return false; |
| 550 | } |
| 551 | |
| 552 | ScopedBIGNUM ret(BN_new()); |
| 553 | if (!ret || |
| 554 | !BN_exp(ret.get(), a.get(), e.get(), ctx) || |
| 555 | !ExpectBIGNUMsEqual(t, "A ^ E", exp.get(), ret.get())) { |
| 556 | return false; |
| 557 | } |
| 558 | |
| 559 | return true; |
| 560 | } |
| 561 | |
David Benjamin | ffb7adc | 2016-07-03 15:19:20 -0400 | [diff] [blame] | 562 | static bool TestModSqrt(FileTest *t, BN_CTX *ctx) { |
| 563 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 564 | ScopedBIGNUM p = GetBIGNUM(t, "P"); |
| 565 | ScopedBIGNUM mod_sqrt = GetBIGNUM(t, "ModSqrt"); |
| 566 | if (!a || !p || !mod_sqrt) { |
| 567 | return false; |
| 568 | } |
| 569 | |
| 570 | ScopedBIGNUM ret(BN_new()); |
| 571 | ScopedBIGNUM ret2(BN_new()); |
| 572 | if (!ret || |
| 573 | !ret2 || |
| 574 | !BN_mod_sqrt(ret.get(), a.get(), p.get(), ctx) || |
| 575 | // There are two possible answers. |
| 576 | !BN_sub(ret2.get(), p.get(), ret.get())) { |
| 577 | return false; |
| 578 | } |
| 579 | |
| 580 | if (BN_cmp(ret2.get(), mod_sqrt.get()) != 0 && |
| 581 | !ExpectBIGNUMsEqual(t, "sqrt(A) (mod P)", mod_sqrt.get(), ret.get())) { |
| 582 | return false; |
| 583 | } |
| 584 | |
| 585 | return true; |
| 586 | } |
| 587 | |
Brian Smith | 286fbf2 | 2016-07-25 14:20:32 -1000 | [diff] [blame] | 588 | static bool TestModInv(FileTest *t, BN_CTX *ctx) { |
| 589 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 590 | ScopedBIGNUM m = GetBIGNUM(t, "M"); |
| 591 | ScopedBIGNUM mod_inv = GetBIGNUM(t, "ModInv"); |
| 592 | if (!a || !m || !mod_inv) { |
| 593 | return false; |
| 594 | } |
| 595 | |
| 596 | ScopedBIGNUM ret(BN_new()); |
| 597 | if (!ret || |
| 598 | !BN_mod_inverse(ret.get(), a.get(), m.get(), ctx) || |
| 599 | !ExpectBIGNUMsEqual(t, "inv(A) (mod M)", mod_inv.get(), ret.get())) { |
| 600 | return false; |
| 601 | } |
| 602 | |
| 603 | BN_set_flags(a.get(), BN_FLG_CONSTTIME); |
| 604 | |
| 605 | if (!ret || |
| 606 | !BN_mod_inverse(ret.get(), a.get(), m.get(), ctx) || |
| 607 | !ExpectBIGNUMsEqual(t, "inv(A) (mod M) (constant-time)", mod_inv.get(), |
| 608 | ret.get())) { |
| 609 | return false; |
| 610 | } |
| 611 | |
| 612 | return true; |
| 613 | } |
| 614 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 615 | struct Test { |
| 616 | const char *name; |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 617 | bool (*func)(FileTest *t, BN_CTX *ctx); |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 618 | }; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 619 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 620 | static const Test kTests[] = { |
| 621 | {"Sum", TestSum}, |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 622 | {"LShift1", TestLShift1}, |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 623 | {"LShift", TestLShift}, |
| 624 | {"RShift", TestRShift}, |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 625 | {"Square", TestSquare}, |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 626 | {"Product", TestProduct}, |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 627 | {"Quotient", TestQuotient}, |
David Benjamin | 5a13e40 | 2016-07-02 20:33:07 -0400 | [diff] [blame] | 628 | {"ModMul", TestModMul}, |
David Benjamin | 45a8c8a | 2016-07-03 10:05:56 -0400 | [diff] [blame] | 629 | {"ModExp", TestModExp}, |
David Benjamin | 4cb00ba | 2016-07-03 11:52:58 -0400 | [diff] [blame] | 630 | {"Exp", TestExp}, |
David Benjamin | ffb7adc | 2016-07-03 15:19:20 -0400 | [diff] [blame] | 631 | {"ModSqrt", TestModSqrt}, |
Brian Smith | 286fbf2 | 2016-07-25 14:20:32 -1000 | [diff] [blame] | 632 | {"ModInv", TestModInv}, |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 633 | }; |
| 634 | |
| 635 | static bool RunTest(FileTest *t, void *arg) { |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 636 | BN_CTX *ctx = reinterpret_cast<BN_CTX *>(arg); |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 637 | for (const Test &test : kTests) { |
| 638 | if (t->GetType() != test.name) { |
| 639 | continue; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 640 | } |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 641 | return test.func(t, ctx); |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 642 | } |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 643 | t->PrintLine("Unknown test type: %s", t->GetType().c_str()); |
| 644 | return false; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 645 | } |
| 646 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 647 | static bool TestBN2BinPadded(BN_CTX *ctx) { |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 648 | uint8_t zeros[256], out[256], reference[128]; |
| 649 | |
| 650 | memset(zeros, 0, sizeof(zeros)); |
| 651 | |
| 652 | // Test edge case at 0. |
| 653 | ScopedBIGNUM n(BN_new()); |
| 654 | if (!n || !BN_bn2bin_padded(NULL, 0, n.get())) { |
| 655 | fprintf(stderr, |
| 656 | "BN_bn2bin_padded failed to encode 0 in an empty buffer.\n"); |
| 657 | return false; |
| 658 | } |
| 659 | memset(out, -1, sizeof(out)); |
| 660 | if (!BN_bn2bin_padded(out, sizeof(out), n.get())) { |
| 661 | fprintf(stderr, |
| 662 | "BN_bn2bin_padded failed to encode 0 in a non-empty buffer.\n"); |
| 663 | return false; |
| 664 | } |
| 665 | if (memcmp(zeros, out, sizeof(out))) { |
| 666 | fprintf(stderr, "BN_bn2bin_padded did not zero buffer.\n"); |
| 667 | return false; |
| 668 | } |
| 669 | |
| 670 | // Test a random numbers at various byte lengths. |
| 671 | for (size_t bytes = 128 - 7; bytes <= 128; bytes++) { |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 672 | if (!BN_rand(n.get(), bytes * 8, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) { |
Brian Smith | 83a8298 | 2015-04-09 16:21:10 -1000 | [diff] [blame] | 673 | ERR_print_errors_fp(stderr); |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 674 | return false; |
| 675 | } |
| 676 | if (BN_num_bytes(n.get()) != bytes || |
| 677 | BN_bn2bin(n.get(), reference) != bytes) { |
| 678 | fprintf(stderr, "Bad result from BN_rand; bytes.\n"); |
| 679 | return false; |
| 680 | } |
| 681 | // Empty buffer should fail. |
| 682 | if (BN_bn2bin_padded(NULL, 0, n.get())) { |
| 683 | fprintf(stderr, |
| 684 | "BN_bn2bin_padded incorrectly succeeded on empty buffer.\n"); |
| 685 | return false; |
| 686 | } |
| 687 | // One byte short should fail. |
| 688 | if (BN_bn2bin_padded(out, bytes - 1, n.get())) { |
| 689 | fprintf(stderr, "BN_bn2bin_padded incorrectly succeeded on short.\n"); |
| 690 | return false; |
| 691 | } |
| 692 | // Exactly right size should encode. |
| 693 | if (!BN_bn2bin_padded(out, bytes, n.get()) || |
| 694 | memcmp(out, reference, bytes) != 0) { |
| 695 | fprintf(stderr, "BN_bn2bin_padded gave a bad result.\n"); |
| 696 | return false; |
| 697 | } |
| 698 | // Pad up one byte extra. |
| 699 | if (!BN_bn2bin_padded(out, bytes + 1, n.get()) || |
| 700 | memcmp(out + 1, reference, bytes) || memcmp(out, zeros, 1)) { |
| 701 | fprintf(stderr, "BN_bn2bin_padded gave a bad result.\n"); |
| 702 | return false; |
| 703 | } |
| 704 | // Pad up to 256. |
| 705 | if (!BN_bn2bin_padded(out, sizeof(out), n.get()) || |
| 706 | memcmp(out + sizeof(out) - bytes, reference, bytes) || |
| 707 | memcmp(out, zeros, sizeof(out) - bytes)) { |
| 708 | fprintf(stderr, "BN_bn2bin_padded gave a bad result.\n"); |
| 709 | return false; |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | return true; |
| 714 | } |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 715 | |
| 716 | static int DecimalToBIGNUM(ScopedBIGNUM *out, const char *in) { |
| 717 | BIGNUM *raw = NULL; |
| 718 | int ret = BN_dec2bn(&raw, in); |
| 719 | out->reset(raw); |
| 720 | return ret; |
| 721 | } |
| 722 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 723 | static bool TestDec2BN(BN_CTX *ctx) { |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 724 | ScopedBIGNUM bn; |
| 725 | int ret = DecimalToBIGNUM(&bn, "0"); |
| 726 | if (ret != 1 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) { |
| 727 | fprintf(stderr, "BN_dec2bn gave a bad result.\n"); |
| 728 | return false; |
| 729 | } |
| 730 | |
| 731 | ret = DecimalToBIGNUM(&bn, "256"); |
| 732 | if (ret != 3 || !BN_is_word(bn.get(), 256) || BN_is_negative(bn.get())) { |
| 733 | fprintf(stderr, "BN_dec2bn gave a bad result.\n"); |
| 734 | return false; |
| 735 | } |
| 736 | |
| 737 | ret = DecimalToBIGNUM(&bn, "-42"); |
| 738 | if (ret != 3 || !BN_abs_is_word(bn.get(), 42) || !BN_is_negative(bn.get())) { |
| 739 | fprintf(stderr, "BN_dec2bn gave a bad result.\n"); |
| 740 | return false; |
| 741 | } |
| 742 | |
| 743 | ret = DecimalToBIGNUM(&bn, "-0"); |
| 744 | if (ret != 2 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) { |
| 745 | fprintf(stderr, "BN_dec2bn gave a bad result.\n"); |
| 746 | return false; |
| 747 | } |
| 748 | |
| 749 | ret = DecimalToBIGNUM(&bn, "42trailing garbage is ignored"); |
| 750 | if (ret != 2 || !BN_abs_is_word(bn.get(), 42) || BN_is_negative(bn.get())) { |
| 751 | fprintf(stderr, "BN_dec2bn gave a bad result.\n"); |
| 752 | return false; |
| 753 | } |
| 754 | |
| 755 | return true; |
| 756 | } |
| 757 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 758 | static bool TestHex2BN(BN_CTX *ctx) { |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 759 | ScopedBIGNUM bn; |
| 760 | int ret = HexToBIGNUM(&bn, "0"); |
| 761 | if (ret != 1 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) { |
| 762 | fprintf(stderr, "BN_hex2bn gave a bad result.\n"); |
| 763 | return false; |
| 764 | } |
| 765 | |
| 766 | ret = HexToBIGNUM(&bn, "256"); |
| 767 | if (ret != 3 || !BN_is_word(bn.get(), 0x256) || BN_is_negative(bn.get())) { |
| 768 | fprintf(stderr, "BN_hex2bn gave a bad result.\n"); |
| 769 | return false; |
| 770 | } |
| 771 | |
| 772 | ret = HexToBIGNUM(&bn, "-42"); |
| 773 | if (ret != 3 || !BN_abs_is_word(bn.get(), 0x42) || !BN_is_negative(bn.get())) { |
| 774 | fprintf(stderr, "BN_hex2bn gave a bad result.\n"); |
| 775 | return false; |
| 776 | } |
| 777 | |
| 778 | ret = HexToBIGNUM(&bn, "-0"); |
| 779 | if (ret != 2 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) { |
| 780 | fprintf(stderr, "BN_hex2bn gave a bad result.\n"); |
| 781 | return false; |
| 782 | } |
| 783 | |
| 784 | ret = HexToBIGNUM(&bn, "abctrailing garbage is ignored"); |
| 785 | if (ret != 3 || !BN_is_word(bn.get(), 0xabc) || BN_is_negative(bn.get())) { |
| 786 | fprintf(stderr, "BN_hex2bn gave a bad result.\n"); |
| 787 | return false; |
| 788 | } |
| 789 | |
| 790 | return true; |
| 791 | } |
| 792 | |
| 793 | static ScopedBIGNUM ASCIIToBIGNUM(const char *in) { |
| 794 | BIGNUM *raw = NULL; |
| 795 | if (!BN_asc2bn(&raw, in)) { |
| 796 | return nullptr; |
| 797 | } |
| 798 | return ScopedBIGNUM(raw); |
| 799 | } |
| 800 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 801 | static bool TestASC2BN(BN_CTX *ctx) { |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 802 | ScopedBIGNUM bn = ASCIIToBIGNUM("0"); |
| 803 | if (!bn || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) { |
| 804 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 805 | return false; |
| 806 | } |
| 807 | |
| 808 | bn = ASCIIToBIGNUM("256"); |
| 809 | if (!bn || !BN_is_word(bn.get(), 256) || BN_is_negative(bn.get())) { |
| 810 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 811 | return false; |
| 812 | } |
| 813 | |
| 814 | bn = ASCIIToBIGNUM("-42"); |
| 815 | if (!bn || !BN_abs_is_word(bn.get(), 42) || !BN_is_negative(bn.get())) { |
| 816 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 817 | return false; |
| 818 | } |
| 819 | |
| 820 | bn = ASCIIToBIGNUM("0x1234"); |
| 821 | if (!bn || !BN_is_word(bn.get(), 0x1234) || BN_is_negative(bn.get())) { |
| 822 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 823 | return false; |
| 824 | } |
| 825 | |
| 826 | bn = ASCIIToBIGNUM("0X1234"); |
| 827 | if (!bn || !BN_is_word(bn.get(), 0x1234) || BN_is_negative(bn.get())) { |
| 828 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 829 | return false; |
| 830 | } |
| 831 | |
| 832 | bn = ASCIIToBIGNUM("-0xabcd"); |
| 833 | if (!bn || !BN_abs_is_word(bn.get(), 0xabcd) || !BN_is_negative(bn.get())) { |
| 834 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 835 | return false; |
| 836 | } |
| 837 | |
| 838 | bn = ASCIIToBIGNUM("-0"); |
| 839 | if (!bn || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) { |
| 840 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 841 | return false; |
| 842 | } |
| 843 | |
| 844 | bn = ASCIIToBIGNUM("123trailing garbage is ignored"); |
| 845 | if (!bn || !BN_is_word(bn.get(), 123) || BN_is_negative(bn.get())) { |
| 846 | fprintf(stderr, "BN_asc2bn gave a bad result.\n"); |
| 847 | return false; |
| 848 | } |
| 849 | |
| 850 | return true; |
| 851 | } |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 852 | |
Matt Braithwaite | 6488725 | 2015-08-11 17:17:28 -0700 | [diff] [blame] | 853 | struct MPITest { |
| 854 | const char *base10; |
| 855 | const char *mpi; |
| 856 | size_t mpi_len; |
| 857 | }; |
| 858 | |
| 859 | static const MPITest kMPITests[] = { |
| 860 | { "0", "\x00\x00\x00\x00", 4 }, |
| 861 | { "1", "\x00\x00\x00\x01\x01", 5 }, |
| 862 | { "-1", "\x00\x00\x00\x01\x81", 5 }, |
| 863 | { "128", "\x00\x00\x00\x02\x00\x80", 6 }, |
| 864 | { "256", "\x00\x00\x00\x02\x01\x00", 6 }, |
| 865 | { "-256", "\x00\x00\x00\x02\x81\x00", 6 }, |
| 866 | }; |
| 867 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 868 | static bool TestMPI() { |
Matt Braithwaite | 6488725 | 2015-08-11 17:17:28 -0700 | [diff] [blame] | 869 | uint8_t scratch[8]; |
| 870 | |
Steven Valdez | cb96654 | 2016-08-17 16:56:14 -0400 | [diff] [blame] | 871 | for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMPITests); i++) { |
Matt Braithwaite | 6488725 | 2015-08-11 17:17:28 -0700 | [diff] [blame] | 872 | const MPITest &test = kMPITests[i]; |
| 873 | ScopedBIGNUM bn(ASCIIToBIGNUM(test.base10)); |
| 874 | const size_t mpi_len = BN_bn2mpi(bn.get(), NULL); |
| 875 | if (mpi_len > sizeof(scratch)) { |
| 876 | fprintf(stderr, "MPI test #%u: MPI size is too large to test.\n", |
| 877 | (unsigned)i); |
| 878 | return false; |
| 879 | } |
| 880 | |
| 881 | const size_t mpi_len2 = BN_bn2mpi(bn.get(), scratch); |
| 882 | if (mpi_len != mpi_len2) { |
| 883 | fprintf(stderr, "MPI test #%u: length changes.\n", (unsigned)i); |
| 884 | return false; |
| 885 | } |
| 886 | |
| 887 | if (mpi_len != test.mpi_len || |
| 888 | memcmp(test.mpi, scratch, mpi_len) != 0) { |
| 889 | fprintf(stderr, "MPI test #%u failed:\n", (unsigned)i); |
| 890 | hexdump(stderr, "Expected: ", test.mpi, test.mpi_len); |
| 891 | hexdump(stderr, "Got: ", scratch, mpi_len); |
| 892 | return false; |
| 893 | } |
| 894 | |
| 895 | ScopedBIGNUM bn2(BN_mpi2bn(scratch, mpi_len, NULL)); |
| 896 | if (bn2.get() == nullptr) { |
| 897 | fprintf(stderr, "MPI test #%u: failed to parse\n", (unsigned)i); |
| 898 | return false; |
| 899 | } |
| 900 | |
| 901 | if (BN_cmp(bn.get(), bn2.get()) != 0) { |
| 902 | fprintf(stderr, "MPI test #%u: wrong result\n", (unsigned)i); |
| 903 | return false; |
| 904 | } |
| 905 | } |
| 906 | |
| 907 | return true; |
| 908 | } |
| 909 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 910 | static bool TestRand() { |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 911 | ScopedBIGNUM bn(BN_new()); |
| 912 | if (!bn) { |
| 913 | return false; |
| 914 | } |
| 915 | |
| 916 | // Test BN_rand accounts for degenerate cases with |top| and |bottom| |
| 917 | // parameters. |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 918 | if (!BN_rand(bn.get(), 0, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY) || |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 919 | !BN_is_zero(bn.get())) { |
| 920 | fprintf(stderr, "BN_rand gave a bad result.\n"); |
| 921 | return false; |
| 922 | } |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 923 | if (!BN_rand(bn.get(), 0, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ODD) || |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 924 | !BN_is_zero(bn.get())) { |
| 925 | fprintf(stderr, "BN_rand gave a bad result.\n"); |
| 926 | return false; |
| 927 | } |
| 928 | |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 929 | if (!BN_rand(bn.get(), 1, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY) || |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 930 | !BN_is_word(bn.get(), 1)) { |
| 931 | fprintf(stderr, "BN_rand gave a bad result.\n"); |
| 932 | return false; |
| 933 | } |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 934 | if (!BN_rand(bn.get(), 1, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ANY) || |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 935 | !BN_is_word(bn.get(), 1)) { |
| 936 | fprintf(stderr, "BN_rand gave a bad result.\n"); |
| 937 | return false; |
| 938 | } |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 939 | if (!BN_rand(bn.get(), 1, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ODD) || |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 940 | !BN_is_word(bn.get(), 1)) { |
| 941 | fprintf(stderr, "BN_rand gave a bad result.\n"); |
| 942 | return false; |
| 943 | } |
| 944 | |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 945 | if (!BN_rand(bn.get(), 2, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ANY) || |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 946 | !BN_is_word(bn.get(), 3)) { |
| 947 | fprintf(stderr, "BN_rand gave a bad result.\n"); |
| 948 | return false; |
| 949 | } |
| 950 | |
| 951 | return true; |
| 952 | } |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 953 | |
| 954 | struct ASN1Test { |
| 955 | const char *value_ascii; |
| 956 | const char *der; |
| 957 | size_t der_len; |
| 958 | }; |
| 959 | |
| 960 | static const ASN1Test kASN1Tests[] = { |
| 961 | {"0", "\x02\x01\x00", 3}, |
| 962 | {"1", "\x02\x01\x01", 3}, |
| 963 | {"127", "\x02\x01\x7f", 3}, |
| 964 | {"128", "\x02\x02\x00\x80", 4}, |
| 965 | {"0xdeadbeef", "\x02\x05\x00\xde\xad\xbe\xef", 7}, |
| 966 | {"0x0102030405060708", |
| 967 | "\x02\x08\x01\x02\x03\x04\x05\x06\x07\x08", 10}, |
| 968 | {"0xffffffffffffffff", |
| 969 | "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", 11}, |
| 970 | }; |
| 971 | |
| 972 | struct ASN1InvalidTest { |
| 973 | const char *der; |
| 974 | size_t der_len; |
| 975 | }; |
| 976 | |
| 977 | static const ASN1InvalidTest kASN1InvalidTests[] = { |
| 978 | // Bad tag. |
| 979 | {"\x03\x01\x00", 3}, |
| 980 | // Empty contents. |
| 981 | {"\x02\x00", 2}, |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 982 | }; |
| 983 | |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 984 | // kASN1BuggyTests contains incorrect encodings and the corresponding, expected |
| 985 | // results of |BN_parse_asn1_unsigned_buggy| given that input. |
David Benjamin | 4c60d35 | 2015-09-23 12:23:01 -0400 | [diff] [blame] | 986 | static const ASN1Test kASN1BuggyTests[] = { |
| 987 | // Negative numbers. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 988 | {"128", "\x02\x01\x80", 3}, |
| 989 | {"255", "\x02\x01\xff", 3}, |
David Benjamin | 4c60d35 | 2015-09-23 12:23:01 -0400 | [diff] [blame] | 990 | // Unnecessary leading zeros. |
| 991 | {"1", "\x02\x02\x00\x01", 4}, |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 992 | }; |
| 993 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 994 | static bool TestASN1() { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 995 | for (const ASN1Test &test : kASN1Tests) { |
| 996 | ScopedBIGNUM bn = ASCIIToBIGNUM(test.value_ascii); |
| 997 | if (!bn) { |
| 998 | return false; |
| 999 | } |
| 1000 | |
| 1001 | // Test that the input is correctly parsed. |
| 1002 | ScopedBIGNUM bn2(BN_new()); |
| 1003 | if (!bn2) { |
| 1004 | return false; |
| 1005 | } |
| 1006 | CBS cbs; |
| 1007 | 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] | 1008 | if (!BN_parse_asn1_unsigned(&cbs, bn2.get()) || CBS_len(&cbs) != 0) { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1009 | fprintf(stderr, "Parsing ASN.1 INTEGER failed.\n"); |
| 1010 | return false; |
| 1011 | } |
| 1012 | if (BN_cmp(bn.get(), bn2.get()) != 0) { |
| 1013 | fprintf(stderr, "Bad parse.\n"); |
| 1014 | return false; |
| 1015 | } |
| 1016 | |
| 1017 | // Test the value serializes correctly. |
| 1018 | CBB cbb; |
| 1019 | uint8_t *der; |
| 1020 | size_t der_len; |
| 1021 | CBB_zero(&cbb); |
| 1022 | if (!CBB_init(&cbb, 0) || |
David Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1023 | !BN_marshal_asn1(&cbb, bn.get()) || |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1024 | !CBB_finish(&cbb, &der, &der_len)) { |
| 1025 | CBB_cleanup(&cbb); |
| 1026 | return false; |
| 1027 | } |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame] | 1028 | ScopedOpenSSLBytes delete_der(der); |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1029 | if (der_len != test.der_len || |
| 1030 | memcmp(der, reinterpret_cast<const uint8_t*>(test.der), der_len) != 0) { |
| 1031 | fprintf(stderr, "Bad serialization.\n"); |
| 1032 | return false; |
| 1033 | } |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1034 | |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1035 | // |BN_parse_asn1_unsigned_buggy| parses all valid input. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1036 | 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] | 1037 | 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] | 1038 | fprintf(stderr, "Parsing ASN.1 INTEGER failed.\n"); |
| 1039 | return false; |
| 1040 | } |
| 1041 | if (BN_cmp(bn.get(), bn2.get()) != 0) { |
| 1042 | fprintf(stderr, "Bad parse.\n"); |
| 1043 | return false; |
| 1044 | } |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1045 | } |
| 1046 | |
| 1047 | for (const ASN1InvalidTest &test : kASN1InvalidTests) { |
| 1048 | ScopedBIGNUM bn(BN_new()); |
| 1049 | if (!bn) { |
| 1050 | return false; |
| 1051 | } |
| 1052 | CBS cbs; |
| 1053 | 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] | 1054 | if (BN_parse_asn1_unsigned(&cbs, bn.get())) { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1055 | fprintf(stderr, "Parsed invalid input.\n"); |
| 1056 | return false; |
| 1057 | } |
| 1058 | ERR_clear_error(); |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1059 | |
| 1060 | // All tests in kASN1InvalidTests are also rejected by |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1061 | // |BN_parse_asn1_unsigned_buggy|. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1062 | 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] | 1063 | if (BN_parse_asn1_unsigned_buggy(&cbs, bn.get())) { |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1064 | fprintf(stderr, "Parsed invalid input.\n"); |
| 1065 | return false; |
| 1066 | } |
| 1067 | ERR_clear_error(); |
| 1068 | } |
| 1069 | |
David Benjamin | 4c60d35 | 2015-09-23 12:23:01 -0400 | [diff] [blame] | 1070 | for (const ASN1Test &test : kASN1BuggyTests) { |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1071 | // These broken encodings are rejected by |BN_parse_asn1_unsigned|. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1072 | ScopedBIGNUM bn(BN_new()); |
| 1073 | if (!bn) { |
| 1074 | return false; |
| 1075 | } |
| 1076 | |
| 1077 | CBS cbs; |
| 1078 | 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] | 1079 | if (BN_parse_asn1_unsigned(&cbs, bn.get())) { |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1080 | fprintf(stderr, "Parsed invalid input.\n"); |
| 1081 | return false; |
| 1082 | } |
| 1083 | ERR_clear_error(); |
| 1084 | |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1085 | // However |BN_parse_asn1_unsigned_buggy| accepts them. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1086 | ScopedBIGNUM bn2 = ASCIIToBIGNUM(test.value_ascii); |
| 1087 | if (!bn2) { |
| 1088 | return false; |
| 1089 | } |
| 1090 | |
| 1091 | 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] | 1092 | 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] | 1093 | fprintf(stderr, "Parsing (invalid) ASN.1 INTEGER failed.\n"); |
| 1094 | return false; |
| 1095 | } |
| 1096 | |
| 1097 | if (BN_cmp(bn.get(), bn2.get()) != 0) { |
| 1098 | fprintf(stderr, "\"Bad\" parse.\n"); |
| 1099 | return false; |
| 1100 | } |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1101 | } |
| 1102 | |
| 1103 | // Serializing negative numbers is not supported. |
| 1104 | ScopedBIGNUM bn = ASCIIToBIGNUM("-1"); |
| 1105 | if (!bn) { |
| 1106 | return false; |
| 1107 | } |
| 1108 | CBB cbb; |
| 1109 | CBB_zero(&cbb); |
| 1110 | if (!CBB_init(&cbb, 0) || |
David Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1111 | BN_marshal_asn1(&cbb, bn.get())) { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1112 | fprintf(stderr, "Serialized negative number.\n"); |
| 1113 | CBB_cleanup(&cbb); |
| 1114 | return false; |
| 1115 | } |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1116 | ERR_clear_error(); |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1117 | CBB_cleanup(&cbb); |
| 1118 | |
| 1119 | return true; |
| 1120 | } |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1121 | |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1122 | static bool TestNegativeZero(BN_CTX *ctx) { |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1123 | ScopedBIGNUM a(BN_new()); |
| 1124 | ScopedBIGNUM b(BN_new()); |
| 1125 | ScopedBIGNUM c(BN_new()); |
David Benjamin | 899b9b1 | 2016-08-02 13:09:19 -0400 | [diff] [blame] | 1126 | if (!a || !b || !c) { |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1127 | return false; |
| 1128 | } |
| 1129 | |
| 1130 | // Test that BN_mul never gives negative zero. |
| 1131 | if (!BN_set_word(a.get(), 1)) { |
| 1132 | return false; |
| 1133 | } |
| 1134 | BN_set_negative(a.get(), 1); |
| 1135 | BN_zero(b.get()); |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1136 | if (!BN_mul(c.get(), a.get(), b.get(), ctx)) { |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1137 | return false; |
| 1138 | } |
| 1139 | if (!BN_is_zero(c.get()) || BN_is_negative(c.get())) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1140 | fprintf(stderr, "Multiplication test failed.\n"); |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1141 | return false; |
| 1142 | } |
| 1143 | |
David Benjamin | 899b9b1 | 2016-08-02 13:09:19 -0400 | [diff] [blame] | 1144 | for (int consttime = 0; consttime < 2; consttime++) { |
| 1145 | ScopedBIGNUM numerator(BN_new()), denominator(BN_new()); |
| 1146 | if (!numerator || !denominator) { |
| 1147 | return false; |
| 1148 | } |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1149 | |
David Benjamin | 899b9b1 | 2016-08-02 13:09:19 -0400 | [diff] [blame] | 1150 | if (consttime) { |
| 1151 | BN_set_flags(numerator.get(), BN_FLG_CONSTTIME); |
| 1152 | BN_set_flags(denominator.get(), BN_FLG_CONSTTIME); |
| 1153 | } |
| 1154 | |
| 1155 | // Test that BN_div never gives negative zero in the quotient. |
| 1156 | if (!BN_set_word(numerator.get(), 1) || |
| 1157 | !BN_set_word(denominator.get(), 2)) { |
| 1158 | return false; |
| 1159 | } |
| 1160 | BN_set_negative(numerator.get(), 1); |
| 1161 | if (!BN_div(a.get(), b.get(), numerator.get(), denominator.get(), ctx)) { |
| 1162 | return false; |
| 1163 | } |
| 1164 | if (!BN_is_zero(a.get()) || BN_is_negative(a.get())) { |
| 1165 | fprintf(stderr, "Incorrect quotient (consttime = %d).\n", consttime); |
| 1166 | return false; |
| 1167 | } |
| 1168 | |
| 1169 | // Test that BN_div never gives negative zero in the remainder. |
| 1170 | if (!BN_set_word(denominator.get(), 1)) { |
| 1171 | return false; |
| 1172 | } |
| 1173 | if (!BN_div(a.get(), b.get(), numerator.get(), denominator.get(), ctx)) { |
| 1174 | return false; |
| 1175 | } |
| 1176 | if (!BN_is_zero(b.get()) || BN_is_negative(b.get())) { |
| 1177 | fprintf(stderr, "Incorrect remainder (consttime = %d).\n", consttime); |
| 1178 | return false; |
| 1179 | } |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1180 | } |
| 1181 | |
| 1182 | // Test that BN_set_negative will not produce a negative zero. |
| 1183 | BN_zero(a.get()); |
| 1184 | BN_set_negative(a.get(), 1); |
| 1185 | if (BN_is_negative(a.get())) { |
| 1186 | fprintf(stderr, "BN_set_negative produced a negative zero.\n"); |
| 1187 | return false; |
| 1188 | } |
| 1189 | |
| 1190 | // Test that forcibly creating a negative zero does not break |BN_bn2hex| or |
| 1191 | // |BN_bn2dec|. |
| 1192 | a->neg = 1; |
| 1193 | ScopedOpenSSLString dec(BN_bn2dec(a.get())); |
| 1194 | ScopedOpenSSLString hex(BN_bn2hex(a.get())); |
| 1195 | if (!dec || !hex || |
| 1196 | strcmp(dec.get(), "-0") != 0 || |
| 1197 | strcmp(hex.get(), "-0") != 0) { |
| 1198 | fprintf(stderr, "BN_bn2dec or BN_bn2hex failed with negative zero.\n"); |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1199 | return false; |
| 1200 | } |
| 1201 | |
| 1202 | return true; |
| 1203 | } |
| 1204 | |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1205 | static bool TestBadModulus(BN_CTX *ctx) { |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1206 | ScopedBIGNUM a(BN_new()); |
| 1207 | ScopedBIGNUM b(BN_new()); |
| 1208 | ScopedBIGNUM zero(BN_new()); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1209 | ScopedBN_MONT_CTX mont(BN_MONT_CTX_new()); |
| 1210 | if (!a || !b || !zero || !mont) { |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1211 | return false; |
| 1212 | } |
| 1213 | |
| 1214 | BN_zero(zero.get()); |
| 1215 | |
| 1216 | if (BN_div(a.get(), b.get(), BN_value_one(), zero.get(), ctx)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1217 | fprintf(stderr, "Division by zero unexpectedly succeeded.\n"); |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1218 | return false; |
| 1219 | } |
| 1220 | ERR_clear_error(); |
| 1221 | |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1222 | if (BN_mod_mul(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1223 | fprintf(stderr, "BN_mod_mul with zero modulus unexpectedly succeeded.\n"); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1224 | return false; |
| 1225 | } |
| 1226 | ERR_clear_error(); |
| 1227 | |
| 1228 | if (BN_mod_exp(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1229 | fprintf(stderr, "BN_mod_exp with zero modulus unexpectedly succeeded.\n"); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1230 | return 0; |
| 1231 | } |
| 1232 | ERR_clear_error(); |
| 1233 | |
Brian Smith | b72f66f | 2016-07-01 11:34:10 -1000 | [diff] [blame] | 1234 | if (BN_mod_exp_mont(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx, |
| 1235 | NULL)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1236 | fprintf(stderr, |
| 1237 | "BN_mod_exp_mont with zero modulus unexpectedly succeeded.\n"); |
Brian Smith | b72f66f | 2016-07-01 11:34:10 -1000 | [diff] [blame] | 1238 | return 0; |
| 1239 | } |
| 1240 | ERR_clear_error(); |
| 1241 | |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1242 | if (BN_mod_exp_mont_consttime(a.get(), BN_value_one(), BN_value_one(), |
| 1243 | zero.get(), ctx, nullptr)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1244 | fprintf(stderr, |
| 1245 | "BN_mod_exp_mont_consttime with zero modulus unexpectedly " |
| 1246 | "succeeded.\n"); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1247 | return 0; |
| 1248 | } |
| 1249 | ERR_clear_error(); |
| 1250 | |
| 1251 | if (BN_MONT_CTX_set(mont.get(), zero.get(), ctx)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1252 | fprintf(stderr, |
| 1253 | "BN_MONT_CTX_set unexpectedly succeeded for zero modulus.\n"); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1254 | return false; |
| 1255 | } |
| 1256 | ERR_clear_error(); |
| 1257 | |
| 1258 | // Some operations also may not be used with an even modulus. |
| 1259 | |
| 1260 | if (!BN_set_word(b.get(), 16)) { |
| 1261 | return false; |
| 1262 | } |
| 1263 | |
| 1264 | if (BN_MONT_CTX_set(mont.get(), b.get(), ctx)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1265 | fprintf(stderr, |
| 1266 | "BN_MONT_CTX_set unexpectedly succeeded for even modulus.\n"); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1267 | return false; |
| 1268 | } |
| 1269 | ERR_clear_error(); |
| 1270 | |
Brian Smith | b72f66f | 2016-07-01 11:34:10 -1000 | [diff] [blame] | 1271 | if (BN_mod_exp_mont(a.get(), BN_value_one(), BN_value_one(), b.get(), ctx, |
| 1272 | NULL)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1273 | fprintf(stderr, |
| 1274 | "BN_mod_exp_mont with even modulus unexpectedly succeeded.\n"); |
Brian Smith | b72f66f | 2016-07-01 11:34:10 -1000 | [diff] [blame] | 1275 | return 0; |
| 1276 | } |
| 1277 | ERR_clear_error(); |
| 1278 | |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1279 | if (BN_mod_exp_mont_consttime(a.get(), BN_value_one(), BN_value_one(), |
| 1280 | b.get(), ctx, nullptr)) { |
David Benjamin | 2b314fa | 2016-08-02 12:49:38 -0400 | [diff] [blame] | 1281 | fprintf(stderr, |
| 1282 | "BN_mod_exp_mont_consttime with even modulus unexpectedly " |
| 1283 | "succeeded.\n"); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1284 | return 0; |
| 1285 | } |
| 1286 | ERR_clear_error(); |
| 1287 | |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1288 | return true; |
| 1289 | } |
David Benjamin | 32a3780 | 2016-06-26 18:17:31 -0400 | [diff] [blame] | 1290 | |
| 1291 | // TestExpModZero tests that 1**0 mod 1 == 0. |
| 1292 | static bool TestExpModZero() { |
| 1293 | ScopedBIGNUM zero(BN_new()), a(BN_new()), r(BN_new()); |
David Benjamin | d224d52 | 2016-08-16 10:03:45 -0400 | [diff] [blame] | 1294 | if (!zero || !a || !r || |
| 1295 | !BN_rand(a.get(), 1024, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) { |
David Benjamin | 32a3780 | 2016-06-26 18:17:31 -0400 | [diff] [blame] | 1296 | return false; |
| 1297 | } |
| 1298 | BN_zero(zero.get()); |
| 1299 | |
| 1300 | if (!BN_mod_exp(r.get(), a.get(), zero.get(), BN_value_one(), nullptr) || |
| 1301 | !BN_is_zero(r.get()) || |
| 1302 | !BN_mod_exp_mont(r.get(), a.get(), zero.get(), BN_value_one(), nullptr, |
| 1303 | nullptr) || |
| 1304 | !BN_is_zero(r.get()) || |
| 1305 | !BN_mod_exp_mont_consttime(r.get(), a.get(), zero.get(), BN_value_one(), |
| 1306 | nullptr, nullptr) || |
| 1307 | !BN_is_zero(r.get()) || |
| 1308 | !BN_mod_exp_mont_word(r.get(), 42, zero.get(), BN_value_one(), nullptr, |
| 1309 | nullptr) || |
| 1310 | !BN_is_zero(r.get())) { |
| 1311 | return false; |
| 1312 | } |
| 1313 | |
| 1314 | return true; |
| 1315 | } |
| 1316 | |
| 1317 | static bool TestSmallPrime(BN_CTX *ctx) { |
| 1318 | static const unsigned kBits = 10; |
| 1319 | |
| 1320 | ScopedBIGNUM r(BN_new()); |
| 1321 | if (!r || !BN_generate_prime_ex(r.get(), static_cast<int>(kBits), 0, NULL, |
| 1322 | NULL, NULL)) { |
| 1323 | return false; |
| 1324 | } |
| 1325 | if (BN_num_bits(r.get()) != kBits) { |
| 1326 | fprintf(stderr, "Expected %u bit prime, got %u bit number\n", kBits, |
| 1327 | BN_num_bits(r.get())); |
| 1328 | return false; |
| 1329 | } |
| 1330 | |
| 1331 | return true; |
| 1332 | } |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1333 | |
David Benjamin | ccd511e | 2016-07-29 12:11:34 -0400 | [diff] [blame] | 1334 | static bool TestCmpWord() { |
| 1335 | static const BN_ULONG kMaxWord = (BN_ULONG)-1; |
| 1336 | |
| 1337 | ScopedBIGNUM r(BN_new()); |
| 1338 | if (!r || |
| 1339 | !BN_set_word(r.get(), 0)) { |
| 1340 | return false; |
| 1341 | } |
| 1342 | |
| 1343 | if (BN_cmp_word(r.get(), 0) != 0 || |
| 1344 | BN_cmp_word(r.get(), 1) >= 0 || |
| 1345 | BN_cmp_word(r.get(), kMaxWord) >= 0) { |
| 1346 | fprintf(stderr, "BN_cmp_word compared against 0 incorrectly.\n"); |
| 1347 | return false; |
| 1348 | } |
| 1349 | |
| 1350 | if (!BN_set_word(r.get(), 100)) { |
| 1351 | return false; |
| 1352 | } |
| 1353 | |
| 1354 | if (BN_cmp_word(r.get(), 0) <= 0 || |
| 1355 | BN_cmp_word(r.get(), 99) <= 0 || |
| 1356 | BN_cmp_word(r.get(), 100) != 0 || |
| 1357 | BN_cmp_word(r.get(), 101) >= 0 || |
| 1358 | BN_cmp_word(r.get(), kMaxWord) >= 0) { |
| 1359 | fprintf(stderr, "BN_cmp_word compared against 100 incorrectly.\n"); |
| 1360 | return false; |
| 1361 | } |
| 1362 | |
| 1363 | BN_set_negative(r.get(), 1); |
| 1364 | |
| 1365 | if (BN_cmp_word(r.get(), 0) >= 0 || |
| 1366 | BN_cmp_word(r.get(), 100) >= 0 || |
| 1367 | BN_cmp_word(r.get(), kMaxWord) >= 0) { |
| 1368 | fprintf(stderr, "BN_cmp_word compared against -100 incorrectly.\n"); |
| 1369 | return false; |
| 1370 | } |
| 1371 | |
| 1372 | if (!BN_set_word(r.get(), kMaxWord)) { |
| 1373 | return false; |
| 1374 | } |
| 1375 | |
| 1376 | if (BN_cmp_word(r.get(), 0) <= 0 || |
| 1377 | BN_cmp_word(r.get(), kMaxWord - 1) <= 0 || |
| 1378 | BN_cmp_word(r.get(), kMaxWord) != 0) { |
| 1379 | fprintf(stderr, "BN_cmp_word compared against kMaxWord incorrectly.\n"); |
| 1380 | return false; |
| 1381 | } |
| 1382 | |
| 1383 | if (!BN_add(r.get(), r.get(), BN_value_one())) { |
| 1384 | return false; |
| 1385 | } |
| 1386 | |
| 1387 | if (BN_cmp_word(r.get(), 0) <= 0 || |
| 1388 | BN_cmp_word(r.get(), kMaxWord) <= 0) { |
| 1389 | fprintf(stderr, "BN_cmp_word compared against kMaxWord + 1 incorrectly.\n"); |
| 1390 | return false; |
| 1391 | } |
| 1392 | |
| 1393 | BN_set_negative(r.get(), 1); |
| 1394 | |
| 1395 | if (BN_cmp_word(r.get(), 0) >= 0 || |
| 1396 | BN_cmp_word(r.get(), kMaxWord) >= 0) { |
| 1397 | fprintf(stderr, |
| 1398 | "BN_cmp_word compared against -kMaxWord - 1 incorrectly.\n"); |
| 1399 | return false; |
| 1400 | } |
| 1401 | |
| 1402 | return true; |
| 1403 | } |
| 1404 | |
David Benjamin | 7c04075 | 2016-08-22 22:19:01 -0700 | [diff] [blame^] | 1405 | static bool TestBN2Dec() { |
| 1406 | static const char *kBN2DecTests[] = { |
| 1407 | "0", |
| 1408 | "1", |
| 1409 | "-1", |
| 1410 | "100", |
| 1411 | "-100", |
| 1412 | "123456789012345678901234567890", |
| 1413 | "-123456789012345678901234567890", |
| 1414 | "123456789012345678901234567890123456789012345678901234567890", |
| 1415 | "-123456789012345678901234567890123456789012345678901234567890", |
| 1416 | }; |
| 1417 | |
| 1418 | for (const char *test : kBN2DecTests) { |
| 1419 | ScopedBIGNUM bn; |
| 1420 | int ret = DecimalToBIGNUM(&bn, test); |
| 1421 | if (ret == 0) { |
| 1422 | return false; |
| 1423 | } |
| 1424 | |
| 1425 | ScopedOpenSSLString dec(BN_bn2dec(bn.get())); |
| 1426 | if (!dec) { |
| 1427 | fprintf(stderr, "BN_bn2dec failed on %s.\n", test); |
| 1428 | return false; |
| 1429 | } |
| 1430 | |
| 1431 | if (strcmp(dec.get(), test) != 0) { |
| 1432 | fprintf(stderr, "BN_bn2dec gave %s, wanted %s.\n", dec.get(), test); |
| 1433 | return false; |
| 1434 | } |
| 1435 | } |
| 1436 | |
| 1437 | return true; |
| 1438 | } |
| 1439 | |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1440 | int main(int argc, char *argv[]) { |
| 1441 | CRYPTO_library_init(); |
| 1442 | |
| 1443 | if (argc != 2) { |
| 1444 | fprintf(stderr, "%s TEST_FILE\n", argv[0]); |
| 1445 | return 1; |
| 1446 | } |
| 1447 | |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame] | 1448 | ScopedBN_CTX ctx(BN_CTX_new()); |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1449 | if (!ctx) { |
| 1450 | return 1; |
| 1451 | } |
| 1452 | |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame] | 1453 | if (!TestBN2BinPadded(ctx.get()) || |
| 1454 | !TestDec2BN(ctx.get()) || |
| 1455 | !TestHex2BN(ctx.get()) || |
| 1456 | !TestASC2BN(ctx.get()) || |
| 1457 | !TestMPI() || |
| 1458 | !TestRand() || |
| 1459 | !TestASN1() || |
| 1460 | !TestNegativeZero(ctx.get()) || |
| 1461 | !TestBadModulus(ctx.get()) || |
| 1462 | !TestExpModZero() || |
David Benjamin | ccd511e | 2016-07-29 12:11:34 -0400 | [diff] [blame] | 1463 | !TestSmallPrime(ctx.get()) || |
David Benjamin | 7c04075 | 2016-08-22 22:19:01 -0700 | [diff] [blame^] | 1464 | !TestCmpWord() || |
| 1465 | !TestBN2Dec()) { |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1466 | return 1; |
| 1467 | } |
| 1468 | |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame] | 1469 | return FileTestMain(RunTest, ctx.get(), argv[1]); |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1470 | } |