summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Sloan <varomodt@google.com>2018-04-03 11:30:38 -0700
committerRobert Sloan <varomodt@google.com>2018-04-03 11:30:43 -0700
commit49d063bbd8327cb483bdee6ea604c068f1bb044e (patch)
tree8b2ae8c3a195cd4a8fed7bc84fafe7eb069f19c7 /src
parentab8b888152733533e60c1ebbe8438594a3a2e3d7 (diff)
downloadboringssl-49d063bbd8327cb483bdee6ea604c068f1bb044e.tar.gz
external/boringssl: Sync to ba9da449a4bf5b90cd020807f2c4176e3ab6fe3e.
This includes the following changes: https://boringssl.googlesource.com/boringssl/+log/d89d65ba12e28e543df4fd9dfbc687bb8be1dba7..ba9da449a4bf5b90cd020807f2c4176e3ab6fe3e Test: BoringSSL CTS Presubmits. Change-Id: Id99aa90153bb7fc37270e9c58d406e5d8c7c44d3
Diffstat (limited to 'src')
-rw-r--r--src/LICENSE70
-rw-r--r--src/crypto/asn1/asn1_test.cc62
-rw-r--r--src/crypto/asn1/tasn_dec.c59
-rw-r--r--src/crypto/constant_time_test.cc19
-rw-r--r--src/crypto/err/asn1.errordata1
-rw-r--r--src/crypto/err/rsa.errordata1
-rw-r--r--src/crypto/fipsmodule/bn/add.c84
-rw-r--r--src/crypto/fipsmodule/bn/bn.c8
-rw-r--r--src/crypto/fipsmodule/bn/bn_test.cc218
-rw-r--r--src/crypto/fipsmodule/bn/bn_tests.txt163
-rw-r--r--src/crypto/fipsmodule/bn/check_bn_tests.go7
-rw-r--r--src/crypto/fipsmodule/bn/cmp.c16
-rw-r--r--src/crypto/fipsmodule/bn/div.c154
-rw-r--r--src/crypto/fipsmodule/bn/gcd.c492
-rw-r--r--src/crypto/fipsmodule/bn/internal.h171
-rw-r--r--src/crypto/fipsmodule/bn/montgomery.c17
-rw-r--r--src/crypto/fipsmodule/bn/montgomery_inv.c2
-rw-r--r--src/crypto/fipsmodule/bn/mul.c30
-rw-r--r--src/crypto/fipsmodule/bn/prime.c393
-rw-r--r--src/crypto/fipsmodule/bn/random.c148
-rw-r--r--src/crypto/fipsmodule/bn/shift.c212
-rw-r--r--src/crypto/fipsmodule/bn/sqrt.c2
-rw-r--r--src/crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt3101
-rw-r--r--src/crypto/fipsmodule/ec/ec_test.cc190
-rw-r--r--src/crypto/fipsmodule/ec/internal.h4
-rw-r--r--src/crypto/fipsmodule/ec/make_ec_scalar_base_mult_tests.go60
-rw-r--r--src/crypto/fipsmodule/ec/oct.c10
-rw-r--r--src/crypto/fipsmodule/ec/p224-64.c58
-rw-r--r--src/crypto/fipsmodule/ec/simple.c74
-rw-r--r--src/crypto/fipsmodule/ecdsa/ecdsa.c81
-rw-r--r--src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt109
-rw-r--r--src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt87
-rw-r--r--src/crypto/fipsmodule/rsa/internal.h7
-rw-r--r--src/crypto/fipsmodule/rsa/rsa.c70
-rw-r--r--src/crypto/fipsmodule/rsa/rsa_impl.c112
-rw-r--r--src/crypto/rsa_extra/rsa_test.cc65
-rw-r--r--src/crypto/x509/vpm_int.h1
-rw-r--r--src/crypto/x509/x509_test.cc248
-rw-r--r--src/crypto/x509/x509_vfy.c4
-rw-r--r--src/crypto/x509/x509_vpm.c70
-rw-r--r--src/include/openssl/asn1.h1
-rw-r--r--src/include/openssl/bn.h27
-rw-r--r--src/include/openssl/rsa.h1
-rw-r--r--src/include/openssl/x509v3.h2
-rw-r--r--src/sources.cmake1
-rw-r--r--src/tool/speed.cc88
-rw-r--r--src/util/bot/DEPS2
-rwxr-xr-xsrc/util/bot/go/bootstrap.py2
-rw-r--r--src/util/bot/update_clang.py2
49 files changed, 5837 insertions, 969 deletions
diff --git a/src/LICENSE b/src/LICENSE
index d133584e..49c41fa7 100644
--- a/src/LICENSE
+++ b/src/LICENSE
@@ -179,3 +179,73 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+
+
+Licenses for support code
+-------------------------
+
+Parts of the TLS test suite are under the Go license. This code is not included
+in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so
+distributing code linked against BoringSSL does not trigger this license:
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+BoringSSL uses the Chromium test infrastructure to run a continuous build,
+trybots etc. The scripts which manage this, and the script for generating build
+metadata, are under the Chromium license. Distributing code linked against
+BoringSSL does not trigger this license.
+
+Copyright 2015 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/crypto/asn1/asn1_test.cc b/src/crypto/asn1/asn1_test.cc
index 7c114ddf..1cca36ce 100644
--- a/src/crypto/asn1/asn1_test.cc
+++ b/src/crypto/asn1/asn1_test.cc
@@ -12,13 +12,18 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <limits.h>
#include <stdio.h>
+#include <vector>
+
#include <gtest/gtest.h>
-#include <limits.h>
#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/bytestring.h>
#include <openssl/err.h>
+#include <openssl/mem.h>
#include "../test/test_util.h"
@@ -88,3 +93,58 @@ TEST(ASN1Test, IntegerSetting) {
}
}
}
+
+typedef struct asn1_linked_list_st {
+ struct asn1_linked_list_st *next;
+} ASN1_LINKED_LIST;
+
+DECLARE_ASN1_ITEM(ASN1_LINKED_LIST)
+DECLARE_ASN1_FUNCTIONS(ASN1_LINKED_LIST)
+
+ASN1_SEQUENCE(ASN1_LINKED_LIST) = {
+ ASN1_OPT(ASN1_LINKED_LIST, next, ASN1_LINKED_LIST),
+} ASN1_SEQUENCE_END(ASN1_LINKED_LIST)
+
+IMPLEMENT_ASN1_FUNCTIONS(ASN1_LINKED_LIST)
+
+static bool MakeLinkedList(bssl::UniquePtr<uint8_t> *out, size_t *out_len,
+ size_t count) {
+ bssl::ScopedCBB cbb;
+ std::vector<CBB> cbbs(count);
+ if (!CBB_init(cbb.get(), 2 * count) ||
+ !CBB_add_asn1(cbb.get(), &cbbs[0], CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ for (size_t i = 1; i < count; i++) {
+ if (!CBB_add_asn1(&cbbs[i - 1], &cbbs[i], CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ }
+ uint8_t *ptr;
+ if (!CBB_finish(cbb.get(), &ptr, out_len)) {
+ return false;
+ }
+ out->reset(ptr);
+ return true;
+}
+
+TEST(ASN1Test, Recursive) {
+ bssl::UniquePtr<uint8_t> data;
+ size_t len;
+
+ // Sanity-check that MakeLinkedList can be parsed.
+ ASSERT_TRUE(MakeLinkedList(&data, &len, 5));
+ const uint8_t *ptr = data.get();
+ ASN1_LINKED_LIST *list = d2i_ASN1_LINKED_LIST(nullptr, &ptr, len);
+ EXPECT_TRUE(list);
+ ASN1_LINKED_LIST_free(list);
+
+ // Excessively deep structures are rejected.
+ ASSERT_TRUE(MakeLinkedList(&data, &len, 100));
+ ptr = data.get();
+ list = d2i_ASN1_LINKED_LIST(nullptr, &ptr, len);
+ EXPECT_FALSE(list);
+ // Note checking the error queue here does not work. The error "stack trace"
+ // is too deep, so the |ASN1_R_NESTED_TOO_DEEP| entry drops off the queue.
+ ASN1_LINKED_LIST_free(list);
+}
diff --git a/src/crypto/asn1/tasn_dec.c b/src/crypto/asn1/tasn_dec.c
index 2f5f132a..32aba0bd 100644
--- a/src/crypto/asn1/tasn_dec.c
+++ b/src/crypto/asn1/tasn_dec.c
@@ -66,6 +66,14 @@
#include "../internal.h"
+/*
+ * Constructed types with a recursive definition (such as can be found in PKCS7)
+ * could eventually exceed the stack given malicious input with excessive
+ * recursion. Therefore we limit the stack depth. This is the maximum number of
+ * recursive invocations of asn1_item_embed_d2i().
+ */
+#define ASN1_MAX_CONSTRUCTED_NEST 30
+
static int asn1_check_eoc(const unsigned char **in, long len);
static int asn1_find_end(const unsigned char **in, long len, char inf);
@@ -82,11 +90,11 @@ static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
static int asn1_template_ex_d2i(ASN1_VALUE **pval,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx);
+ ASN1_TLC *ctx, int depth);
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx);
+ ASN1_TLC *ctx, int depth);
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
const unsigned char **in, long len,
const ASN1_ITEM *it,
@@ -153,9 +161,9 @@ ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval,
* tag mismatch return -1 to handle OPTIONAL
*/
-int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
- const ASN1_ITEM *it,
- int tag, int aclass, char opt, ASN1_TLC *ctx)
+static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in,
+ long len, const ASN1_ITEM *it, int tag, int aclass,
+ char opt, ASN1_TLC *ctx, int depth)
{
const ASN1_TEMPLATE *tt, *errtt = NULL;
const ASN1_COMPAT_FUNCS *cf;
@@ -188,6 +196,11 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
len = INT_MAX/2;
}
+ if (++depth > ASN1_MAX_CONSTRUCTED_NEST) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_TOO_DEEP);
+ goto err;
+ }
+
switch (it->itype) {
case ASN1_ITYPE_PRIMITIVE:
if (it->templates) {
@@ -203,7 +216,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
goto err;
}
return asn1_template_ex_d2i(pval, in, len,
- it->templates, opt, ctx);
+ it->templates, opt, ctx, depth);
}
return asn1_d2i_ex_primitive(pval, in, len, it,
tag, aclass, opt, ctx);
@@ -326,7 +339,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
/*
* We mark field as OPTIONAL so its absence can be recognised.
*/
- ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx);
+ ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx, depth);
/* If field not present, try the next one */
if (ret == -1)
continue;
@@ -444,7 +457,8 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
* attempt to read in field, allowing each to be OPTIONAL
*/
- ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx);
+ ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx,
+ depth);
if (!ret) {
errtt = seqtt;
goto err;
@@ -514,6 +528,13 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
return 0;
}
+int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
+ const ASN1_ITEM *it,
+ int tag, int aclass, char opt, ASN1_TLC *ctx)
+{
+ return asn1_item_ex_d2i(pval, in, len, it, tag, aclass, opt, ctx, 0);
+}
+
/*
* Templates are handled with two separate functions. One handles any
* EXPLICIT tag and the other handles the rest.
@@ -522,7 +543,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
static int asn1_template_ex_d2i(ASN1_VALUE **val,
const unsigned char **in, long inlen,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx)
+ ASN1_TLC *ctx, int depth)
{
int flags, aclass;
int ret;
@@ -556,7 +577,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
return 0;
}
/* We've found the field so it can't be OPTIONAL now */
- ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx);
+ ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx, depth);
if (!ret) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
return 0;
@@ -579,7 +600,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
}
}
} else
- return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx);
+ return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx, depth);
*in = p;
return 1;
@@ -592,7 +613,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx)
+ ASN1_TLC *ctx, int depth)
{
int flags, aclass;
int ret;
@@ -661,8 +682,8 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
break;
}
skfield = NULL;
- if (!ASN1_item_ex_d2i(&skfield, &p, len,
- ASN1_ITEM_ptr(tt->item), -1, 0, 0, ctx)) {
+ if (!asn1_item_ex_d2i(&skfield, &p, len, ASN1_ITEM_ptr(tt->item),
+ -1, 0, 0, ctx, depth)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
}
@@ -679,9 +700,8 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
}
} else if (flags & ASN1_TFLG_IMPTAG) {
/* IMPLICIT tagging */
- ret = ASN1_item_ex_d2i(val, &p, len,
- ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt,
- ctx);
+ ret = asn1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item), tt->tag,
+ aclass, opt, ctx, depth);
if (!ret) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
@@ -689,8 +709,9 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
return -1;
} else {
/* Nothing special */
- ret = ASN1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
- -1, tt->flags & ASN1_TFLG_COMBINE, opt, ctx);
+ ret = asn1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
+ -1, tt->flags & ASN1_TFLG_COMBINE, opt, ctx,
+ depth);
if (!ret) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
diff --git a/src/crypto/constant_time_test.cc b/src/crypto/constant_time_test.cc
index 3cb5866a..59a7bb18 100644
--- a/src/crypto/constant_time_test.cc
+++ b/src/crypto/constant_time_test.cc
@@ -53,6 +53,9 @@
#include <gtest/gtest.h>
+#include <openssl/mem.h>
+#include <openssl/rand.h>
+
static uint8_t FromBool8(bool b) {
return b ? CONSTTIME_TRUE_8 : CONSTTIME_FALSE_8;
@@ -134,3 +137,19 @@ TEST(ConstantTimeTest, Test) {
}
}
}
+
+TEST(ConstantTimeTest, MemCmp) {
+ uint8_t buf[256], copy[256];
+ RAND_bytes(buf, sizeof(buf));
+
+ OPENSSL_memcpy(copy, buf, sizeof(buf));
+ EXPECT_EQ(0, CRYPTO_memcmp(buf, copy, sizeof(buf)));
+
+ for (size_t i = 0; i < sizeof(buf); i++) {
+ for (uint8_t bit = 1; bit != 0; bit <<= 1) {
+ OPENSSL_memcpy(copy, buf, sizeof(buf));
+ copy[i] ^= bit;
+ EXPECT_NE(0, CRYPTO_memcmp(buf, copy, sizeof(buf)));
+ }
+ }
+}
diff --git a/src/crypto/err/asn1.errordata b/src/crypto/err/asn1.errordata
index c304b2ca..56cbbe52 100644
--- a/src/crypto/err/asn1.errordata
+++ b/src/crypto/err/asn1.errordata
@@ -58,6 +58,7 @@ ASN1,156,MSTRING_NOT_UNIVERSAL
ASN1,157,MSTRING_WRONG_TAG
ASN1,158,NESTED_ASN1_ERROR
ASN1,159,NESTED_ASN1_STRING
+ASN1,192,NESTED_TOO_DEEP
ASN1,160,NON_HEX_CHARACTERS
ASN1,161,NOT_ASCII_FORMAT
ASN1,162,NOT_ENOUGH_DATA
diff --git a/src/crypto/err/rsa.errordata b/src/crypto/err/rsa.errordata
index 9d18e04a..75d265a5 100644
--- a/src/crypto/err/rsa.errordata
+++ b/src/crypto/err/rsa.errordata
@@ -18,6 +18,7 @@ RSA,116,DATA_TOO_SMALL
RSA,117,DATA_TOO_SMALL_FOR_KEY_SIZE
RSA,118,DIGEST_TOO_BIG_FOR_RSA_KEY
RSA,119,D_E_NOT_CONGRUENT_TO_1
+RSA,147,D_OUT_OF_RANGE
RSA,120,EMPTY_PUBLIC_KEY
RSA,121,ENCODE_ERROR
RSA,122,FIRST_OCTET_INVALID
diff --git a/src/crypto/fipsmodule/bn/add.c b/src/crypto/fipsmodule/bn/add.c
index ece78671..38a84506 100644
--- a/src/crypto/fipsmodule/bn/add.c
+++ b/src/crypto/fipsmodule/bn/add.c
@@ -100,7 +100,7 @@ int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
return ret;
}
-int bn_uadd_fixed(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+int bn_uadd_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
// Widths are public, so we normalize to make |a| the larger one.
if (a->width < b->width) {
const BIGNUM *tmp = a;
@@ -128,7 +128,7 @@ int bn_uadd_fixed(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
}
int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
- if (!bn_uadd_fixed(r, a, b)) {
+ if (!bn_uadd_consttime(r, a, b)) {
return 0;
}
bn_set_minimal_width(r);
@@ -223,69 +223,45 @@ int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
return 1;
}
-int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
- int max, min, dif;
- register BN_ULONG t1, t2, *ap, *bp, *rp;
- int i, carry;
-
- max = bn_minimal_width(a);
- min = bn_minimal_width(b);
- dif = max - min;
-
- if (dif < 0) // hmm... should not be happening
- {
- OPENSSL_PUT_ERROR(BN, BN_R_ARG2_LT_ARG3);
- return 0;
+int bn_usub_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+ // |b| may have more words than |a| given non-minimal inputs, but all words
+ // beyond |a->width| must then be zero.
+ int b_width = b->width;
+ if (b_width > a->width) {
+ if (!bn_fits_in_words(b, a->width)) {
+ OPENSSL_PUT_ERROR(BN, BN_R_ARG2_LT_ARG3);
+ return 0;
+ }
+ b_width = a->width;
}
- if (!bn_wexpand(r, max)) {
+ if (!bn_wexpand(r, a->width)) {
return 0;
}
- ap = a->d;
- bp = b->d;
- rp = r->d;
-
- carry = 0;
- for (i = min; i != 0; i--) {
- t1 = *(ap++);
- t2 = *(bp++);
- if (carry) {
- carry = (t1 <= t2);
- t1 -= t2 + 1;
- } else {
- carry = (t1 < t2);
- t1 -= t2;
- }
- *(rp++) = t1;
- }
-
- if (carry) // subtracted
- {
- if (!dif) {
- // error: a < b
- return 0;
- }
-
- while (dif) {
- dif--;
- t1 = *(ap++);
- t2 = t1 - 1;
- *(rp++) = t2;
- if (t1) {
- break;
- }
- }
+ BN_ULONG borrow = bn_sub_words(r->d, a->d, b->d, b_width);
+ for (int i = b_width; i < a->width; i++) {
+ // |r| and |a| may alias, so use a temporary.
+ BN_ULONG tmp = a->d[i];
+ r->d[i] = a->d[i] - borrow;
+ borrow = tmp < r->d[i];
}
- if (dif > 0 && rp != ap) {
- OPENSSL_memcpy(rp, ap, sizeof(*rp) * dif);
+ if (borrow) {
+ OPENSSL_PUT_ERROR(BN, BN_R_ARG2_LT_ARG3);
+ return 0;
}
- r->width = max;
+ r->width = a->width;
r->neg = 0;
- bn_set_minimal_width(r);
+ return 1;
+}
+int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+ if (!bn_usub_consttime(r, a, b)) {
+ return 0;
+ }
+ bn_set_minimal_width(r);
return 1;
}
diff --git a/src/crypto/fipsmodule/bn/bn.c b/src/crypto/fipsmodule/bn/bn.c
index 4e5043fd..c020d964 100644
--- a/src/crypto/fipsmodule/bn/bn.c
+++ b/src/crypto/fipsmodule/bn/bn.c
@@ -187,13 +187,19 @@ unsigned BN_num_bits_word(BN_ULONG l) {
int bits = (l != 0);
#if BN_BITS2 > 32
+ // Look at the upper half of |x|. |x| is at most 64 bits long.
x = l >> 32;
+ // Set |mask| to all ones if |x| (the top 32 bits of |l|) is non-zero and all
+ // all zeros otherwise.
mask = 0u - x;
mask = (0u - (mask >> (BN_BITS2 - 1)));
+ // If |x| is non-zero, the lower half is included in the bit count in full,
+ // and we count the upper half. Otherwise, we count the lower half.
bits += 32 & mask;
- l ^= (x ^ l) & mask;
+ l ^= (x ^ l) & mask; // |l| is |x| if |mask| and remains |l| otherwise.
#endif
+ // The remaining blocks are analogous iterations at lower powers of two.
x = l >> 16;
mask = 0u - x;
mask = (0u - (mask >> (BN_BITS2 - 1)));
diff --git a/src/crypto/fipsmodule/bn/bn_test.cc b/src/crypto/fipsmodule/bn/bn_test.cc
index f73054d2..93d6d0ff 100644
--- a/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/src/crypto/fipsmodule/bn/bn_test.cc
@@ -239,8 +239,7 @@ static void TestSum(BIGNUMFileTest *t, BN_CTX *ctx) {
// having. Note that these functions are frequently used when the
// prerequisites don't hold. In those cases, they are supposed to work as if
// the prerequisite hold, but we don't test that yet. TODO: test that.
- if (!BN_is_negative(a.get()) &&
- !BN_is_negative(b.get()) && BN_cmp(a.get(), b.get()) >= 0) {
+ if (!BN_is_negative(a.get()) && !BN_is_negative(b.get())) {
ASSERT_TRUE(BN_uadd(ret.get(), a.get(), b.get()));
EXPECT_BIGNUMS_EQUAL("A +u B", sum.get(), ret.get());
@@ -276,6 +275,16 @@ static void TestSum(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(BN_copy(ret.get(), b.get()));
ASSERT_TRUE(BN_usub(ret.get(), sum.get(), ret.get()));
EXPECT_BIGNUMS_EQUAL("Sum -u B (r is b)", a.get(), ret.get());
+
+ ASSERT_TRUE(bn_abs_sub_consttime(ret.get(), sum.get(), a.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("|Sum - A|", b.get(), ret.get());
+ ASSERT_TRUE(bn_abs_sub_consttime(ret.get(), a.get(), sum.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("|A - Sum|", b.get(), ret.get());
+
+ ASSERT_TRUE(bn_abs_sub_consttime(ret.get(), sum.get(), b.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("|Sum - B|", a.get(), ret.get());
+ ASSERT_TRUE(bn_abs_sub_consttime(ret.get(), b.get(), sum.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("|B - Sum|", a.get(), ret.get());
}
// Test with |BN_add_word| and |BN_sub_word| if |b| is small enough.
@@ -321,12 +330,18 @@ static void TestLShift1(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(BN_lshift1(ret.get(), a.get()));
EXPECT_BIGNUMS_EQUAL("A << 1", lshift1.get(), ret.get());
- ASSERT_TRUE(BN_rshift1(ret.get(), lshift1.get()));
- EXPECT_BIGNUMS_EQUAL("LShift >> 1", a.get(), ret.get());
+ ASSERT_TRUE(BN_lshift(ret.get(), a.get(), 1));
+ EXPECT_BIGNUMS_EQUAL("A << 1 (variable shift)", lshift1.get(), ret.get());
ASSERT_TRUE(BN_rshift1(ret.get(), lshift1.get()));
EXPECT_BIGNUMS_EQUAL("LShift >> 1", a.get(), ret.get());
+ ASSERT_TRUE(BN_rshift(ret.get(), lshift1.get(), 1));
+ EXPECT_BIGNUMS_EQUAL("LShift >> 1 (variable shift)", a.get(), ret.get());
+
+ ASSERT_TRUE(bn_rshift_secret_shift(ret.get(), lshift1.get(), 1, ctx));
+ EXPECT_BIGNUMS_EQUAL("LShift >> 1 (secret shift)", a.get(), ret.get());
+
// Set the LSB to 1 and test rshift1 again.
ASSERT_TRUE(BN_set_bit(lshift1.get(), 0));
ASSERT_TRUE(
@@ -335,6 +350,13 @@ static void TestLShift1(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(BN_rshift1(ret.get(), lshift1.get()));
EXPECT_BIGNUMS_EQUAL("(LShift | 1) >> 1", a.get(), ret.get());
+
+ ASSERT_TRUE(BN_rshift(ret.get(), lshift1.get(), 1));
+ EXPECT_BIGNUMS_EQUAL("(LShift | 1) >> 1 (variable shift)", a.get(),
+ ret.get());
+
+ ASSERT_TRUE(bn_rshift_secret_shift(ret.get(), lshift1.get(), 1, ctx));
+ EXPECT_BIGNUMS_EQUAL("(LShift | 1) >> 1 (secret shift)", a.get(), ret.get());
}
static void TestLShift(BIGNUMFileTest *t, BN_CTX *ctx) {
@@ -350,8 +372,15 @@ static void TestLShift(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(BN_lshift(ret.get(), a.get(), n));
EXPECT_BIGNUMS_EQUAL("A << N", lshift.get(), ret.get());
+ ASSERT_TRUE(BN_copy(ret.get(), a.get()));
+ ASSERT_TRUE(BN_lshift(ret.get(), ret.get(), n));
+ EXPECT_BIGNUMS_EQUAL("A << N (in-place)", lshift.get(), ret.get());
+
ASSERT_TRUE(BN_rshift(ret.get(), lshift.get(), n));
EXPECT_BIGNUMS_EQUAL("A >> N", a.get(), ret.get());
+
+ ASSERT_TRUE(bn_rshift_secret_shift(ret.get(), lshift.get(), n, ctx));
+ EXPECT_BIGNUMS_EQUAL("A >> N (secret shift)", a.get(), ret.get());
}
static void TestRShift(BIGNUMFileTest *t, BN_CTX *ctx) {
@@ -366,6 +395,18 @@ static void TestRShift(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(ret);
ASSERT_TRUE(BN_rshift(ret.get(), a.get(), n));
EXPECT_BIGNUMS_EQUAL("A >> N", rshift.get(), ret.get());
+
+ ASSERT_TRUE(BN_copy(ret.get(), a.get()));
+ ASSERT_TRUE(BN_rshift(ret.get(), ret.get(), n));
+ EXPECT_BIGNUMS_EQUAL("A >> N (in-place)", rshift.get(), ret.get());
+
+ ASSERT_TRUE(bn_rshift_secret_shift(ret.get(), a.get(), n, ctx));
+ EXPECT_BIGNUMS_EQUAL("A >> N (secret shift)", rshift.get(), ret.get());
+
+ ASSERT_TRUE(BN_copy(ret.get(), a.get()));
+ ASSERT_TRUE(bn_rshift_secret_shift(ret.get(), ret.get(), n, ctx));
+ EXPECT_BIGNUMS_EQUAL("A >> N (in-place secret shift)", rshift.get(),
+ ret.get());
}
static void TestSquare(BIGNUMFileTest *t, BN_CTX *ctx) {
@@ -525,9 +566,31 @@ static void TestQuotient(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(BN_add(ret.get(), ret.get(), remainder.get()));
EXPECT_BIGNUMS_EQUAL("Quotient * B + Remainder", a.get(), ret.get());
+ // The remaining division variants only handle a positive quotient.
+ if (BN_is_negative(b.get())) {
+ BN_set_negative(b.get(), 0);
+ BN_set_negative(quotient.get(), !BN_is_negative(quotient.get()));
+ }
+
+ bssl::UniquePtr<BIGNUM> nnmod(BN_new());
+ ASSERT_TRUE(nnmod);
+ ASSERT_TRUE(BN_copy(nnmod.get(), remainder.get()));
+ if (BN_is_negative(nnmod.get())) {
+ ASSERT_TRUE(BN_add(nnmod.get(), nnmod.get(), b.get()));
+ }
+ ASSERT_TRUE(BN_nnmod(ret.get(), a.get(), b.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("A % B (non-negative)", nnmod.get(), ret.get());
+
+ // The remaining division variants only handle a positive numerator.
+ if (BN_is_negative(a.get())) {
+ BN_set_negative(a.get(), 0);
+ BN_set_negative(quotient.get(), 0);
+ BN_set_negative(remainder.get(), 0);
+ }
+
// Test with |BN_mod_word| and |BN_div_word| if the divisor is small enough.
BN_ULONG b_word = BN_get_word(b.get());
- if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) {
+ if (b_word != (BN_ULONG)-1) {
BN_ULONG remainder_word = BN_get_word(remainder.get());
ASSERT_NE(remainder_word, (BN_ULONG)-1);
ASSERT_TRUE(BN_copy(ret.get(), a.get()));
@@ -537,19 +600,15 @@ static void TestQuotient(BIGNUMFileTest *t, BN_CTX *ctx) {
ret_word = BN_mod_word(a.get(), b_word);
EXPECT_EQ(remainder_word, ret_word);
- }
- // Test BN_nnmod.
- if (!BN_is_negative(b.get())) {
- bssl::UniquePtr<BIGNUM> nnmod(BN_new());
- ASSERT_TRUE(nnmod);
- ASSERT_TRUE(BN_copy(nnmod.get(), remainder.get()));
- if (BN_is_negative(nnmod.get())) {
- ASSERT_TRUE(BN_add(nnmod.get(), nnmod.get(), b.get()));
+ if (b_word <= 0xffff) {
+ EXPECT_EQ(remainder_word, bn_mod_u16_consttime(a.get(), b_word));
}
- ASSERT_TRUE(BN_nnmod(ret.get(), a.get(), b.get(), ctx));
- EXPECT_BIGNUMS_EQUAL("A % B (non-negative)", nnmod.get(), ret.get());
}
+
+ ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("A / B (constant-time)", quotient.get(), ret.get());
+ EXPECT_BIGNUMS_EQUAL("A % B (constant-time)", remainder.get(), ret2.get());
}
static void TestModMul(BIGNUMFileTest *t, BN_CTX *ctx) {
@@ -808,15 +867,24 @@ static void TestModInv(BIGNUMFileTest *t, BN_CTX *ctx) {
ASSERT_TRUE(BN_gcd(ret.get(), a.get(), m.get(), ctx));
EXPECT_BIGNUMS_EQUAL("GCD(A, M)", BN_value_one(), ret.get());
+
+ ASSERT_TRUE(BN_nnmod(a.get(), a.get(), m.get(), ctx));
+ int no_inverse;
+ ASSERT_TRUE(
+ bn_mod_inverse_consttime(ret.get(), &no_inverse, a.get(), m.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("inv(A) (mod M) (constant-time)", mod_inv.get(),
+ ret.get());
}
static void TestGCD(BIGNUMFileTest *t, BN_CTX *ctx) {
bssl::UniquePtr<BIGNUM> a = t->GetBIGNUM("A");
bssl::UniquePtr<BIGNUM> b = t->GetBIGNUM("B");
bssl::UniquePtr<BIGNUM> gcd = t->GetBIGNUM("GCD");
+ bssl::UniquePtr<BIGNUM> lcm = t->GetBIGNUM("LCM");
ASSERT_TRUE(a);
ASSERT_TRUE(b);
ASSERT_TRUE(gcd);
+ ASSERT_TRUE(lcm);
bssl::UniquePtr<BIGNUM> ret(BN_new());
ASSERT_TRUE(ret);
@@ -828,6 +896,38 @@ static void TestGCD(BIGNUMFileTest *t, BN_CTX *ctx) {
<< "A^-1 (mod B) computed, but it does not exist";
EXPECT_FALSE(BN_mod_inverse(ret.get(), b.get(), a.get(), ctx))
<< "B^-1 (mod A) computed, but it does not exist";
+
+ if (!BN_is_zero(b.get())) {
+ bssl::UniquePtr<BIGNUM> a_reduced(BN_new());
+ ASSERT_TRUE(a_reduced);
+ ASSERT_TRUE(BN_nnmod(a_reduced.get(), a.get(), b.get(), ctx));
+ int no_inverse;
+ EXPECT_FALSE(bn_mod_inverse_consttime(ret.get(), &no_inverse,
+ a_reduced.get(), b.get(), ctx))
+ << "A^-1 (mod B) computed, but it does not exist";
+ EXPECT_TRUE(no_inverse);
+ }
+
+ if (!BN_is_zero(a.get())) {
+ bssl::UniquePtr<BIGNUM> b_reduced(BN_new());
+ ASSERT_TRUE(b_reduced);
+ ASSERT_TRUE(BN_nnmod(b_reduced.get(), b.get(), a.get(), ctx));
+ int no_inverse;
+ EXPECT_FALSE(bn_mod_inverse_consttime(ret.get(), &no_inverse,
+ b_reduced.get(), a.get(), ctx))
+ << "B^-1 (mod A) computed, but it does not exist";
+ EXPECT_TRUE(no_inverse);
+ }
+ }
+
+ int is_relative_prime;
+ ASSERT_TRUE(
+ bn_is_relatively_prime(&is_relative_prime, a.get(), b.get(), ctx));
+ EXPECT_EQ(is_relative_prime, BN_is_one(gcd.get()));
+
+ if (!BN_is_zero(gcd.get())) {
+ ASSERT_TRUE(bn_lcm_consttime(ret.get(), a.get(), b.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("LCM(A, B)", lcm.get(), ret.get());
}
}
@@ -1856,6 +1956,7 @@ TEST_F(BNTest, PrimeChecking) {
bssl::UniquePtr<BIGNUM> p(BN_new());
ASSERT_TRUE(p);
int is_probably_prime_1 = 0, is_probably_prime_2 = 0;
+ enum bn_primality_result_t result_3;
const int max_prime = kPrimes[OPENSSL_ARRAY_SIZE(kPrimes)-1];
size_t next_prime_index = 0;
@@ -1878,6 +1979,11 @@ TEST_F(BNTest, PrimeChecking) {
&is_probably_prime_2, p.get(), BN_prime_checks, ctx(),
true /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(is_prime ? 1 : 0, is_probably_prime_2);
+ if (i > 3 && i % 2 == 1) {
+ ASSERT_TRUE(BN_enhanced_miller_rabin_primality_test(
+ &result_3, p.get(), BN_prime_checks, ctx(), nullptr /* callback */));
+ EXPECT_EQ(is_prime, result_3 == bn_probably_prime);
+ }
}
// Negative numbers are not prime.
@@ -1920,7 +2026,18 @@ TEST_F(BNTest, PrimeChecking) {
&is_probably_prime_2, p.get(), BN_prime_checks, ctx(),
true /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(0, is_probably_prime_2);
+
+ ASSERT_TRUE(BN_enhanced_miller_rabin_primality_test(
+ &result_3, p.get(), BN_prime_checks, ctx(), nullptr /* callback */));
+ EXPECT_EQ(bn_composite, result_3);
}
+
+ // BN_primality_test works with null |BN_CTX|.
+ ASSERT_TRUE(BN_set_word(p.get(), 5));
+ ASSERT_TRUE(BN_primality_test(
+ &is_probably_prime_1, p.get(), BN_prime_checks, nullptr /* ctx */,
+ false /* do_trial_division */, nullptr /* callback */));
+ EXPECT_EQ(1, is_probably_prime_1);
}
TEST_F(BNTest, NumBitsWord) {
@@ -2168,42 +2285,41 @@ TEST_F(BNTest, NonMinimal) {
}
TEST_F(BNTest, CountLowZeroBits) {
- bssl::UniquePtr<BIGNUM> ten(BN_new());
- ASSERT_TRUE(ten);
- ASSERT_TRUE(BN_set_word(ten.get(), 10));
-
- bssl::UniquePtr<BIGNUM> eight(BN_new());
- ASSERT_TRUE(eight);
- ASSERT_TRUE(BN_set_word(eight.get(), 8));
-
- bssl::UniquePtr<BIGNUM> two_exp_256(BN_new());
- ASSERT_TRUE(two_exp_256);
- ASSERT_TRUE(BN_lshift(two_exp_256.get(), BN_value_one(), 256));
-
- bssl::UniquePtr<BIGNUM> two_exp_256_plus_4(BN_new());
- ASSERT_TRUE(two_exp_256_plus_4);
- ASSERT_TRUE(BN_lshift(two_exp_256_plus_4.get(), BN_value_one(), 256));
- ASSERT_TRUE(BN_add_word(two_exp_256_plus_4.get(), 4));
+ bssl::UniquePtr<BIGNUM> bn(BN_new());
+ ASSERT_TRUE(bn);
- bssl::UniquePtr<BIGNUM> zero(BN_new());
- ASSERT_TRUE(zero);
- BN_zero(zero.get());
+ for (int i = 0; i < BN_BITS2; i++) {
+ SCOPED_TRACE(i);
+ for (int set_high_bits = 0; set_high_bits < 2; set_high_bits++) {
+ BN_ULONG word = ((BN_ULONG)1) << i;
+ if (set_high_bits) {
+ BN_ULONG junk;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&junk), sizeof(junk));
+ word |= junk & ~(word - 1);
+ }
+ SCOPED_TRACE(word);
+
+ ASSERT_TRUE(BN_set_word(bn.get(), word));
+ EXPECT_EQ(i, BN_count_low_zero_bits(bn.get()));
+ ASSERT_TRUE(bn_resize_words(bn.get(), 16));
+ EXPECT_EQ(i, BN_count_low_zero_bits(bn.get()));
+
+ ASSERT_TRUE(BN_set_word(bn.get(), word));
+ ASSERT_TRUE(BN_lshift(bn.get(), bn.get(), BN_BITS2 * 5));
+ EXPECT_EQ(i + BN_BITS2 * 5, BN_count_low_zero_bits(bn.get()));
+ ASSERT_TRUE(bn_resize_words(bn.get(), 16));
+ EXPECT_EQ(i + BN_BITS2 * 5, BN_count_low_zero_bits(bn.get()));
+
+ ASSERT_TRUE(BN_set_word(bn.get(), word));
+ ASSERT_TRUE(BN_set_bit(bn.get(), BN_BITS2 * 5));
+ EXPECT_EQ(i, BN_count_low_zero_bits(bn.get()));
+ ASSERT_TRUE(bn_resize_words(bn.get(), 16));
+ EXPECT_EQ(i, BN_count_low_zero_bits(bn.get()));
+ }
+ }
- EXPECT_EQ(1, BN_count_low_zero_bits(ten.get()));
- EXPECT_EQ(3, BN_count_low_zero_bits(eight.get()));
- EXPECT_EQ(256, BN_count_low_zero_bits(two_exp_256.get()));
- EXPECT_EQ(2, BN_count_low_zero_bits(two_exp_256_plus_4.get()));
- EXPECT_EQ(0, BN_count_low_zero_bits(zero.get()));
-
- ASSERT_TRUE(bn_resize_words(ten.get(), 16));
- ASSERT_TRUE(bn_resize_words(eight.get(), 16));
- ASSERT_TRUE(bn_resize_words(two_exp_256.get(), 16));
- ASSERT_TRUE(bn_resize_words(two_exp_256_plus_4.get(), 16));
- ASSERT_TRUE(bn_resize_words(zero.get(), 16));
-
- EXPECT_EQ(1, BN_count_low_zero_bits(ten.get()));
- EXPECT_EQ(3, BN_count_low_zero_bits(eight.get()));
- EXPECT_EQ(256, BN_count_low_zero_bits(two_exp_256.get()));
- EXPECT_EQ(2, BN_count_low_zero_bits(two_exp_256_plus_4.get()));
- EXPECT_EQ(0, BN_count_low_zero_bits(zero.get()));
+ BN_zero(bn.get());
+ EXPECT_EQ(0, BN_count_low_zero_bits(bn.get()));
+ ASSERT_TRUE(bn_resize_words(bn.get(), 16));
+ EXPECT_EQ(0, BN_count_low_zero_bits(bn.get()));
}
diff --git a/src/crypto/fipsmodule/bn/bn_tests.txt b/src/crypto/fipsmodule/bn/bn_tests.txt
index 101a449d..7f85a024 100644
--- a/src/crypto/fipsmodule/bn/bn_tests.txt
+++ b/src/crypto/fipsmodule/bn/bn_tests.txt
@@ -7796,6 +7796,91 @@ Remainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86ca
A = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b
B = -cf429f101a2e19a65af1e238f6745215cf476ff2609c846f10289f1ef21b89af2aec53def3f4ec07ea42041f8b5862dc37fd03b2df12adaa8c9f1933cc69b526d47797b40f49545fd093b8ceddee3c55721d1fa19b336218de0cac56d410cc6cff4e620578cf820f5cdaadc367dc4d6372aab1e0ae3831a6d153c14920b1dcf09e7629b7442a06385420d79742e409677e3b82ec58bcbfa668ca072e981e20728a983d84a432605389c855a6668e0ee0d2b67449
+Quotient = 6f949f45c70d69f65ace3e8d79071803fc6b8cbecc1ec1105ee6dd4e3a07577f1df5674853637faf6e5064ac86c3595627497311d749864c87ae8d6a0fcdbf258de637ac8db6cf079a230105582230644422186051875243269bdd6558b95eea7db6f16147554764d8a36d8faca89e8e7583576a0f9beb7142bf4d4d77d97c91
+Remainder = 0
+A = 6f949f45c70d69f65ace3e8d79071803fc6b8cbecc1ec1105ee6dd4e3a07577f1df5674853637faf6e5064ac86c3595627497311d749864c87ae8d6a0fcdbf258de637ac8db6cf079a230105582230644422186051875243269bdd6558b95eea7db6f16147554764d8a36d8faca89e8e7583576a0f9beb7142bf4d4d77d97c91
+B = 1
+
+Quotient = 3b5c3007d9c49498ff8437b6f0014d146b63c20b6c5b91febee47211f42109f6081204b21a8af99e9ab2b5165d536344fec16bd691fb3883ee7335e12d69afc8bff57641ac7a4cee350209a08301553854873da153ccf056427a2415e3ce72972afb5883393806ec2388169b513674c0935f67ec79c89dfc4bdc6f9cf877a10f
+Remainder = 1
+A = 76b8600fb3892931ff086f6de0029a28d6c78416d8b723fd7dc8e423e84213ec102409643515f33d35656a2cbaa6c689fd82d7ad23f67107dce66bc25ad35f917feaec8358f499dc6a0413410602aa70a90e7b42a799e0ac84f4482bc79ce52e55f6b10672700dd847102d36a26ce98126becfd8f3913bf897b8df39f0ef421f
+B = 2
+
+Quotient = 4f54d7e1ac8816945de169e9a2c497ff240e313c2b7d58612c8175e277f032cd4ee5dd640605028c59395a1eb4aa00772a8187a0568b93919aa5b95b0462e5bd31c1e507170039306e1b2f4f75f63ab0a3add0eb01217df61a74765fc37e941dedf10fe142ae317573a4f0c8ce408c213749a12f56add5d100d0973b019350a1
+Remainder = 0
+A = edfe87a5059843bd19a43dbce84dc7fd6c2a93b482780923858461a767d09867ecb1982c120f07a50bac0e5c1dfe01657f8496e103a2bab4cff12c110d28b1379545af154500ab914a518dee61e2b011eb0972c1036479e24f5d631f4a7bbc59c9d32fa3c80a94605aeed25a6ac1a463a5dce38e040981730271c5b104b9f1e3
+B = 3
+
+Quotient = 2922aed641a12010a3099f3c03f708962e2791dd860e65440acf3b982a4041804dcbedf45deefdae5130df96902056f8b2942069fc17bfb29f46a096a36e842ecb30d0800da13b6572c5b3a095038baa3107ca28094063571b517f7cda3659b63099c57a40d7dd2893b92d60b1fe2fb4594fc3a19b7d7957921437556db0e353
+Remainder = 0
+A = a48abb59068480428c267cf00fdc2258b89e4776183995102b3cee60a9010601372fb7d177bbf6b944c37e5a40815be2ca5081a7f05efeca7d1a825a8dba10bb2cc342003684ed95cb16ce82540e2ea8c41f28a025018d5c6d45fdf368d966d8c26715e9035f74a24ee4b582c7f8bed1653f0e866df5e55e4850dd55b6c38d4c
+B = 4
+
+Quotient = 216236f9c82fe6f1c021853a21fde3e21e6de355cf193f16b403edf59a6a6ebeedb266d4c7a6683f5f6a434c7129f582d2a5a852269d66d2eda45a1e2f25286c665f6641ff8b55913603064cc7a157f755e515a426873e7bc6b9d699d1f316759c4505a67b7a025598f9d1af6ebff2ed0fe393db829f768178c1080ea004e4f4
+Remainder = 4
+A = a6eb12e0e8ef82b8c0a79a22a9f5736a982570ad0b7e3b718413a5cc041429baa47c0227e640093cdd13507e35d1cb8e1d3c499ac113021ea435c296ebb9ca1dffdcff49fdb8abd60e0f1f7fe626b7d4ad796c34c0a4386ae1a1310119bf704c0d591c4069620babfce1186d29bfbea14f71e3498d1d50875bc52849201878c8
+B = 5
+
+Quotient = b9fbd48d54b9b70374425aabe16d6a8a819944a43185c2fd07073e20358510ac3de13cff33fe6220ba952d88b2e0f3f7eddb8daf27462b476b5e127e72ea60fd56cc54bf14d2d92765d5d21652d8e16aad4423cd9789515d59aaa02d42d3e957dde50ed1c9a69e2295144a643a8104660ccaafba250854e7f28a686935738
+Remainder = b6d
+A = 8ec1cca67b888cfa26bcee98ee887c47507a253008032c2b37e50f2fb914a34c357f6351e368c2521f3781736d4dab43ce130640f1a55c3851e9b5320f34e772751fd70cab7bd7aebdaa9fc22297790661fecd7b4ed0e6f4275377f2bdcba89bf1d251e0074864618b6e1319eee807e054d193e2616ce52c09ab3d24c187332d
+B = c48
+
+Quotient = 5157f1bb35866dcaa3abb4abb73580d43d03536c3c7960aa95910db60f4d1ffada96c7d89dfcb290bd8c5bb154872e2dd6e50602fafb435193575a4cf253e4d22dbecf11f8f97408dcc83d6e591b1d5daa59825ed8cb08cf562fc50d62cd666b9720055dc11cd42278258e5bd8021aada0b39a340b6c5585bb6c9c84a9ff8
+Remainder = 3d2
+A = 469e999cc737f4d12c97d19a13ce331841f8232cb780602c18592e274ec8b503884566ffcf28a206288f1a9ab3a25bd74bd054781664a331922a96254d6155677836e7455a6690fcb1acd7550cdbca3e9124356ed7b644660092f8d2df06d22ae7f38ca8a4e7472aecce9ad73c47d3a93cc3ec9faeeacd3f59f70ae22c9614b2
+B = de4
+
+Quotient = 3566586b9f864dac5ed132d95d4ac6d1fd5ef6a2c67fee39ece89d615b4c681284b4dd5e27b90c6270b85b150fa2a63440e470b0f937b0eb83432be03eaeb37a0927a9c76b07fe40e3509c93a7b660b77ebbec9bca235d387a9a80a6432c77ddd8190c0ae8ea1d72331d5f4985467755b27573bf23109a01c02975e07daf3
+Remainder = 2a2f
+A = 9d68d0643f1d44b63aff6a83fca08c52bf800dc59260db9b7ff930eb1bc01a47966fa509abd7da21ad856f7cf536d32dc7c962afaca1c9e43bcde135e4c5b9cd9b3c8ad775e06fda06117f8cc03ffad8e5f4b456baba7eaa9c67af7a19c2f4d65120d51fa8d31d0cc1ec7502187cd784fd2d78514cbccff969123718de7cb30d
+B = 2f2a
+
+Quotient = e36f2fbcfe134fdf3137539006d6d9c03b8774883211f759b0258bb09585440d6ff440e799ffc434a2fc529773a455db9abf72d8c55903d9ae5abd5b2b5e9ccf23c015882cab8565c654532d9407a188a40d0cb026fb3bfda428d4bdfc14bec72b5cbd59540c42598f1371e9e61a86e6b4c957ea331baca764b771212495
+Remainder = 6eeb
+A = b669c646d1bbd7389fc642da6d2c440788fec53bd8409ee604222d08b1fc31b3d301e42a8168be0ac394e5f20eb51708b11e7b09d25043f19032310d6649d33eb6c9688506ebd56ebfd0d3f277511ad3caaba3642c53d27e8fb0eb991c75577f584c52b1ec44111b3a9bf5863c18d8a07b91d8ae0bdbbb3b05ec8d11380a9c3a
+B = cd53
+
+Quotient = a891f8a42093cd86d76cb11cf734a65dccd5b4d350328a7d2f2be76e2edb6b7dcf4c5e1915c65764c77ae73fd6e42eb8451253507e16f2e25ef80e5d1f27ea18dc976a9b12147ecb643b2ab060163307df818127b2e40dcea95a109d7841edc9288190587ac48ba9687ccd0d014d531bcf66ec401bbcbed777325fd1060c
+Remainder = 6e66
+A = 9077614b809f4b22707cf965a7e79217e13ca2011cf9e069babe2b4d908e318608f91da095864403b168d750d904fbfe11c9ed80ba9f60d57a8dac2754647002a0848fefb7a5aa8e04fd28dcb9c8e669de4ef794eab2abc93d68dcbf4400d86de603d199a3ee93050638fca7063ea99a9465dfb60d0568b99dfa1ed79da41522
+B = db65
+
+Quotient = 1b16f2e2ef7709fe285ede17beb7d9932caae2dd5fa0eebb541770ca1d53da4428820986cb7e79026eb8bc261eceb200b7696a4b90f675ea9af8389c60dde4d564c8adeba6b117edd05469d285670c0bc78afbc3ad047828cdc611fbcab403c0cb79665d6285b43fa04b77f0309bc7f74136778f8ec16899df040db34f4751
+Remainder = 68
+A = e91e7c26e2b562fe2568613656381d5581628e4705ede6660ca5b79b4a609748889707faf9295b57eecfbb1c0b1cb5cc2a5825b84878e8b9e3960f29b59580385a4af0aae375f8eb7fc66aa6a1fdc4a95e29048ce1e5760722c77cc1c95b1c4c16fdb3e59ed4961f8869711ff24c91ccbe2fb6e0617a5f242227e1e60b3ab673
+B = 89b
+
+Quotient = 37370826964cbd65a48598e73b519db77df6f520bcead8c0446f1288ac189403adb65603b2a68ab3cc232b667232f2e206b5bee0fd48fea8b3ff515f452b5ef0cac591b6ac8c8c509c59c6d3d4e3fa03e22578ff71f1c72ddad9d637ae0497ef0e2a4b261a72cb784f8283eb7e82b6a05aff0a2f61da4780e4e7cfcc4807
+Remainder = 3a29
+A = 16ad5614f9129c7952c5ee8057d8d12a70780144e616e3ed571b2e38a9ce482a52c436eb9ccb6e4f400321bf1f3ef4c8dc897cd91f868eb7018d084784c4840a1d078c8c6a75e950cb76cf2cd81b719ac04d2be5c9a830b1d1361f7ef6345af66a6d56c53234cd98f587b6762401674973df670addcc4a05ec0344d402453a25
+B = 6924
+
+Quotient = 9bb00032a27651eac898b8a567e19ed6448669c8514b5659c4b1103069d9289c6c00b38b44160e0efb2c635b7a64c8296c1c1b5c2cdb285b749e614eb9247c6defa06f8dac077b1e1c26059847de56a1a5ddf7fb1254662624f2ffe6edc48f3b318ffdc7ba2a81ef2d963b934120f58afba2b107a215b58f324e2d923f75
+Remainder = c03
+A = 74524695d4dc11023ff202ed2d165551ace0c126f7a51ebb3ff21ecd7c058cd4a6bda2254c55ce6ef76fd11807f92e80dad31bfd254f9a2e1ca89949f65a1fab8f6a4978c488f2dfa61df46c1faa418ff45250d82958e8f5fdd9426c44a3bcd7c4eeca276abae466787a5ff0ec482514e03434ee68fce24fc620e31265c3718c
+B = bf45
+
+Quotient = cecbbc189fb1d44c5511f742b63207bcba9c78d09342cdcd12a1b1bc3a95466e7fdd8c59329a9b18f7c793c43f08d52339a8202dfa3a9fa86a2426bf5a94e006849b45cbe9a5dd74ca43e2acdf1051be23359624e8f146b203864d03651d98165b783398a59b446314c9b01f79b1139c30df348b14ffd25b22d9d90866b
+Remainder = b265
+A = c3721776b9b5fea8608aa9d381d80ac603d27043089dac276832e7cde8d222ffe142f06c314e94c3b9f6148d029f260879b700e1d435b5f318c8c8caebe92236c9060c183783edec2845e6d4e816197196a0de3644544093b04ac6fb4c69d7446954fbabadcc5dc3309e9a3fcf70368ba7448455cec9c3dc78512a19ebb04f6
+B = f1f3
+
+Quotient = 4090a2c78cf8711388347149926610d624543765c9667567ad86eef9f9777f53c0cc0f9a989d9195a5e0da875c03e5c74614f95b8752f9ab89fa61c264b8b5d3e02b043fd539d36dbc6782f45a555d1f36751603d5c3423c7f27b3b5dcb91ddc81bf1563dd3abb0970de6109d76da1f4f9d5208ade2b131fc407c5b169c
+Remainder = 2a87
+A = 129d32cde3c648298f8e8e8123f2e8ee9cad3f909a5647ed09e91cb99549d177575f54a7a3ebbd4ed2b89940722927a8b9565ffbc13d8df6d2616d5b1925b87bbb6aa6d39f2b11d26d071fa30e63083ed5a5357ecf0ab1028cf0a43178486679e86fe4dcb071c49832c83c9de4599d672e5ecfc7c9190f1d7275f5a0abed80f
+B = 49ce
+
+Quotient = 43340591e68e228fb03e44a5f2046afe41a3d7ca99ea9ff1a445d75f95f2ff7f55fb914791613b5db7369121d416a5f92f834b0b5e9280b49a9e66be4c682019881e6e8883d7a923d2a5d309b9d265b01d6b8a4ee07f7552934f2de002cf961fd93f33641aaaccc7c367fb6798436eecc9bb22357087a9c482131e1065eb
+Remainder = 6332
+A = 42e75e3b8c23287044593d9fa4bc5df437a0f8e876d3105334a677b5ecebf653e8bd7e55dbbf6876005196e44980bc23df491949c59aa199cc9e0a111b58f954eaff2bd270214726e5c98de502ba71b42089fba51e8763f0c11f278faf4c61589ceb674d7c7c61f62f8d18ccd619c20243a508c26b934f06ddeec0421b372326
+B = fedc
+
+Quotient = 688c7120765f8ef7363f7ae1bb65bc568b16e32c59762f59f34a57f08839d19019313dfcc9e96d7415766bc0aa032b19ecea72c249bffa0538bb1ac06401657df2fbea5c46b18d8a79cee4029e5972d8361fb7e6c2c537673aecd727dbc758a3bca1a001765a216e9985eb7eea67ae979f3803f14587507ba0f8fa29957
+Remainder = 9970
+A = 688c0894053f1897a74844a2408400f0cec058157649d5e3c3f064a63049495647a124cb8beca38aa802564a3e428116c1d085d7d6fdb0453eb5e2054941017c8d7df7605c5546d8ec446a33ba56d47ec34781c70ade74a203859c3b049f7cdc63fde35fd658ab14781751f8fee8c42ff0a064b941960af4507d59309b50019
+B = ffff
+
# ModMul tests.
#
@@ -11065,204 +11150,280 @@ M = d3d3f95d50570351528a76ab1e806bae1968bd420899bdb3d87c823fac439a4354c31f6c888c
# GCD tests.
#
-# These test vectors satisfy gcd(A, B) = GCD.
+# These test vectors satisfy gcd(A, B) = GCD and lcm(A, B) = LCM.
GCD = 0
A = 0
B = 0
+# Just to appease the syntax-checker.
+LCM = 0
GCD = 1
A = 92ff140ac8a659b31dd904161f9213706a08a817ae845e522c3af0c9096699e059b47c8c2f16434b1c5766ebb384b79190f2b2a62c2378f45e116890e7bb407a
B = 2f532c9e5902b0d68cd2ed69b2083bc226e8b04c549212c425a5287bb171c6a47fcb926c70cc0d34b8d6201c617aee66af865d31fdc8a2eeb986c19da8bb0897
+LCM = 1b2c97003e520b0bdd59d8c35a180b4aa36bce14211590435b990ad8f4c034ce3c77899581cb4ee1a022874203459b6d53859ab1d99ff755efa253fc0e5d8487bb000c13c566e8937f0fe90b95b68bc278610d4f232770b08d1f31bee55a03da47f2d0ebb9e7861c4f16cc22168b68593e9efcde00f54104b4c3e1a0b294d7f6
GCD = a
A = faaffa431343074f5c5d6f5788500d7bc68b86eb37edf166f699b4d75b76dae2cb7c8f6eccae8f18f6d510ef72f0b9633d5740c0bebb934d3be796bd9a53808e
B = 2f48ec5aa5511283c2935b15725d30f62244185573203b48c7eb135b2e6db5c115c9446ac78b020574665b06a75eb287e0dbeb5da7c193294699b4c2129d2ac4
+LCM = 4a15f305e9622aa19bd8f39e968bfc16d527a47f7a5219d7b02c242c77ef8b608a4a6141f643ca97cedf07c0f1f3e8879d2568b056718aa15c0756899a08ccbe0a658bae67face96fa110edb91757bfa4828e8ff7c5d71b204f36238b12dd26f17be8ba9771f7068d63e41d423671f898f054b1187605754bc5546f2b02c5ac
GCD = 16
A = cf0b21bde98b41b479ac8071086687a6707e9efaacd4e5299668ce1be8b13290f27fd32ae68df87c292e8583a09d73ec8e8a04a65a487380dcd7dacca3b6e692
B = 3be3f563f81d5ad5c1211db7eff430aa345e830ce07b4bde7d4d32dba3ac618d2034351e5435fd6c7f077971fb4a1e83a7396a74fdff7fce1267112851db2582
+LCM = 233a2188de2c017235024b182286f17562b2ee5ab9fdfe4efa2f61c4ff99fa44e1ead5bf6cde05bd7502ce78373c83e3f9dbab0c9bb8620a87c2640bce5d12c685af656df789bb3d0ba1edbaa98cf4f0166d422ab17aa6706f8132264d45b72827d6671a00a9186e723379e3a3bb7902d08865f357c74100059f83800241976
GCD = 1
A = dd7b7597d7c1eb399b1cea9b3042c14bd6022d31b1d2642a8f82fc32de6eadaf012fbbf349eaec4922a8468740ca73c6090833d6a69a380ed947b39c2f9b0b76
B = 8e0dc8654e70eec55496038a8d3fff3c2086bc6dbfc0e2dbdf5bd7de03c5aef01a3982556ac3fc34fd5f13368be6cdc252c82367b7462e210f940f847d382dd9
+LCM = 7ae667df4bd4dd35bbec28719a9f1b5e1f396a9ab386c086742a6ab3014a3386d39f35b50624d0c5b4e6b206c2635c7de5ea69e2faa85dd616a7e36622962a07632839857aa49332942feccff2aee1c962e2f4e8ccfd738a5da5bf528b4c5a2440409350f5a17a39d234403e8482ccf838e0d2758ccfb8018198a51dbb407506
GCD = 1
A = 0
B = 1
+LCM = 0
GCD = 1
A = 1
B = 0
+LCM = 0
GCD = 1
A = 1
B = 1
+LCM = 1
GCD = 2b2
A = dfccaa3549c1b59ab3e114fe87dc5d187719abad58c51724e972741eb895ab79a49f385f61d531ec5c88dbb505ae375093fa848165f71a5ed65e7832a42ade191a
B = fa58a81f43088da45e659fc1117d0f1cd015aa096c8e5377cf1832191baf7cc28b5c24998b93b64f8900a0973faedb9babaaf1854345f011739da8f1175d9684c
+LCM = 5132f7ab7a982b9dc55114bd96800b7637f9742cf8a7a00a0d69d5e4574fc85792c89a1c52bcfc74b9d7f3f6164819466c46b2d622e280ced7ad1211604084a15dc1fd1951a05c8ce37122c0ec15891d818a70d3763670ea3195098de9b1ca50ea89893a9753fb9ea801541058f44801f7f50967124abfc864a2b01c41f94193c
GCD = 8e
A = 248d96a8a4cab0a1b194e08c1146868b094597cadbc35531f0ed2d77cba9f15cb5cc7c10e64ce054bf93396d25259d750b3de3aba65073db1fd2b852a6454ac1a
B = 4c7bad8e1844901fd6a2ce2edc82e698d28ec95d6672ca148d85b49ecc78dd0a8b870e202244210bc98592b99ff6abbd20630f9eee7d46b15ccfae8d08b86799de
+LCM = 13b01f9d9c6c13e90c97e3d95bbce5a835c631b3de3bd4ff5df13ad850f5223dbdf71c53912275d0397df9335ef3a3ba8e4684c6b25962bb7b18bc74144cb5edf0196f79863a7ff032619a71646a92281f7baace7f223d254cb4d05ec19bf8d4c8ce4455a9d770daec89c0d3cf338cbdae39cf982b3c4568f5c9def4e1133d28a
GCD = 3e55
A = 2fa97382f46676b7a4cc2b8153f17b58792d24660e187d33ce55c81cc193ccb6e1e2b89feea1d5fd8faa36e13bf947fb48635e450a4d1488d0978324194a1f43c6
B = ab08ad074139963bc18e5d87ba68db64ca6f4c279616c64039b02c55f2375b3bc04114e8e05e1ba92fb6470768f61d123845aea36774c18612736a220934561faf
+LCM = 82c7c377ecda2cb9228604cd287df5eff94edd4a539c3eb3b3fdd4b4a79d2f4eaf2b22f8286272d3dad2e370cfcd9ea4d93ebb3f049c52b8fa23b68a5bf79af989822e2cfb978f68c6a5058f47319dffcb455b089b06ae6db9e5c8a2b6e951d6e118bd2b4cd08b6e5733476a446a57387d940d1289ec00e24315821ed3a5daf2
GCD = a7a
A = 923706dfed67834a1e7e6c8e8e9f93bfbc0b43ca1f324886cf1f1380fb9b77109275d4b50af1b7689802fe9b3623ac46c7ba0e17e908c20278127b07a5c12d86ec
B = 64473e878a29021fac1c1ce34a63eae1f4f83ee6851333b67213278b9a4a16f005cba0e8cdb410035bb580062f0e486c1a3a01f4a4edf782495f1dc3ebfa837d86
+LCM = 57785ca45b8873032f1709331436995525eed815c55140582ce57fd852116835deac7ca9d95ce9f280e246ea4d4f1b7140ab7e0dd6dc869de87f1b27372098b155ad0a1828fd387dff514acc92eae708609285edaab900583a786caf95153f71e6e6092c8c5ee727346567e6f58d60a5e01c2fa8ebcf86da9ea46876ecc58e914
GCD = 42
A = 0
B = 42
+LCM = 0
GCD = 42
A = 42
B = 0
+LCM = 0
GCD = 42
A = 42
B = 42
+LCM = 42
GCD = f60d
A = ef7886c3391407529d5cf2e75ed53e5c3f74439ad2e2dc48a79bc1a5322789b4ced2914b97f8ff4b9910d212243b54001eb8b375365b9a87bd022dd3772c78a9fd63
B = d1d3ec32fa3103911830d4ec9f629c5f75af7039e307e05bc2977d01446cd2cbeeb8a8435b2170cf4d9197d83948c7b8999d901fe47d3ce7e4d30dc1b2de8af0c6e4
+LCM = cc376ed2dc362c38a45a719b2ed48201dab3e5506e3f1314e57af229dc7f3a6a0dad3d21cfb148c23a0bbb0092d667051aa0b35cff5b5cc61a7c52dec4ed72f6783edf181b3bf0500b79f87bb95abc66e4055f259791e4e5eb897d82de0e128ecf8a091119475351d65b7f320272db190898a02d33f45f03e27c36cb1c45208037dc
GCD = 9370
A = 1ee02fb1c02100d1937f9749f628c65384ff822e638fdb0f42e27b10ee36e380564d6e861fcad0518f4da0f8636c1b9f5124c0bc2beb3ca891004a14cd7b118ddfe0
B = 67432fd1482d19c4a1c2a4997eab5dbf9c5421977d1de60b739af94c41a5ad384cd339ebfaa43e5ad6441d5b9aaed5a9f7485025f4b4d5014e1e406d5bd838a44e50
+LCM = 159ff177bdb0ffbd09e2aa7d86de266c5de910c12a48cbe61f6fa446f63a2151194777555cd59903d24cb30965973571fb1f89c26f2b760526f73ded7ee8a34ebcecd1a3374a7559bcdb9ac6e78be17a62b830d6bb3982afdf10cf83d61fd0d588eab17d6abef8e6a7a5763fcb766d9a4d86adf5bb904f2dd6b528b9faec603987a0
GCD = c5f
A = 5a3a2088b5c759420ed0fb9c4c7685da3725b659c132a710ef01e79435e63d009d2931ea0a9ed9432f3d6b8851730c323efb9db686486614332c6e6ba54d597cf98
B = 1b1eb33b006a98178bb35bbcf09c5bebd92d9ace79fa34c1567efa8d6cf6361547807cd3f8e7b8cd3ddb6209dccbae4b4c16c8c1ec19741a3a57f61571882b7aed7
+LCM = c5cbbbe9532d30d2a7dd7c1c8a6e69fd4fa4828a844d6afb44f3747fef584f7f1f3b835b006f8747d84f7699e88f6267b634e7aef78d6c7584829537d79514eec7d11219721f91015f5cefdc296261d85dba388729438991a8027de4827cd9eb575622e2912b28c9ce26d441e97880d18db025812cef5de01adeaec1322a9c9858
GCD = e052
A = 67429f79b2ec3847cfc7e662880ab1d94acdf04284260fcfffd67c2862d59704ed45bcc53700c88a5eea023bc09029e9fd114fc94c227fd47a1faa1a5ef117b09bd2
B = 39faa7cbdeb78f9028c1d50ab34fbe6924c83a1262596f6b85865d4e19cc258b3c3af1ee2898e39e5bee5839e92eac6753bbbb0253bd576d1839a59748b778846a86
+LCM = 1ab071fb733ef142e94def10b26d69982128561669e58b20b80d39cf7c2759d26b4a65d73b7f940c6e8fc417180ef62d7e52ac24678137bd927cd8d004ad52b02affe176a1ecde903dbc26dcc705678f76dd8cd874c0c3fe737474309767507bbe70dd7fb671bbb3694cedf0dcdaa0c716250ddd6dfec525261572fa3e1387f7b906
GCD = 3523
A = 0
B = 3523
+LCM = 0
GCD = 3523
A = 3523
B = 0
+LCM = 0
GCD = 3523
A = 3523
B = 3523
+LCM = 3523
GCD = f035a941
A = 16cd5745464dfc426726359312398f3c4486ed8aaeea6386a67598b10f744f336c89cdafcb18e643d55c3a62f4ab2c658a0d19ea3967ea1af3aee22e11f12c6df6e886f7
B = 74df09f309541d26b4b39e0c01152b8ad05ad2dfe9dd2b6706240e9d9f0c530bfb9e4b1cad3d4a94342aab309e66dd42d9df01b47a45173b507e41826f24eb1e8bcc4459
+LCM = b181771d0e9d6b36fdfcbf01d349c7de6b7e305e1485ea2aa32938aa919a3eee9811e1c3c649068a7572f5d251b424308da31400d81ac4078463f9f71d7efd2e681f92b13a6ab3ca5c9063032dcbdf3d3a9940ce65e54786463bbc06544e1280f25bc7579d264f6f1590cf09d1badbf542ce435a14ab04d25d88ddbac7d22e8cae1c91f
GCD = 33ad1b8f
A = 1af010429a74e1b612c2fc4d7127436f2a5dafda99015ad15385783bd3af8d81798a57d85038bcf09a2a9e99df713b4d6fc1e3926910fbbf1f006133cb27dc5ebb9cca85
B = 92a4f45a90965a4ef454f1cdd883d20f0f3be34d43588b5914677c39d577a052d1b25a522be1a656860a540970f99cbc8a3adf3e2139770f664b4b7b9379e13daf7d26c
+LCM = 4c715520ed920718c3b2f62821bc75e3ff9fd184f76c60faf2906ef68d28cd540d3d6c071fa8704edd519709c3b09dfaee12cb02ab01ad0f3af4f5923d5705ce6d18bcab705a97e21896bb5dd8acb36ee8ec98c254a4ddc744297827a33c241f09016a5f109248c83dd41e4cea73ce3eabb28d76678b7e15545b96d22da83c111b6b624
GCD = dc0429aa
A = ccb423cfb78d7150201a97114b6644e8e0bbbb33cadb0ef5da5d3c521a244ec96e6d1538c64c10c85b2089bdd702d74c505adce9235aa4195068c9077217c0d431de7f96
B = 710786f3d9022fc3acbf47ac901f62debcfda684a39234644bac630ab2d211111df71c0844b02c969fc5b4c5a15b785c96efd1e403514235dc9356f7faf75a0888de5e5a
+LCM = 6929af911850c55450e2f2c4c9a72adf284fe271cf26e41c66e1a2ee19e30d928ae824f13d4e2a6d7bb12d10411573e04011725d3b6089c28d87738749107d990162b485805f5eedc8f788345bcbb5963641f73c303b2d92f80529902d3c2d7899623958499c8a9133aae49a616c96a2c5482a37947f23af18c3247203ac2d0e760340e6
GCD = 743166058
A = 16cd476e8031d4624716238a3f85badd97f274cdfd9d53e0bd74de2a6c46d1827cc83057f3889588b6b7ca0640e7d743ed4a6eaf6f9b8df130011ecc72f56ef0af79680
B = 86eba1fc8d761f22e0f596a03fcb6fe53ad15a03f5b4e37999f60b20966f78ba3280f02d3853f9ace40438ccfaf8faed7ace2f2bf089b2cdd4713f3f293bf602666c39f8
+LCM = 1a7a1b38727324d6ba0290f259b8e2b89c339b2445cada38a5a00ded1468ab069f40678ce76f7f78c7c6f97783cc8a49ef7e2a0c73abbac3abc66d1ce99566ce7f874a8949ca3442051e71967695dc65361184748c1908e1b587dc02ed899a524b34eb30b6f8db302432cfa1a8fbf2c46591e0ab3db7fd32c01b1f86c39832ee9f0c80
GCD = 6612ba2c
A = 0
B = 6612ba2c
+LCM = 0
GCD = 6612ba2c
A = 6612ba2c
B = 0
+LCM = 0
GCD = 6612ba2c
A = 6612ba2c
B = 6612ba2c
+LCM = 6612ba2c
GCD = 2272525aa08ccb20
A = 11b9e23001e7446f6483fc9977140d91c3d82568dabb1f043a5620544fc3dda233b51009274cdb004fdff3f5c4267d34181d543d913553b6bdb11ce2a9392365fec8f9a3797e1200
B = 11295529342bfb795f0611d03afb873c70bd16322b2cf9483f357f723b5b19f796a6206cf3ae3982daaeafcd9a68f0ce3355a7eba3fe4e743683709a2dd4b2ff46158bd99ff4d5a0
+LCM = 8d4cbf00d02f6adbaa70484bcd42ea932000843dcb667c69b75142426255f79b6c3b6bf22572597100c06c3277e40bf60c14c1f4a6822d86167812038cf1eefec2b0b19981ad99ad3125ff4a455a4a8344cbc609e1b3a173533db432bd717c72be25e05ed488d3970e7ed17a46353c5e0d91c8428d2fec7a93210759589df042cab028f545e3a00
GCD = 3480bf145713d56f9
A = 8cf8ef1d4f216c6bcec673208fd93b7561b0eb8303af57113edc5c6ff4e1eeae9ddc3112b943d947653ba2179b7f63505465126d88ad0a0a15b682f5c89aa4a2a51c768cd9fdeaa9
B = a6fd114023e7d79017c552a9051ca827f3ffa9f31e2ee9d78f8408967064fcdc9466e95cc8fac9a4fa88248987caf7cf57af58400d27abd60d9b79d2fe03fad76b879eceb504d7f
+LCM = 1c05eee73a4f0db210a9007f94a5af88c1cdd2cba456061fd41de1e746d836fa4e0e972812842e0f44f10a61505f5d55760c48ba0d06af78bb6bde7da8b0080b29f82b1161e9c0b5458e05ac090b00f4d78b1cc10cf065124ba610e3acab092a36fe408525e21c0ddc7c9696ed4e48bd2f70423deecfe62cecc865c6088f265da0e5961d3f3a84f
GCD = 917e74ae941fcaae
A = 652f8a92d96cbf0a309629011d0fbaceb1266bc2e8243d9e494eead4cf7100c661b537a8bea93dec88cfc68597d88a976c125c3b4de19aba38d4ea9578202e59848d42652518348a
B = 32e07b71979d57e8344e97c39680a61e07d692d824ae26b682156890792d8a766ee29a4968f461aaced5bf049044fba2f4120b1c1f05985676f975d4582e9e82750d73c532cd07b2
+LCM = 23620c7b897dc26c7717e32f3517ac70bf09fbe08f7255ab010cf4cf946f4e96304c425043452c5d5a0e841d3a3cfd9c2d84d9256f3b5974fe3ebfa9255fe20a710d3e6511606c0d85970381101c7f4986d65ad6a73a71507f146b11f903043cfa805cc0b14d4f3072da98bf22282f7762040406c02d5b3ef9e7587f63bab8b29c61d8e30911aa96
GCD = 2b9adc82005b2697
A = 19764a84f46045ef1bca571d3cbf49b4545998e64d2e564cc343a53bc7a0bcfbe0baa5383f2b346e224eb9ce1137d9a4f79e8e19f946a493ff08c9b423574d56cbe053155177c37
B = 1bbd489ad2ab825885cdac571a95ab4924e7446ce06c0f77cf29666a1e20ed5d9bc65e4102e11131d824acad1592075e13024e11f12f8210d86ab52aa60deb250b3930aabd960e5a
+LCM = 1032a0c5fffc0425e6478185db0e5985c645dd929c7ebfeb5c1ee12ee3d7b842cfab8c9aa7ff3131ac41d4988fb928c0073103cea6bb2cc39808f1b0ad79a6d080eac5a0fc6e3853d43f903729549e03dba0a4405500e0096b9c8e00510c1852982baec441ed94efb80a78ed28ed526d055ad34751b831b8749b7c19728bf229357cc5e17eb8e1a
GCD = 8d9d4f30773c4edf
A = 0
B = 8d9d4f30773c4edf
+LCM = 0
GCD = 8d9d4f30773c4edf
A = 8d9d4f30773c4edf
B = 0
+LCM = 0
GCD = 8d9d4f30773c4edf
A = 8d9d4f30773c4edf
B = 8d9d4f30773c4edf
+LCM = 8d9d4f30773c4edf
GCD = 6ebd8eafb9a957a6c3d3d5016be604f9624b0debf04d19cdabccf3612bbd59e00
A = 34dc66a0ffd5b8b5e0ffc858dfc4655753e59247c4f82a4d2543b1f7bb7be0e24d2bbf27bb0b2b7e56ee22b29bbde7baf0d7bfb96331e27ba029de9ffdff7bdb7dc4da836d0e58a0829367ec84ea256833fd4fe1456ad4dd920557a345e12000
B = 1f3406a20e20ebf96ccb765f898889a19b7636608fd7dc7c212607b641399543f71111d60e42989de01eaa6ff19a86ea8fbde1a3d368c0d86dc899e8e250fc764090f337958ca493119cbb4ad70cbfae7097d06d4f90ec62fbdd3f0a4496e600
+LCM = ee502c50e3667946e9089d0a9a0382e7fd0b75a17db23b56a0eec997a112c4dbd56d188808f76fe90451e5605550c9559ef14a95014c6eb97e9c1c659b98515c41470142843de60f72fb4c235faa55b0a97d943221003d44e2c28928f0b84bf071256254897ed31a7fd8d174fc962bc1311f67900ac3abcad83a28e259812f1ee229511ab1d82d41f5add34693ba7519babd52eb4ec9de31581f5f2e40a000
GCD = ef7399b217fc6a62b90461e58a44b22e5280d480b148ec4e3b4d106583f8e428
A = 7025e2fe5f00aec73d90f5ad80d99ca873f71997d58e59937423a5e6ddeb5e1925ed2fd2c36a5a9fc560c9023d6332c5d8a4b333d3315ed419d60b2f98ccf28bbf5bf539284fd070d2690aeaac747a3d6384ee6450903a64c3017de33c969c98
B = df0ac41dbabce1deeb0bceb1b65b1079850052ecf6534d0cff84a5a7fb5e63baee028d240f4419925154b96eaa69e8fbb1aae5102db7916234f290aa60c5d7e69406f02aeea9fe9384afbff7d878c9ac87cd31f7c35dff243b1441e09baff478
+LCM = 687669343f5208a6b2bb2e2efcac41ec467a438fde288cc5ef7157d130139ba65db9eb53e86a30c870bd769c0e0ab15a50f656cd9626621ae68d85eaff491b98da3ea5812062e4145af11ea5e1da457084911961ef2cd2ac45715f885ba94b4082aa76ffd1f32461f47c845b229d350bf36514c5ce3a7c782418746be342eca2721346ade73a59475f178c4f2448e1326110f5d26a0fef1a7a0c9288489e4dc8
GCD = 84b917557acf24dff70cb282a07fc52548b6fbbe96ca8c46d0397c8e44d30573
A = 81dbb771713342b33912b03f08649fb2506874b96125a1ac712bc94bfd09b679db7327a824f0a5837046f58af3a8365c89e06ff4d48784f60086a99816e0065a5f6f0f49066b0ff4c972a6b837b63373ca4bb04dcc21e5effb6dfe38271cb0fa
B = 1da91553c0a2217442f1c502a437bb14d8c385aa595db47b23a97b53927b4493dd19f1bc8baf145bc10052394243089a7b88d19b6f106e64a5ab34acad94538ab504d1c8ebf22ac42048bbd1d4b0294a2e12c09fe2a3bd92756ba7578cb34b39
+LCM = 1d0530f8142754d1ee0249b0c3968d0ae7570e37dadbe4824ab966d655abf04cd6de5eb700eba89d8352dec3ae51f2a10267c32fbd39b788c7c5047fe69da3d7ad505435a6212f44899ba7e983bb780f62bcdee6f94b7dba8af7070a4cc008f351ae8be4579bc4a2e5c659ce000ad9c8cdc83723b32c96aeb0f5f4127f6347353d05525f559a8543cd389ad0af6f9d08a75b8c0b32419c097e6efe8746aee92e
GCD = 66091477ea3b37f115038095814605896e845b20259a772f09405a8818f644aa
A = cedac27069a68edfd49bd5a859173c8e318ba8be65673d9d2ba13c717568754ed9cbc10bb6c32da3b7238cff8c1352d6325668fd21b4e82620c2e75ee0c4b1aff6fb1e9b948bbdb1af83cecdf356299b50543b72f801b6a58444b176e4369e0
B = 5f64ca1ba481f42c4c9cf1ffa0e515b52aa9d69ceb97c4a2897f2e9fa87f72bae56ee6c5227f354304994c6a5cc742d9f09b2c058521975f69ca5835bce898cf22b28457cd7e28870df14e663bb46c9be8f6662f4ff34d5c4ae17a888eba504e
+LCM = c163cb28642e19a40aa77887c63180c2c49fc10cda98f6f929c8131752ea30b5283a814a81681b69b9d1762e6c1a9db85f480bc17f998d235fd7e64c1caa70ef170c9e816d3e80f516b29f2c80cfb68bf208b4d5082ef078da4314b3f20c7d6c54b0aeb378096b029a7b61c0a4cd14aeddc01004c53915a4f692d2291752e5af46b23d7fa6dd61f2d56c6f4bf8e6119688abac8fd7aba80e846a7764bb3fca0
GCD = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
A = 0
B = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
+LCM = 0
GCD = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
A = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
B = 0
+LCM = 0
GCD = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
A = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
B = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
+LCM = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182
GCD = 120451d8307219aa0c96f328ad653ccd462e92423ca93ed8a3dde45bf5cb9b13cdaf9800e4d05dd71c4db6a129fb3280ee4ec96ec5297d881c1a8b5efccbd91fef21f5c5bf5fba42a4c8eaa358f620a074b7a17054527bdaa58d5acaa0dfdc48ecba1a10ebf4d57bb4215de406e6be13fed3fe493b1cd1e2d11a8d4ac03c47756
A = 3f8179a8e1f0b342475a855c3e1bae402dd41424cf24a0b4d2e263c8efb08bde7d92eae8607fb5e88b1378f0f1bd0733f229a35be6b1383a48d32749d5d6b32427d26323b7ab05bb5781289e96bfbc21971439319b15f6c0fe93fdb35d0b67ec41443c59a081dd3cef047ac797fccb45bece84c0bb0bb7e1797259526d8ec9cc63ba4d32cfc692ccd3d243cb2b53ac216312f3a8e8c0daa09d21b6150d697639a5e52059414a417c607be8ec0eee2e708219cadbaf37a369c4485b01ed87bbc2
B = 2c474e396a2dd9cd10b9d7313f69d3b4ca123e9fd853edd488339236d14c56453a1381958864a04d2624e81995dabcdd0ccf60db9917813f887de68da075d0ea4440001e18f470e43b38ee3440b49be651d709fbdef980e3e4149913f4ae2681124f54523f4881376ddb533b5219e804cc26f4c2e577be4e02613c4da80ba1215775b0a5178a965ad47bd2befb32493943ded1004ef66347b4983f8d1ba990d4a943505dfce6debcfb322842ed88106cd6dee9aa592ff0d2274bc727a6e1f14c
+LCM = 9c129cf649555bfd2d3d9c64dc6d6f022295e53bca5d2f218adaa66aa60eb4694429b7e83bf81b6df4459c5104023ab9a33f006ffcd8114507baa17e2ef6fe23ebdd4740f66879033da2041f2cb7ba517ad3526ffe75614ea9432c085f71b2d65a736bac7ba42b639e330b82733372083843dcb78b6a273ab20e0d4b7c8998a14048aa15bb20a0a0bd997917107274c89b4cec175fb98043d52e6c555bd9e0036566d052a6d4e7e276d1e8835e1f06e3ca46d47747ba586e95fb1a790d992834b7c3e136141eb8a434e6c12067246ac3c0a81c69e03b1ed28aa0b3173d6eff83d278c2f461a47a416f3f9a5dae3bb410fd18817bd4115e7f1e84b936cc02364
GCD = 95aa569a2c76854300d7660847dd20fe0b8c445fdbcaa98465cee61aee76ad6a438e75a8c573198570ffb62bc07ec3a2be0ae0a1f631670fa88d6f75f3161e8b9a4d44b6801ffc884c7f469c5ed1f27b1edecce9f2977f9e92d1a3b230492fea7e6f2af739dc158a7fbd29856cbedb57b4119e64b27ab09eb1c2df01507d6e7fd
A = 4c653b5bfec44e9be100c064dffe5d8cd59b0cf4cc56b03eabb4ef87cfda6506c9a756b811907fe9d8b783eb7a0b9e129773bf1da365ddb488d27b16fb983e89345d1ccdb4f06a67a11925c3f266373be5d7b0075189c6f3c2157e2da197058fe0a7bcc50adc34e99e254a29abbe2d5948d3157e1b0c3fca3d641760f7b9862843b63abef0b3d83fd486f4526b30382fda355575da30e9a106718a3921774c4d69f5311f8d737fe618f5236b4763fe1b2ee7f13184db67367d3903c535ff6d7b
B = 2dcca83c99a28e9fd2f84e78973699baf2f04fd454094730948b22477834a0064817b86e0835e6d7b26e5b0b1dcf4ad91a07ac0780d6522df1fcac758cf5db6c2a5623d7c0f1afefd5718f7b6de639867d07a9ec525991304e9355d1635104bea837f74758d6aa2aab4e4afbb606af1d98de7417505e4710cd0589bdff9a0bf38a857cc59a5f1781043e694fc2337fd84bdeb28b13a222bb09328a81ec409ad586e74236393d27398cc24d412135e34247c589149e134b97f4bd538ac9a3424b
+LCM = 1760c0b0066aa0695767099e87e9388729ea89b8e8c36bddcd04d257591e741613c07b0e69447c0a468c33a745084171e06523d987d8db40a1433bf435325e8a724a0876503b34495170ff3671d42117a2e4f3a75b1d9dd809a34fa0fb26fe50d84f80a9b02e40190e5efb927a5a61a03f13edbce2e666af6c3a2a9bcb84e47e3090008753ff27c4b8cf06480f471379a93f5230923623a83b286b71a555cd5e5347282f664ed90b14b2c4de84a70375e488211a7b3931119ef3bbe029b712389fe784818a0bf29d80733ce9cc940c547aa1eb3f06d492eb676bf37802283c82ce76156dfaab5c2d5107e08062681b5fa169f6eb68e1ab8bd9b2005e90bd4fd
GCD = 244b9b1290cf5b4ba2f810574c050651489f2d3a2b03e702b76ebfaf4e33de9bbe5da24c919e68d3a72eadd35982b3a89c6b18b38ff7082ac65263e52b6ec75a5717b971c98257b194c828bff0216a99536603b41a396ea2fb50f5ea7cf3edf10bb0d039123e78593ae9ffcbbba02e51e038533e83b6bc73c70551d6467f39809
A = 41a0b1310669500681cdf888836f6c556758750f562d743ac780dd4c0d161856380e44fdbb1f8a2786bf45be6b0e7f1cb2cd85f6b9e50acc72793d92383c7d7fb796fc74d32e8fac8225bdc19ae47546d9c9c75f5f06ca684f07daccaf89ccf2cddeb7ec255d530c7dd1e71daf44cafdc9d30fbcb1cbaefae3480585f79f4177e3834a5bc91845e2e8cd8aeb27f484e5e5b2c3c076dbb6c23e91303f0a0fdde83cd33a8ea6ed1549e727b4d766c1017c169710fd98e1585d60f66e121f9180b3
B = 251f5aeaa60b3959285f49540cdaf8e21451110bbddb9933bbbcaea3112f4eb45e435a3ba37c52d2ab79ce997a8f6c829b3aa561f2852924b8effb52396d09d2bf257ebb4fb56c7aa25648f69b06d2cd01e876c9f9c0679de9e6fffa79eb7e603723e5af7de46ee405a5a079229577b5b6fffb8d43e391fe6f4eb89638e64d6eff8026249aaa355a91625eb0bfd14caa81e4c3586aaa2e94fde143a44f223a91e226661d12f55dfcdb4215e5a64e14e968005733be6a71c465de312ca109b34a
+LCM = 431f918b274f3e43f446e4e85567883d6536a0332db662cef088f5a36b0f4b68372048174ba10fee94b9f8f1c2e189c974be2e6e8ae8e2ae108445326d40f63e38d8d4e2e46174589a3cbc9583e0036dc8146e79eee9e96f4436313b3f143dd0f5aceab05243def7f915169c360f55ef123977cf623c5ba432c3259c62fb5e37d5adab0f24b825aa4ada99ec4e83e9ca4698399e1ed633091ce5f9844c540a642cd264201116ed4168aa2105a5159f5df064f845830c469140f766c7319052ce59bd1ad7c3f2d8c30e54f147f6aeb5586c70c984302ba18d854a60aec01b394c7d66fa33fe18fe4a8cfb3238df219294e6e42190a30d28b10049a1b75853a4e
GCD = 206695d52bc391a4db61bf8cb6ea96188333a9c78f477ee76976c2346dad682cf56ca6f176d86ef67d41ff5921b6162b0eca52359975872430dd14c45643eacdf028d830770714c033fd150669705851b2f02de932322d271d565d26768530c3f6cb84f0b3356f970b9070b26c050ead0417152c324c8ffe266d4e8b5b7bef3a
A = 1114eb9f1a9d5947eb1399e57f5c980833489685023ed2fe537fe1276c1e026b9a19e6fff55aa889d6c4e977b6e6f3111e2ad463138637b50f42cf32e57d83f282de9e72f813e5969195159a666d74dcd689bd527c60199ae327f7bd548ac36868fea5fdf6f35d19b921e7c10b6448ca480de6826478cd0642d72f05af3f8e65ce42409fbd49f56e81946e89c8e83962c4edc0ed54600600a305e52d081aed3c351e450e11f8fb0ce5754c92cf765b71393b2b7a89c95df79b9ea1b3cb600862
B = 1d8f3179ca7b5cc7119360c10de939ffa57c9043da2f2b0ca3009c9bdad9f19ed16e3c2c197bef4b527fa1bf2bbab98b77e26c329911db68bd63d3d0fbfc727a977395b9ad067106de3094d68e097830858c5ccfa505fc25e972bdee6f347e7d1163efacd3d29a791ec2a94ffeed467884ae04896efc5e7e5f43d8d76c147e3c9951a1999173bc4e5767d51268b92cc68487ba1295372143b538711e0a62bf0ac111cc750ca4dd6c318c9cbe106d7fc492261404b86a1ba728e2d25b1976dc42
+LCM = f9570211f694141bfb096560551080cbe02a80271b4505591aaea9e3b99ea1d5ac1c1f2378fd72799e117ac2a73381b1ad26314e39972164d93971479ee3ba21a4d98cef0bd299d540ce5826995dcee0de420dff73d30b23cbf3188c625c7696df517535bc5675d71faa00807efbebdca547933f4a37849d1c014484a77da6df0670c4974bcc91eb5f5fe5faf9dd095ef195ec32ad9eeebf0e63288b4032ed9e70b888afc642f4ff96f0b4c0a68787301c12e4527fe79bdfe72dd3844ab5e094a9295df6616f24d1b9eeebc2116177dacf91969dda73667bc421ef3ccd8d5c23dddc283f5d36568d31f2654926be67f78e181075bdc148f2b39c630b141ae8a
GCD = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
A = 0
B = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
+LCM = 0
GCD = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
A = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
B = 0
+LCM = 0
GCD = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
A = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
B = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
+LCM = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423
+
+GCD = 2
+A = 14e95a85e59ade9ef39e2f400c65db18702fa5fc485b9bba479a5282b2206129160e54f73ef4917983c17b4c5ebff7be112a886de069706eee29ba902515cb038
+B = ddcfff1d39c90c599f55495bf71c1e7597c6b08b7430707f360c6a6e5137bbc7b403c6d9e2c34f3d2f29d5d32b869346853c2de239cc35381bdfb4a01569211a
+LCM = 90f38564ee72e55d362c04599e7d74f068c75f541b84e97abba2841f1a9f66b06b5c9009f6a4c2e319fced85270588de03ccebddbd9279aaecb13bdc1dbea7f42acaee751cb7da83779b8785cc86f41b94b13b54964208ca287d981634778d1096f20e76ca636c0717fd27e0800c43f599a5eded807421b502eaf9990a8c8ed8
+
+GCD = 4
+A = 3c719c1c363cdeb7b57c2aabb71f425da4c3e6d3e447204d555e7cf0f3d372bdda906f36078045044978dafc20171767c8b1464d52dfdf3e2ba8a4906da033a8
+B = 30fe0ef151ac51404e128c064d836b191921769dc02d9b09889ed40eb68d15bfdd2edea33580a1a4d7dcee918fefd5c776cbe80ca6131aa080d3989b5e77e1b24
+LCM = 2e4526157bbd765b0486d90bcd4728f890bc6dbd9a855c67ca5cb2d6b48f8e74e1d99485999e04b193afca58dbf282610185d6c0272007744ff26e00dbdc813929b47940b137dc56ba974da07d54a1c50ec4a5c2b26e83f47cf17f4ccce8c3687e8d1e91d7c491a599f3d057c73473723ce9eee52c20fe8ae1595447552a7ee8
+
+GCD = 10
+A = 44e04071d09119ea9783a53df35de4a989200133bb20280fdca6003d3ca63fdd9350ad1a1673d444d2f7c7be639824681643ec4f77535c626bd3ee8fa100e0bb0
+B = ca927a5a3124ce89accd6ac41a8441d352a5d42feb7f62687a5ebc0e181cc2679888ecc2d38516bdc3b3443550efccac81e53044ae9341ecace2598fe5ce67780
+LCM = 36805ba9b2412a0cb3fe4ed9bdabfa55515c9d615a3d0af268c45c5f6098d2de4a583f3791f1e3883c55d51ce23c5658fd0e8faa9a3709a1cfbd6a61dbab861690f27c86664f084c86cfd4a183b24aaadf59a6f8cbec04f1b0ded8a59b188cb46ae920052e3e099a570540dbc00f7d4a571eef08aa70d2d189a1804bf04e94a80
+
+GCD = 100
+A = 73725032b214a677687c811031555b0c51c1703f10d59b97a4d732b7feaec5726cb3882193419d3f057583b2bc02b297d76bb689977936febaae92638fdfc46a00
+B = 979f4c10f4dc60ad15068cedd62ff0ab293aeaa1d6935763aed41fe3e445de2e366e8661eadf345201529310f4b805c5800b99f351fddab95d7f313e3bb429d900
+LCM = 4460439b4be72f533e9c7232f7e99c48328b457969364c951868ceab56cb2cbbeda8be2e8e3cae45c0758048468b841fdb246b2086d19b59d17b389333166ab82ed785860620d53c44f7aaaff4625ee70fb8072df10fb4d1acb142eadc02978ff2bb07cea9f434e35424b3323a7bda3a1a57aa60c75e49ebb2f59fb653aa77da00
+
+GCD = 100000000
+A = f8b4f19e09f5862d79fb2931c4d616a1b8e0dd44781ca52902c8035166c8fca52d33a56ff484c365ec1257de7fa8ed2786163cfc051d5223b4aad859a049e8ba00000000
+B = 6e54cb41b454b080e68a2c3dd0fa79f516eb80239af2be8250ca9cd377ba501aabafc09146fad4402bdc7a49f2c3eec815e25f4c0a223f58e36709eefd92410500000000
+LCM = 6b3020a880ddeff9d17d3dc234da8771962de3322cd15ba7b1e4b1dd4a6a2a802a16c49653865c6fdf6c207cbe0940f8d81ef4cb0e159385fd709d515ee99d109ad9ad680031cbae4eab2ed62944babdade4e3036426b18920022f737897c7d751dce98d626cdda761fec48ad87a377fb70f97a0a15aa3d10d865785719cc5a200000000
diff --git a/src/crypto/fipsmodule/bn/check_bn_tests.go b/src/crypto/fipsmodule/bn/check_bn_tests.go
index abb4ca5b..bf05e75f 100644
--- a/src/crypto/fipsmodule/bn/check_bn_tests.go
+++ b/src/crypto/fipsmodule/bn/check_bn_tests.go
@@ -265,7 +265,7 @@ func main() {
}
}
case "GCD":
- if checkKeys(test, "A", "B", "GCD") {
+ if checkKeys(test, "A", "B", "GCD", "LCM") {
a := test.Values["A"]
b := test.Values["B"]
// Go's GCD function does not accept zero, unlike OpenSSL.
@@ -278,6 +278,11 @@ func main() {
g = new(big.Int).GCD(nil, nil, a, b)
}
checkResult(test, "GCD(A, B)", "GCD", g)
+ if g.Sign() != 0 {
+ lcm := new(big.Int).Mul(a, b)
+ lcm = lcm.Div(lcm, g)
+ checkResult(test, "LCM(A, B)", "LCM", lcm)
+ }
}
default:
fmt.Fprintf(os.Stderr, "Line %d: unknown test type %q.\n", test.LineNumber, test.Type)
diff --git a/src/crypto/fipsmodule/bn/cmp.c b/src/crypto/fipsmodule/bn/cmp.c
index 89775c0f..692adb5a 100644
--- a/src/crypto/fipsmodule/bn/cmp.c
+++ b/src/crypto/fipsmodule/bn/cmp.c
@@ -128,14 +128,14 @@ int bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len) {
}
int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w) {
- switch (bn_minimal_width(bn)) {
- case 1:
- return bn->d[0] == w;
- case 0:
- return w == 0;
- default:
- return 0;
+ if (bn->width == 0) {
+ return w == 0;
+ }
+ BN_ULONG mask = bn->d[0] ^ w;
+ for (int i = 1; i < bn->width; i++) {
+ mask |= bn->d[i];
}
+ return mask == 0;
}
int BN_cmp_word(const BIGNUM *a, BN_ULONG b) {
@@ -150,7 +150,7 @@ int BN_cmp_word(const BIGNUM *a, BN_ULONG b) {
}
int BN_is_zero(const BIGNUM *bn) {
- return bn_minimal_width(bn) == 0;
+ return bn_fits_in_words(bn, 0);
}
int BN_is_one(const BIGNUM *bn) {
diff --git a/src/crypto/fipsmodule/bn/div.c b/src/crypto/fipsmodule/bn/div.c
index 6f850d9a..1950561e 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -414,6 +414,35 @@ int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) {
return (d->neg ? BN_sub : BN_add)(r, r, d);
}
+BN_ULONG bn_reduce_once(BN_ULONG *r, const BN_ULONG *a, BN_ULONG carry,
+ const BN_ULONG *m, size_t num) {
+ assert(r != a);
+ // |r| = |a| - |m|. |bn_sub_words| performs the bulk of the subtraction, and
+ // then we apply the borrow to |carry|.
+ carry -= bn_sub_words(r, a, m, num);
+ // We know 0 <= |a| < 2*|m|, so -|m| <= |r| < |m|.
+ //
+ // If 0 <= |r| < |m|, |r| fits in |num| words and |carry| is zero. We then
+ // wish to select |r| as the answer. Otherwise -m <= r < 0 and we wish to
+ // return |r| + |m|, or |a|. |carry| must then be -1 or all ones. In both
+ // cases, |carry| is a suitable input to |bn_select_words|.
+ //
+ // Although |carry| may be one if it was one on input and |bn_sub_words|
+ // returns zero, this would give |r| > |m|, violating our input assumptions.
+ assert(carry == 0 || carry == (BN_ULONG)-1);
+ bn_select_words(r, carry, a /* r < 0 */, r /* r >= 0 */, num);
+ return carry;
+}
+
+BN_ULONG bn_reduce_once_in_place(BN_ULONG *r, BN_ULONG carry, const BN_ULONG *m,
+ BN_ULONG *tmp, size_t num) {
+ // See |bn_reduce_once| for why this logic works.
+ carry -= bn_sub_words(tmp, r, m, num);
+ assert(carry == 0 || carry == (BN_ULONG)-1);
+ bn_select_words(r, carry, r /* tmp < 0 */, tmp /* tmp >= 0 */, num);
+ return carry;
+}
+
// bn_mod_sub_words sets |r| to |a| - |b| (mod |m|), using |tmp| as scratch
// space. Each array is |num| words long. |a| and |b| must be < |m|. Any pair of
// |r|, |a|, and |b| may alias.
@@ -426,32 +455,83 @@ static void bn_mod_sub_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
bn_select_words(r, 0 - borrow, tmp /* r < 0 */, r /* r >= 0 */, num);
}
-// bn_mod_add_words sets |r| to |a| + |b| (mod |m|), using |tmp| as scratch
-// space. Each array is |num| words long. |a| and |b| must be < |m|. Any pair of
-// |r|, |a|, and |b| may alias.
-static void bn_mod_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
- const BN_ULONG *m, BN_ULONG *tmp, size_t num) {
- // tmp = a + b. Note the result fits in |num|+1 words. We store the extra word
- // in |carry|.
- BN_ULONG carry = bn_add_words(tmp, a, b, num);
- // r = a + b - m. We use |bn_sub_words| to perform the bulk of the
- // subtraction, and then apply the borrow to |carry|.
- carry -= bn_sub_words(r, tmp, m, num);
- // |a| and |b| were both fully-reduced, so we know:
- //
- // 0 + 0 - m <= r < m + m - m
- // -m <= r < m
- //
- // If 0 <= |r| < |m|, |r| fits in |num| words and |carry| is zero. We then
- // wish to select |r| as the answer. Otherwise -m <= r < 0 and we wish to
- // return |r| + |m|, or |tmp|. |carry| must then be -1 or all ones. In both
- // cases, |carry| is a suitable input to |bn_select_words|.
- //
- // Although |carry| may be one if |bn_add_words| returns one and
- // |bn_sub_words| returns zero, this would give |r| > |m|, which violates are
- // input assumptions.
- assert(carry == 0 || carry == (BN_ULONG)-1);
- bn_select_words(r, carry, tmp /* r < 0 */, r /* r >= 0 */, num);
+void bn_mod_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+ const BN_ULONG *m, BN_ULONG *tmp, size_t num) {
+ BN_ULONG carry = bn_add_words(r, a, b, num);
+ bn_reduce_once_in_place(r, carry, m, tmp, num);
+}
+
+int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder,
+ const BIGNUM *numerator, const BIGNUM *divisor,
+ BN_CTX *ctx) {
+ if (BN_is_negative(numerator) || BN_is_negative(divisor)) {
+ OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
+ return 0;
+ }
+ if (BN_is_zero(divisor)) {
+ OPENSSL_PUT_ERROR(BN, BN_R_DIV_BY_ZERO);
+ return 0;
+ }
+
+ // This function implements long division in binary. It is not very efficient,
+ // but it is simple, easy to make constant-time, and performant enough for RSA
+ // key generation.
+
+ int ret = 0;
+ BN_CTX_start(ctx);
+ BIGNUM *q = quotient, *r = remainder;
+ if (quotient == NULL || quotient == numerator || quotient == divisor) {
+ q = BN_CTX_get(ctx);
+ }
+ if (remainder == NULL || remainder == numerator || remainder == divisor) {
+ r = BN_CTX_get(ctx);
+ }
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ if (q == NULL || r == NULL || tmp == NULL ||
+ !bn_wexpand(q, numerator->width) ||
+ !bn_wexpand(r, divisor->width) ||
+ !bn_wexpand(tmp, divisor->width)) {
+ goto err;
+ }
+
+ OPENSSL_memset(q->d, 0, numerator->width * sizeof(BN_ULONG));
+ q->width = numerator->width;
+ q->neg = 0;
+
+ OPENSSL_memset(r->d, 0, divisor->width * sizeof(BN_ULONG));
+ r->width = divisor->width;
+ r->neg = 0;
+
+ // Incorporate |numerator| into |r|, one bit at a time, reducing after each
+ // step. At the start of each loop iteration, |r| < |divisor|
+ for (int i = numerator->width - 1; i >= 0; i--) {
+ for (int bit = BN_BITS2 - 1; bit >= 0; bit--) {
+ // Incorporate the next bit of the numerator, by computing
+ // r = 2*r or 2*r + 1. Note the result fits in one more word. We store the
+ // extra word in |carry|.
+ BN_ULONG carry = bn_add_words(r->d, r->d, r->d, divisor->width);
+ r->d[0] |= (numerator->d[i] >> bit) & 1;
+ // |r| was previously fully-reduced, so we know:
+ // 2*0 <= r <= 2*(divisor-1) + 1
+ // 0 <= r <= 2*divisor - 1 < 2*divisor.
+ // Thus |r| satisfies the preconditions for |bn_reduce_once_in_place|.
+ BN_ULONG subtracted = bn_reduce_once_in_place(r->d, carry, divisor->d,
+ tmp->d, divisor->width);
+ // The corresponding bit of the quotient is set iff we needed to subtract.
+ q->d[i] |= (~subtracted & 1) << bit;
+ }
+ }
+
+ if ((quotient != NULL && !BN_copy(quotient, q)) ||
+ (remainder != NULL && !BN_copy(remainder, r))) {
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ BN_CTX_end(ctx);
+ return ret;
}
static BIGNUM *bn_scratch_space_from_ctx(size_t width, BN_CTX *ctx) {
@@ -498,12 +578,12 @@ int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m) {
BN_CTX *ctx = BN_CTX_new();
int ok = ctx != NULL &&
- bn_mod_add_quick_ctx(r, a, b, m, ctx);
+ bn_mod_add_consttime(r, a, b, m, ctx);
BN_CTX_free(ctx);
return ok;
}
-int bn_mod_add_quick_ctx(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+int bn_mod_add_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m, BN_CTX *ctx) {
BN_CTX_start(ctx);
a = bn_resized_from_ctx(a, m->width, ctx);
@@ -527,7 +607,7 @@ int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
return BN_nnmod(r, r, m, ctx);
}
-int bn_mod_sub_quick_ctx(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+int bn_mod_sub_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m, BN_CTX *ctx) {
BN_CTX_start(ctx);
a = bn_resized_from_ctx(a, m->width, ctx);
@@ -547,7 +627,7 @@ int BN_mod_sub_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m) {
BN_CTX *ctx = BN_CTX_new();
int ok = ctx != NULL &&
- bn_mod_sub_quick_ctx(r, a, b, m, ctx);
+ bn_mod_sub_consttime(r, a, b, m, ctx);
BN_CTX_free(ctx);
return ok;
}
@@ -610,19 +690,19 @@ int BN_mod_lshift(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
abs_m->neg = 0;
}
- ret = bn_mod_lshift_quick_ctx(r, r, n, (abs_m ? abs_m : m), ctx);
+ ret = bn_mod_lshift_consttime(r, r, n, (abs_m ? abs_m : m), ctx);
BN_free(abs_m);
return ret;
}
-int bn_mod_lshift_quick_ctx(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
+int bn_mod_lshift_consttime(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
BN_CTX *ctx) {
if (!BN_copy(r, a)) {
return 0;
}
for (int i = 0; i < n; i++) {
- if (!bn_mod_lshift1_quick_ctx(r, r, m, ctx)) {
+ if (!bn_mod_lshift1_consttime(r, r, m, ctx)) {
return 0;
}
}
@@ -632,7 +712,7 @@ int bn_mod_lshift_quick_ctx(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
int BN_mod_lshift_quick(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m) {
BN_CTX *ctx = BN_CTX_new();
int ok = ctx != NULL &&
- bn_mod_lshift_quick_ctx(r, a, n, m, ctx);
+ bn_mod_lshift_consttime(r, a, n, m, ctx);
BN_CTX_free(ctx);
return ok;
}
@@ -645,15 +725,15 @@ int BN_mod_lshift1(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx) {
return BN_nnmod(r, r, m, ctx);
}
-int bn_mod_lshift1_quick_ctx(BIGNUM *r, const BIGNUM *a, const BIGNUM *m,
+int bn_mod_lshift1_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *m,
BN_CTX *ctx) {
- return bn_mod_add_quick_ctx(r, a, a, m, ctx);
+ return bn_mod_add_consttime(r, a, a, m, ctx);
}
int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *m) {
BN_CTX *ctx = BN_CTX_new();
int ok = ctx != NULL &&
- bn_mod_lshift1_quick_ctx(r, a, m, ctx);
+ bn_mod_lshift1_consttime(r, a, m, ctx);
BN_CTX_free(ctx);
return ok;
}
diff --git a/src/crypto/fipsmodule/bn/gcd.c b/src/crypto/fipsmodule/bn/gcd.c
index 850d4467..7868b407 100644
--- a/src/crypto/fipsmodule/bn/gcd.c
+++ b/src/crypto/fipsmodule/bn/gcd.c
@@ -114,121 +114,310 @@
#include "internal.h"
-static BIGNUM *euclid(BIGNUM *a, BIGNUM *b) {
- BIGNUM *t;
- int shifts = 0;
- // 0 <= b <= a
- while (!BN_is_zero(b)) {
- // 0 < b <= a
+static BN_ULONG word_is_odd_mask(BN_ULONG a) { return (BN_ULONG)0 - (a & 1); }
- if (BN_is_odd(a)) {
- if (BN_is_odd(b)) {
- if (!BN_sub(a, a, b)) {
- goto err;
- }
- if (!BN_rshift1(a, a)) {
- goto err;
- }
- if (BN_cmp(a, b) < 0) {
- t = a;
- a = b;
- b = t;
- }
- } else {
- // a odd - b even
- if (!BN_rshift1(b, b)) {
- goto err;
- }
- if (BN_cmp(a, b) < 0) {
- t = a;
- a = b;
- b = t;
- }
- }
- } else {
- // a is even
- if (BN_is_odd(b)) {
- if (!BN_rshift1(a, a)) {
- goto err;
- }
- if (BN_cmp(a, b) < 0) {
- t = a;
- a = b;
- b = t;
- }
- } else {
- // a even - b even
- if (!BN_rshift1(a, a)) {
- goto err;
- }
- if (!BN_rshift1(b, b)) {
- goto err;
- }
- shifts++;
- }
- }
- // 0 <= b <= a
+static void maybe_rshift1_words(BN_ULONG *a, BN_ULONG mask, BN_ULONG *tmp,
+ size_t num) {
+ bn_rshift1_words(tmp, a, num);
+ bn_select_words(a, mask, tmp, a, num);
+}
+
+static void maybe_rshift1_words_carry(BN_ULONG *a, BN_ULONG carry,
+ BN_ULONG mask, BN_ULONG *tmp,
+ size_t num) {
+ maybe_rshift1_words(a, mask, tmp, num);
+ if (num != 0) {
+ carry &= mask;
+ a[num - 1] |= carry << (BN_BITS2-1);
+ }
+}
+
+static BN_ULONG maybe_add_words(BN_ULONG *a, BN_ULONG mask, const BN_ULONG *b,
+ BN_ULONG *tmp, size_t num) {
+ BN_ULONG carry = bn_add_words(tmp, a, b, num);
+ bn_select_words(a, mask, tmp, a, num);
+ return carry & mask;
+}
+
+static int bn_gcd_consttime(BIGNUM *r, unsigned *out_shift, const BIGNUM *x,
+ const BIGNUM *y, BN_CTX *ctx) {
+ size_t width = x->width > y->width ? x->width : y->width;
+ if (width == 0) {
+ *out_shift = 0;
+ BN_zero(r);
+ return 1;
}
- if (shifts) {
- if (!BN_lshift(a, a, shifts)) {
- goto err;
- }
+ // This is a constant-time implementation of Stein's algorithm (binary GCD).
+ int ret = 0;
+ BN_CTX_start(ctx);
+ BIGNUM *u = BN_CTX_get(ctx);
+ BIGNUM *v = BN_CTX_get(ctx);
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ if (u == NULL || v == NULL || tmp == NULL ||
+ !BN_copy(u, x) ||
+ !BN_copy(v, y) ||
+ !bn_resize_words(u, width) ||
+ !bn_resize_words(v, width) ||
+ !bn_resize_words(tmp, width)) {
+ goto err;
+ }
+
+ // Each loop iteration halves at least one of |u| and |v|. Thus we need at
+ // most the combined bit width of inputs for at least one value to be zero.
+ unsigned x_bits = x->width * BN_BITS2, y_bits = y->width * BN_BITS2;
+ unsigned num_iters = x_bits + y_bits;
+ if (num_iters < x_bits) {
+ OPENSSL_PUT_ERROR(BN, BN_R_BIGNUM_TOO_LONG);
+ goto err;
}
- return a;
+ unsigned shift = 0;
+ for (unsigned i = 0; i < num_iters; i++) {
+ BN_ULONG both_odd = word_is_odd_mask(u->d[0]) & word_is_odd_mask(v->d[0]);
+
+ // If both |u| and |v| are odd, subtract the smaller from the larger.
+ BN_ULONG u_less_than_v =
+ (BN_ULONG)0 - bn_sub_words(tmp->d, u->d, v->d, width);
+ bn_select_words(u->d, both_odd & ~u_less_than_v, tmp->d, u->d, width);
+ bn_sub_words(tmp->d, v->d, u->d, width);
+ bn_select_words(v->d, both_odd & u_less_than_v, tmp->d, v->d, width);
+
+ // At least one of |u| and |v| is now even.
+ BN_ULONG u_is_odd = word_is_odd_mask(u->d[0]);
+ BN_ULONG v_is_odd = word_is_odd_mask(v->d[0]);
+ assert(!(u_is_odd & v_is_odd));
+
+ // If both are even, the final GCD gains a factor of two.
+ shift += 1 & (~u_is_odd & ~v_is_odd);
+
+ // Halve any which are even.
+ maybe_rshift1_words(u->d, ~u_is_odd, tmp->d, width);
+ maybe_rshift1_words(v->d, ~v_is_odd, tmp->d, width);
+ }
+
+ // One of |u| or |v| is zero at this point. The algorithm usually makes |u|
+ // zero, unless |y| was already zero on input. Fix this by combining the
+ // values.
+ assert(BN_is_zero(u) || BN_is_zero(v));
+ for (size_t i = 0; i < width; i++) {
+ v->d[i] |= u->d[i];
+ }
+
+ *out_shift = shift;
+ ret = bn_set_words(r, v->d, width);
err:
- return NULL;
+ BN_CTX_end(ctx);
+ return ret;
}
-int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) {
- BIGNUM *a, *b, *t;
+int BN_gcd(BIGNUM *r, const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx) {
+ unsigned shift;
+ return bn_gcd_consttime(r, &shift, x, y, ctx) &&
+ BN_lshift(r, r, shift);
+}
+
+int bn_is_relatively_prime(int *out_relatively_prime, const BIGNUM *x,
+ const BIGNUM *y, BN_CTX *ctx) {
int ret = 0;
+ BN_CTX_start(ctx);
+ unsigned shift;
+ BIGNUM *gcd = BN_CTX_get(ctx);
+ if (gcd == NULL ||
+ !bn_gcd_consttime(gcd, &shift, x, y, ctx)) {
+ goto err;
+ }
+
+ // Check that 2^|shift| * |gcd| is one.
+ if (gcd->width == 0) {
+ *out_relatively_prime = 0;
+ } else {
+ BN_ULONG mask = shift | (gcd->d[0] ^ 1);
+ for (int i = 1; i < gcd->width; i++) {
+ mask |= gcd->d[i];
+ }
+ *out_relatively_prime = mask == 0;
+ }
+ ret = 1;
+
+err:
+ BN_CTX_end(ctx);
+ return ret;
+}
+int bn_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
BN_CTX_start(ctx);
- a = BN_CTX_get(ctx);
- b = BN_CTX_get(ctx);
+ unsigned shift;
+ BIGNUM *gcd = BN_CTX_get(ctx);
+ int ret = gcd != NULL &&
+ bn_mul_consttime(r, a, b, ctx) &&
+ bn_gcd_consttime(gcd, &shift, a, b, ctx) &&
+ bn_div_consttime(r, NULL, r, gcd, ctx) &&
+ bn_rshift_secret_shift(r, r, shift, ctx);
+ BN_CTX_end(ctx);
+ return ret;
+}
- if (a == NULL || b == NULL) {
- goto err;
+int bn_mod_inverse_consttime(BIGNUM *r, int *out_no_inverse, const BIGNUM *a,
+ const BIGNUM *n, BN_CTX *ctx) {
+ *out_no_inverse = 0;
+ if (BN_is_negative(a) || BN_ucmp(a, n) >= 0) {
+ OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED);
+ return 0;
}
- if (BN_copy(a, in_a) == NULL) {
- goto err;
+ if (BN_is_zero(a)) {
+ if (BN_is_one(n)) {
+ BN_zero(r);
+ return 1;
+ }
+ *out_no_inverse = 1;
+ OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
+ return 0;
}
- if (BN_copy(b, in_b) == NULL) {
- goto err;
+
+ // This is a constant-time implementation of the extended binary GCD
+ // algorithm. It is adapted from the Handbook of Applied Cryptography, section
+ // 14.4.3, algorithm 14.51, and modified to bound coefficients and avoid
+ // negative numbers.
+ //
+ // For more details and proof of correctness, see
+ // https://github.com/mit-plv/fiat-crypto/pull/333. In particular, see |step|
+ // and |mod_inverse_consttime| for the algorithm in Gallina and see
+ // |mod_inverse_consttime_spec| for the correctness result.
+
+ if (!BN_is_odd(a) && !BN_is_odd(n)) {
+ *out_no_inverse = 1;
+ OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
+ return 0;
}
- a->neg = 0;
- b->neg = 0;
+ // This function exists to compute the RSA private exponent, where |a| is one
+ // word. We'll thus use |a_width| when available.
+ size_t n_width = n->width, a_width = a->width;
+ if (a_width > n_width) {
+ a_width = n_width;
+ }
- if (BN_cmp(a, b) < 0) {
- t = a;
- a = b;
- b = t;
+ int ret = 0;
+ BN_CTX_start(ctx);
+ BIGNUM *u = BN_CTX_get(ctx);
+ BIGNUM *v = BN_CTX_get(ctx);
+ BIGNUM *A = BN_CTX_get(ctx);
+ BIGNUM *B = BN_CTX_get(ctx);
+ BIGNUM *C = BN_CTX_get(ctx);
+ BIGNUM *D = BN_CTX_get(ctx);
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ BIGNUM *tmp2 = BN_CTX_get(ctx);
+ if (u == NULL || v == NULL || A == NULL || B == NULL || C == NULL ||
+ D == NULL || tmp == NULL || tmp2 == NULL ||
+ !BN_copy(u, a) ||
+ !BN_copy(v, n) ||
+ !BN_one(A) ||
+ !BN_one(D) ||
+ // For convenience, size |u| and |v| equivalently.
+ !bn_resize_words(u, n_width) ||
+ !bn_resize_words(v, n_width) ||
+ // |A| and |C| are bounded by |m|.
+ !bn_resize_words(A, n_width) ||
+ !bn_resize_words(C, n_width) ||
+ // |B| and |D| are bounded by |a|.
+ !bn_resize_words(B, a_width) ||
+ !bn_resize_words(D, a_width) ||
+ // |tmp| and |tmp2| may be used at either size.
+ !bn_resize_words(tmp, n_width) ||
+ !bn_resize_words(tmp2, n_width)) {
+ goto err;
}
- t = euclid(a, b);
- if (t == NULL) {
+
+ // Each loop iteration halves at least one of |u| and |v|. Thus we need at
+ // most the combined bit width of inputs for at least one value to be zero.
+ unsigned a_bits = a_width * BN_BITS2, n_bits = n_width * BN_BITS2;
+ unsigned num_iters = a_bits + n_bits;
+ if (num_iters < a_bits) {
+ OPENSSL_PUT_ERROR(BN, BN_R_BIGNUM_TOO_LONG);
goto err;
}
- if (BN_copy(r, t) == NULL) {
+ // Before and after each loop iteration, the following hold:
+ //
+ // u = A*a - B*n
+ // v = D*n - C*a
+ // 0 < u <= a
+ // 0 <= v <= n
+ // 0 <= A < n
+ // 0 <= B <= a
+ // 0 <= C < n
+ // 0 <= D <= a
+ //
+ // After each loop iteration, u and v only get smaller, and at least one of
+ // them shrinks by at least a factor of two.
+ for (unsigned i = 0; i < num_iters; i++) {
+ BN_ULONG both_odd = word_is_odd_mask(u->d[0]) & word_is_odd_mask(v->d[0]);
+
+ // If both |u| and |v| are odd, subtract the smaller from the larger.
+ BN_ULONG v_less_than_u =
+ (BN_ULONG)0 - bn_sub_words(tmp->d, v->d, u->d, n_width);
+ bn_select_words(v->d, both_odd & ~v_less_than_u, tmp->d, v->d, n_width);
+ bn_sub_words(tmp->d, u->d, v->d, n_width);
+ bn_select_words(u->d, both_odd & v_less_than_u, tmp->d, u->d, n_width);
+
+ // If we updated one of the values, update the corresponding coefficient.
+ BN_ULONG carry = bn_add_words(tmp->d, A->d, C->d, n_width);
+ carry -= bn_sub_words(tmp2->d, tmp->d, n->d, n_width);
+ bn_select_words(tmp->d, carry, tmp->d, tmp2->d, n_width);
+ bn_select_words(A->d, both_odd & v_less_than_u, tmp->d, A->d, n_width);
+ bn_select_words(C->d, both_odd & ~v_less_than_u, tmp->d, C->d, n_width);
+
+ bn_add_words(tmp->d, B->d, D->d, a_width);
+ bn_sub_words(tmp2->d, tmp->d, a->d, a_width);
+ bn_select_words(tmp->d, carry, tmp->d, tmp2->d, a_width);
+ bn_select_words(B->d, both_odd & v_less_than_u, tmp->d, B->d, a_width);
+ bn_select_words(D->d, both_odd & ~v_less_than_u, tmp->d, D->d, a_width);
+
+ // Our loop invariants hold at this point. Additionally, exactly one of |u|
+ // and |v| is now even.
+ BN_ULONG u_is_even = ~word_is_odd_mask(u->d[0]);
+ BN_ULONG v_is_even = ~word_is_odd_mask(v->d[0]);
+ assert(u_is_even != v_is_even);
+
+ // Halve the even one and adjust the corresponding coefficient.
+ maybe_rshift1_words(u->d, u_is_even, tmp->d, n_width);
+ BN_ULONG A_or_B_is_odd =
+ word_is_odd_mask(A->d[0]) | word_is_odd_mask(B->d[0]);
+ BN_ULONG A_carry =
+ maybe_add_words(A->d, A_or_B_is_odd & u_is_even, n->d, tmp->d, n_width);
+ BN_ULONG B_carry =
+ maybe_add_words(B->d, A_or_B_is_odd & u_is_even, a->d, tmp->d, a_width);
+ maybe_rshift1_words_carry(A->d, A_carry, u_is_even, tmp->d, n_width);
+ maybe_rshift1_words_carry(B->d, B_carry, u_is_even, tmp->d, a_width);
+
+ maybe_rshift1_words(v->d, v_is_even, tmp->d, n_width);
+ BN_ULONG C_or_D_is_odd =
+ word_is_odd_mask(C->d[0]) | word_is_odd_mask(D->d[0]);
+ BN_ULONG C_carry =
+ maybe_add_words(C->d, C_or_D_is_odd & v_is_even, n->d, tmp->d, n_width);
+ BN_ULONG D_carry =
+ maybe_add_words(D->d, C_or_D_is_odd & v_is_even, a->d, tmp->d, a_width);
+ maybe_rshift1_words_carry(C->d, C_carry, v_is_even, tmp->d, n_width);
+ maybe_rshift1_words_carry(D->d, D_carry, v_is_even, tmp->d, a_width);
+ }
+
+ assert(BN_is_zero(v));
+ if (!BN_is_one(u)) {
+ *out_no_inverse = 1;
+ OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
goto err;
}
- ret = 1;
+
+ ret = BN_copy(r, A) != NULL;
err:
BN_CTX_end(ctx);
return ret;
}
-// solves ax == 1 (mod n)
-static int bn_mod_inverse_general(BIGNUM *out, int *out_no_inverse,
- const BIGNUM *a, const BIGNUM *n,
- BN_CTX *ctx);
-
int BN_mod_inverse_odd(BIGNUM *out, int *out_no_inverse, const BIGNUM *a,
const BIGNUM *n, BN_CTX *ctx) {
*out_no_inverse = 0;
@@ -423,7 +612,7 @@ BIGNUM *BN_mod_inverse(BIGNUM *out, const BIGNUM *a, const BIGNUM *n,
int no_inverse;
if (!BN_is_odd(n)) {
- if (!bn_mod_inverse_general(out, &no_inverse, a, n, ctx)) {
+ if (!bn_mod_inverse_consttime(out, &no_inverse, a, n, ctx)) {
goto err;
}
} else if (!BN_mod_inverse_odd(out, &no_inverse, a, n, ctx)) {
@@ -469,139 +658,6 @@ err:
return ret;
}
-// bn_mod_inverse_general is the general inversion algorithm that works for
-// both even and odd |n|. It was specifically designed to contain fewer
-// branches that may leak sensitive information; see "New Branch Prediction
-// Vulnerabilities in OpenSSL and Necessary Software Countermeasures" by
-// Onur Acıçmez, Shay Gueron, and Jean-Pierre Seifert.
-static int bn_mod_inverse_general(BIGNUM *out, int *out_no_inverse,
- const BIGNUM *a, const BIGNUM *n,
- BN_CTX *ctx) {
- BIGNUM *A, *B, *X, *Y, *M, *D, *T;
- int ret = 0;
- int sign;
-
- *out_no_inverse = 0;
-
- BN_CTX_start(ctx);
- A = BN_CTX_get(ctx);
- B = BN_CTX_get(ctx);
- X = BN_CTX_get(ctx);
- D = BN_CTX_get(ctx);
- M = BN_CTX_get(ctx);
- Y = BN_CTX_get(ctx);
- T = BN_CTX_get(ctx);
- if (T == NULL) {
- goto err;
- }
-
- BIGNUM *R = out;
-
- BN_zero(Y);
- if (!BN_one(X) || BN_copy(B, a) == NULL || BN_copy(A, n) == NULL) {
- goto err;
- }
- A->neg = 0;
-
- sign = -1;
- // From B = a mod |n|, A = |n| it follows that
- //
- // 0 <= B < A,
- // -sign*X*a == B (mod |n|),
- // sign*Y*a == A (mod |n|).
-
- while (!BN_is_zero(B)) {
- BIGNUM *tmp;
-
- // 0 < B < A,
- // (*) -sign*X*a == B (mod |n|),
- // sign*Y*a == A (mod |n|)
-
- // (D, M) := (A/B, A%B) ...
- if (!BN_div(D, M, A, B, ctx)) {
- goto err;
- }
-
- // Now
- // A = D*B + M;
- // thus we have
- // (**) sign*Y*a == D*B + M (mod |n|).
-
- tmp = A; // keep the BIGNUM object, the value does not matter
-
- // (A, B) := (B, A mod B) ...
- A = B;
- B = M;
- // ... so we have 0 <= B < A again
-
- // Since the former M is now B and the former B is now A,
- // (**) translates into
- // sign*Y*a == D*A + B (mod |n|),
- // i.e.
- // sign*Y*a - D*A == B (mod |n|).
- // Similarly, (*) translates into
- // -sign*X*a == A (mod |n|).
- //
- // Thus,
- // sign*Y*a + D*sign*X*a == B (mod |n|),
- // i.e.
- // sign*(Y + D*X)*a == B (mod |n|).
- //
- // So if we set (X, Y, sign) := (Y + D*X, X, -sign), we arrive back at
- // -sign*X*a == B (mod |n|),
- // sign*Y*a == A (mod |n|).
- // Note that X and Y stay non-negative all the time.
-
- if (!BN_mul(tmp, D, X, ctx)) {
- goto err;
- }
- if (!BN_add(tmp, tmp, Y)) {
- goto err;
- }
-
- M = Y; // keep the BIGNUM object, the value does not matter
- Y = X;
- X = tmp;
- sign = -sign;
- }
-
- if (!BN_is_one(A)) {
- *out_no_inverse = 1;
- OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
- goto err;
- }
-
- // The while loop (Euclid's algorithm) ends when
- // A == gcd(a,n);
- // we have
- // sign*Y*a == A (mod |n|),
- // where Y is non-negative.
-
- if (sign < 0) {
- if (!BN_sub(Y, n, Y)) {
- goto err;
- }
- }
- // Now Y*a == A (mod |n|).
-
- // Y*a == 1 (mod |n|)
- if (!Y->neg && BN_ucmp(Y, n) < 0) {
- if (!BN_copy(R, Y)) {
- goto err;
- }
- } else {
- if (!BN_nnmod(R, Y, n, ctx)) {
- goto err;
- }
- }
-
- ret = 1;
-
-err:
- BN_CTX_end(ctx);
- return ret;
-}
-
int bn_mod_inverse_prime(BIGNUM *out, const BIGNUM *a, const BIGNUM *p,
BN_CTX *ctx, const BN_MONT_CTX *mont_p) {
BN_CTX_start(ctx);
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index 20945a98..668d8ddf 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -285,10 +285,8 @@ void bn_sqr_comba4(BN_ULONG r[8], const BN_ULONG a[4]);
int bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len);
// bn_in_range_words returns one if |min_inclusive| <= |a| < |max_exclusive|,
-// where |a| and |max_exclusive| both are |len| words long. This function leaks
-// which of [0, min_inclusive), [min_inclusive, max_exclusive), and
-// [max_exclusive, 2^(BN_BITS2*len)) contains |a|, but otherwise the value of
-// |a| is secret.
+// where |a| and |max_exclusive| both are |len| words long. |a| and
+// |max_exclusive| are treated as secret.
int bn_in_range_words(const BN_ULONG *a, BN_ULONG min_inclusive,
const BN_ULONG *max_exclusive, size_t len);
@@ -303,6 +301,27 @@ int bn_rand_range_words(BN_ULONG *out, BN_ULONG min_inclusive,
const BN_ULONG *max_exclusive, size_t len,
const uint8_t additional_data[32]);
+// bn_range_secret_range behaves like |BN_rand_range_ex|, but treats
+// |max_exclusive| as secret. Because of this constraint, the distribution of
+// values returned is more complex.
+//
+// Rather than repeatedly generating values until one is in range, which would
+// leak information, it generates one value. If the value is in range, it sets
+// |*out_is_uniform| to one. Otherwise, it sets |*out_is_uniform| to zero,
+// fixing up the value to force it in range.
+//
+// The subset of calls to |bn_rand_secret_range| which set |*out_is_uniform| to
+// one are uniformly distributed in the target range. Calls overall are not.
+// This function is intended for use in situations where the extra values are
+// still usable and where the number of iterations needed to reach the target
+// number of uniform outputs may be blinded for negligible probabilities of
+// timing leaks.
+//
+// Although this function treats |max_exclusive| as secret, it treats the number
+// of bits in |max_exclusive| as public.
+int bn_rand_secret_range(BIGNUM *r, int *out_is_uniform, BN_ULONG min_inclusive,
+ const BIGNUM *max_exclusive);
+
int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
const BN_ULONG *np, const BN_ULONG *n0, int num);
@@ -323,18 +342,6 @@ int bn_mod_exp_base_2_consttime(BIGNUM *r, unsigned p, const BIGNUM *n,
#error "Either BN_ULLONG or BN_UMULT_LOHI must be defined on every platform."
#endif
-// bn_mod_inverse_prime sets |out| to the modular inverse of |a| modulo |p|,
-// computed with Fermat's Little Theorem. It returns one on success and zero on
-// error. If |mont_p| is NULL, one will be computed temporarily.
-int bn_mod_inverse_prime(BIGNUM *out, const BIGNUM *a, const BIGNUM *p,
- BN_CTX *ctx, const BN_MONT_CTX *mont_p);
-
-// bn_mod_inverse_secret_prime behaves like |bn_mod_inverse_prime| but uses
-// |BN_mod_exp_mont_consttime| instead of |BN_mod_exp_mont| in hopes of
-// protecting the exponent.
-int bn_mod_inverse_secret_prime(BIGNUM *out, const BIGNUM *a, const BIGNUM *p,
- BN_CTX *ctx, const BN_MONT_CTX *mont_p);
-
// bn_jacobi returns the Jacobi symbol of |a| and |b| (which is -1, 0 or 1), or
// -2 on error.
int bn_jacobi(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
@@ -352,8 +359,44 @@ int bn_one_to_montgomery(BIGNUM *r, const BN_MONT_CTX *mont, BN_CTX *ctx);
// value for |mont| and zero otherwise.
int bn_less_than_montgomery_R(const BIGNUM *bn, const BN_MONT_CTX *mont);
+// bn_mod_u16_consttime returns |bn| mod |d|, ignoring |bn|'s sign bit. It runs
+// in time independent of the value of |bn|, but it treats |d| as public.
+OPENSSL_EXPORT uint16_t bn_mod_u16_consttime(const BIGNUM *bn, uint16_t d);
+
+// bn_odd_number_is_obviously_composite returns one if |bn| is divisible by one
+// of the first several odd primes and zero otherwise.
+int bn_odd_number_is_obviously_composite(const BIGNUM *bn);
+
+// bn_rshift1_words sets |r| to |a| >> 1, where both arrays are |num| bits wide.
+void bn_rshift1_words(BN_ULONG *r, const BN_ULONG *a, size_t num);
-// Fixed-width arithmetic.
+// bn_rshift_words sets |r| to |a| >> |shift|, where both arrays are |num| bits
+// wide.
+void bn_rshift_words(BN_ULONG *r, const BN_ULONG *a, unsigned shift,
+ size_t num);
+
+// bn_rshift_secret_shift behaves like |BN_rshift| but runs in time independent
+// of both |a| and |n|.
+OPENSSL_EXPORT int bn_rshift_secret_shift(BIGNUM *r, const BIGNUM *a,
+ unsigned n, BN_CTX *ctx);
+
+// bn_reduce_once sets |r| to |a| mod |m| where 0 <= |a| < 2*|m|. It returns
+// zero if |a| < |m| and a mask of all ones if |a| >= |m|. Each array is |num|
+// words long, but |a| has an additional word specified by |carry|. |carry| must
+// be zero or one, as implied by the bounds on |a|.
+//
+// |r|, |a|, and |m| may not alias. Use |bn_reduce_once_in_place| if |r| and |a|
+// must alias.
+BN_ULONG bn_reduce_once(BN_ULONG *r, const BN_ULONG *a, BN_ULONG carry,
+ const BN_ULONG *m, size_t num);
+
+// bn_reduce_once_in_place behaves like |bn_reduce_once| but acts in-place on
+// |r|, using |tmp| as scratch space. |r|, |tmp|, and |m| may not alias.
+BN_ULONG bn_reduce_once_in_place(BN_ULONG *r, BN_ULONG carry, const BN_ULONG *m,
+ BN_ULONG *tmp, size_t num);
+
+
+// Constant-time non-modular arithmetic.
//
// The following functions implement non-modular arithmetic in constant-time
// and pessimally set |r->width| to the largest possible word size.
@@ -362,42 +405,102 @@ int bn_less_than_montgomery_R(const BIGNUM *bn, const BN_MONT_CTX *mont);
// to increase without bound. The corresponding public API functions minimize
// their outputs to avoid regressing calculator consumers.
-// bn_uadd_fixed behaves like |BN_uadd|, but it pessimally sets
+// bn_uadd_consttime behaves like |BN_uadd|, but it pessimally sets
// |r->width| = |a->width| + |b->width| + 1.
-int bn_uadd_fixed(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+int bn_uadd_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+
+// bn_usub_consttime behaves like |BN_usub|, but it pessimally sets
+// |r->width| = |a->width|.
+int bn_usub_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+
+// bn_abs_sub_consttime sets |r| to the absolute value of |a| - |b|, treating
+// both inputs as secret. It returns one on success and zero on error.
+OPENSSL_EXPORT int bn_abs_sub_consttime(BIGNUM *r, const BIGNUM *a,
+ const BIGNUM *b, BN_CTX *ctx);
-// bn_mul_fixed behaves like |BN_mul|, but it rejects negative inputs and
+// bn_mul_consttime behaves like |BN_mul|, but it rejects negative inputs and
// pessimally sets |r->width| to |a->width| + |b->width|, to avoid leaking
// information about |a| and |b|.
-int bn_mul_fixed(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+int bn_mul_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+
+// bn_sqrt_consttime behaves like |BN_sqrt|, but it pessimally sets |r->width|
+// to 2*|a->width|, to avoid leaking information about |a| and |b|.
+int bn_sqr_consttime(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx);
+
+// bn_div_consttime behaves like |BN_div|, but it rejects negative inputs and
+// treats both inputs, including their magnitudes, as secret. It is, as a
+// result, much slower than |BN_div| and should only be used for rare operations
+// where Montgomery reduction is not available.
+//
+// Note that |quotient->width| will be set pessimally to |numerator->width|.
+OPENSSL_EXPORT int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder,
+ const BIGNUM *numerator,
+ const BIGNUM *divisor, BN_CTX *ctx);
-// bn_sqrt_fixed behaves like |BN_sqrt|, but it pessimally sets |r->width| to
-// 2*|a->width|, to avoid leaking information about |a| and |b|.
-int bn_sqr_fixed(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx);
+// bn_is_relatively_prime checks whether GCD(|x|, |y|) is one. On success, it
+// returns one and sets |*out_relatively_prime| to one if the GCD was one and
+// zero otherwise. On error, it returns zero.
+OPENSSL_EXPORT int bn_is_relatively_prime(int *out_relatively_prime,
+ const BIGNUM *x, const BIGNUM *y,
+ BN_CTX *ctx);
+
+// bn_lcm_consttime sets |r| to LCM(|a|, |b|). It returns one and success and
+// zero on error. |a| and |b| are both treated as secret.
+OPENSSL_EXPORT int bn_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+ BN_CTX *ctx);
// Constant-time modular arithmetic.
//
-// The following functions implement basic constant-time modular arithemtic on
-// word arrays.
+// The following functions implement basic constant-time modular arithmetic.
+
+// bn_mod_add_words sets |r| to |a| + |b| (mod |m|), using |tmp| as scratch
+// space. Each array is |num| words long. |a| and |b| must be < |m|. Any pair of
+// |r|, |a|, and |b| may alias.
+void bn_mod_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+ const BN_ULONG *m, BN_ULONG *tmp, size_t num);
-// bn_mod_add_quick_ctx acts like |BN_mod_add_quick| but takes a |BN_CTX|.
-int bn_mod_add_quick_ctx(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+// bn_mod_add_consttime acts like |BN_mod_add_quick| but takes a |BN_CTX|.
+int bn_mod_add_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m, BN_CTX *ctx);
-// bn_mod_sub_quick_ctx acts like |BN_mod_sub_quick| but takes a |BN_CTX|.
-int bn_mod_sub_quick_ctx(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+// bn_mod_sub_consttime acts like |BN_mod_sub_quick| but takes a |BN_CTX|.
+int bn_mod_sub_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
const BIGNUM *m, BN_CTX *ctx);
-// bn_mod_lshift1_quick_ctx acts like |BN_mod_lshift1_quick| but takes a
+// bn_mod_lshift1_consttime acts like |BN_mod_lshift1_quick| but takes a
// |BN_CTX|.
-int bn_mod_lshift1_quick_ctx(BIGNUM *r, const BIGNUM *a, const BIGNUM *m,
+int bn_mod_lshift1_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *m,
BN_CTX *ctx);
-// bn_mod_lshift_quick_ctx acts like |BN_mod_lshift_quick| but takes a |BN_CTX|.
-int bn_mod_lshift_quick_ctx(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
+// bn_mod_lshift_consttime acts like |BN_mod_lshift_quick| but takes a |BN_CTX|.
+int bn_mod_lshift_consttime(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m,
BN_CTX *ctx);
+// bn_mod_inverse_consttime sets |r| to |a|^-1, mod |n|. |a| must be non-
+// negative and less than |n|. It returns one on success and zero on error. On
+// failure, if the failure was caused by |a| having no inverse mod |n| then
+// |*out_no_inverse| will be set to one; otherwise it will be set to zero.
+//
+// This function treats both |a| and |n| as secret, provided they are both non-
+// zero and the inverse exists. It should only be used for even moduli where
+// none of the less general implementations are applicable.
+OPENSSL_EXPORT int bn_mod_inverse_consttime(BIGNUM *r, int *out_no_inverse,
+ const BIGNUM *a, const BIGNUM *n,
+ BN_CTX *ctx);
+
+// bn_mod_inverse_prime sets |out| to the modular inverse of |a| modulo |p|,
+// computed with Fermat's Little Theorem. It returns one on success and zero on
+// error. If |mont_p| is NULL, one will be computed temporarily.
+int bn_mod_inverse_prime(BIGNUM *out, const BIGNUM *a, const BIGNUM *p,
+ BN_CTX *ctx, const BN_MONT_CTX *mont_p);
+
+// bn_mod_inverse_secret_prime behaves like |bn_mod_inverse_prime| but uses
+// |BN_mod_exp_mont_consttime| instead of |BN_mod_exp_mont| in hopes of
+// protecting the exponent.
+int bn_mod_inverse_secret_prime(BIGNUM *out, const BIGNUM *a, const BIGNUM *p,
+ BN_CTX *ctx, const BN_MONT_CTX *mont_p);
+
// Low-level operations for small numbers.
//
diff --git a/src/crypto/fipsmodule/bn/montgomery.c b/src/crypto/fipsmodule/bn/montgomery.c
index c21a0309..7ce8c4c3 100644
--- a/src/crypto/fipsmodule/bn/montgomery.c
+++ b/src/crypto/fipsmodule/bn/montgomery.c
@@ -289,18 +289,7 @@ static int bn_from_montgomery_in_place(BN_ULONG *r, size_t num_r, BN_ULONG *a,
a += num_n;
// |a| thus requires at most one additional subtraction |n| to be reduced.
- // Subtract |n| and select the answer in constant time.
- OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
- crypto_word_t_too_small);
- BN_ULONG v = bn_sub_words(r, a, n, num_n) - carry;
- // |v| is one if |a| - |n| underflowed or zero if it did not. Note |v| cannot
- // be -1. That would imply the subtraction did not fit in |num_n| words, and
- // we know at most one subtraction is needed.
- v = 0u - v;
- for (size_t i = 0; i < num_n; i++) {
- r[i] = constant_time_select_w(v, a[i], r[i]);
- a[i] = 0;
- }
+ bn_reduce_once(r, a, carry, n, num_n);
return 1;
}
@@ -381,11 +370,11 @@ static int bn_mod_mul_montgomery_fallback(BIGNUM *r, const BIGNUM *a,
}
if (a == b) {
- if (!bn_sqr_fixed(tmp, a, ctx)) {
+ if (!bn_sqr_consttime(tmp, a, ctx)) {
goto err;
}
} else {
- if (!bn_mul_fixed(tmp, a, b, ctx)) {
+ if (!bn_mul_consttime(tmp, a, b, ctx)) {
goto err;
}
}
diff --git a/src/crypto/fipsmodule/bn/montgomery_inv.c b/src/crypto/fipsmodule/bn/montgomery_inv.c
index 15e62e4f..a920ca41 100644
--- a/src/crypto/fipsmodule/bn/montgomery_inv.c
+++ b/src/crypto/fipsmodule/bn/montgomery_inv.c
@@ -177,7 +177,7 @@ int bn_mod_exp_base_2_consttime(BIGNUM *r, unsigned p, const BIGNUM *n,
// Set |r| to the larger power of two smaller than |n|, then shift with
// reductions the rest of the way.
if (!BN_set_bit(r, n_bits - 1) ||
- !bn_mod_lshift_quick_ctx(r, r, p - (n_bits - 1), n, ctx)) {
+ !bn_mod_lshift_consttime(r, r, p - (n_bits - 1), n, ctx)) {
return 0;
}
diff --git a/src/crypto/fipsmodule/bn/mul.c b/src/crypto/fipsmodule/bn/mul.c
index 352b7e5f..4a0711d8 100644
--- a/src/crypto/fipsmodule/bn/mul.c
+++ b/src/crypto/fipsmodule/bn/mul.c
@@ -306,6 +306,24 @@ static BN_ULONG bn_abs_sub_part_words(BN_ULONG *r, const BN_ULONG *a,
return borrow;
}
+int bn_abs_sub_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+ BN_CTX *ctx) {
+ int cl = a->width < b->width ? a->width : b->width;
+ int dl = a->width - b->width;
+ int r_len = a->width < b->width ? b->width : a->width;
+ BN_CTX_start(ctx);
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ int ok = tmp != NULL &&
+ bn_wexpand(r, r_len) &&
+ bn_wexpand(tmp, r_len);
+ if (ok) {
+ bn_abs_sub_part_words(r->d, a->d, b->d, cl, dl, tmp->d);
+ r->width = r_len;
+ }
+ BN_CTX_end(ctx);
+ return ok;
+}
+
// Karatsuba recursive multiplication algorithm
// (cf. Knuth, The Art of Computer Programming, Vol. 2)
@@ -523,9 +541,9 @@ static void bn_mul_part_recursive(BN_ULONG *r, const BN_ULONG *a,
assert(c == 0);
}
-// bn_mul_impl implements |BN_mul| and |bn_mul_fixed|. Note this function breaks
-// |BIGNUM| invariants and may return a negative zero. This is handled by the
-// callers.
+// bn_mul_impl implements |BN_mul| and |bn_mul_consttime|. Note this function
+// breaks |BIGNUM| invariants and may return a negative zero. This is handled by
+// the callers.
static int bn_mul_impl(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
BN_CTX *ctx) {
int al = a->width;
@@ -628,7 +646,7 @@ int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
return 1;
}
-int bn_mul_fixed(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
+int bn_mul_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
// Prevent negative zeros.
if (a->neg || b->neg) {
OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
@@ -773,7 +791,7 @@ int BN_mul_word(BIGNUM *bn, BN_ULONG w) {
return 1;
}
-int bn_sqr_fixed(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) {
+int bn_sqr_consttime(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) {
int al = a->width;
if (al <= 0) {
r->width = 0;
@@ -832,7 +850,7 @@ err:
}
int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) {
- if (!bn_sqr_fixed(r, a, ctx)) {
+ if (!bn_sqr_consttime(r, a, ctx)) {
return 0;
}
diff --git a/src/crypto/fipsmodule/bn/prime.c b/src/crypto/fipsmodule/bn/prime.c
index a291f7a0..d2dfc2c8 100644
--- a/src/crypto/fipsmodule/bn/prime.c
+++ b/src/crypto/fipsmodule/bn/prime.c
@@ -112,6 +112,8 @@
#include <openssl/mem.h>
#include "internal.h"
+#include "../../internal.h"
+
// The quick sieve algorithm approach to weeding out primes is Philip
// Zimmermann's, as implemented in PGP. I have had a read of his comments and
@@ -341,6 +343,67 @@ static int BN_prime_checks_for_size(int bits) {
return 28;
}
+// BN_PRIME_CHECKS_BLINDED is the iteration count for blinding the constant-time
+// primality test. See |BN_primality_test| for details. This number is selected
+// so that, for a candidate N-bit RSA prime, picking |BN_PRIME_CHECKS_BLINDED|
+// random N-bit numbers will have at least |BN_prime_checks_for_size(N)| values
+// in range with high probability.
+//
+// The following Python script computes the blinding factor needed for the
+// corresponding iteration count.
+/*
+import math
+
+# We choose candidate RSA primes between sqrt(2)/2 * 2^N and 2^N and select
+# witnesses by generating random N-bit numbers. Thus the probability of
+# selecting one in range is at least sqrt(2)/2.
+p = math.sqrt(2) / 2
+
+# Target around 2^-8 probability of the blinding being insufficient given that
+# key generation is a one-time, noisy operation.
+epsilon = 2**-8
+
+def choose(a, b):
+ r = 1
+ for i in xrange(b):
+ r *= a - i
+ r /= (i + 1)
+ return r
+
+def failure_rate(min_uniform, iterations):
+ """ Returns the probability that, for |iterations| candidate witnesses, fewer
+ than |min_uniform| of them will be uniform. """
+ prob = 0.0
+ for i in xrange(min_uniform):
+ prob += (choose(iterations, i) *
+ p**i * (1-p)**(iterations - i))
+ return prob
+
+for min_uniform in (3, 4, 5, 6, 8, 13, 19, 28):
+ # Find the smallest number of iterations under the target failure rate.
+ iterations = min_uniform
+ while True:
+ prob = failure_rate(min_uniform, iterations)
+ if prob < epsilon:
+ print min_uniform, iterations, prob
+ break
+ iterations += 1
+
+Output:
+ 3 9 0.00368894873911
+ 4 11 0.00363319494662
+ 5 13 0.00336215573898
+ 6 15 0.00300145783158
+ 8 19 0.00225214119331
+ 13 27 0.00385610026955
+ 19 38 0.0021410539126
+ 28 52 0.00325405801769
+
+16 iterations suffices for 400-bit primes and larger (6 uniform samples needed),
+which is already well below the minimum acceptable key size for RSA.
+*/
+#define BN_PRIME_CHECKS_BLINDED 16
+
static int probable_prime(BIGNUM *rnd, int bits);
static int probable_prime_dh(BIGNUM *rnd, int bits, const BIGNUM *add,
const BIGNUM *rem, BN_CTX *ctx);
@@ -461,81 +524,301 @@ err:
return found;
}
-int BN_primality_test(int *is_probably_prime, const BIGNUM *candidate,
- int checks, BN_CTX *ctx, int do_trial_division,
- BN_GENCB *cb) {
- switch (BN_is_prime_fasttest_ex(candidate, checks, ctx, do_trial_division, cb)) {
- case 1:
- *is_probably_prime = 1;
- return 1;
- case 0:
- *is_probably_prime = 0;
+// The following functions use a Barrett reduction variant to avoid leaking the
+// numerator. See http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html
+//
+// We use 32-bit numerator and 16-bit divisor for simplicity. This allows
+// computing |m| and |q| without architecture-specific code.
+
+// mod_u16 returns |n| mod |d|. |p| and |m| are the "magic numbers" for |d| (see
+// reference). For proof of correctness in Coq, see
+// https://github.com/davidben/fiat-crypto/blob/barrett/src/Arithmetic/BarrettReduction/RidiculousFish.v
+// Note the Coq version of |mod_u16| additionally includes the computation of
+// |p| and |m| from |bn_mod_u16_consttime| below.
+static uint16_t mod_u16(uint32_t n, uint16_t d, uint32_t p, uint32_t m) {
+ // Compute floor(n/d) per steps 3 through 5.
+ uint32_t q = ((uint64_t)m * n) >> 32;
+ // Note there is a typo in the reference. We right-shift by one, not two.
+ uint32_t t = ((n - q) >> 1) + q;
+ t = t >> (p - 1);
+
+ // Multiply and subtract to get the remainder.
+ n -= d * t;
+ assert(n < d);
+ return n;
+}
+
+// shift_and_add_mod_u16 returns |r| * 2^32 + |a| mod |d|. |p| and |m| are the
+// "magic numbers" for |d| (see reference).
+static uint16_t shift_and_add_mod_u16(uint16_t r, uint32_t a, uint16_t d,
+ uint32_t p, uint32_t m) {
+ // Incorporate |a| in two 16-bit chunks.
+ uint32_t t = r;
+ t <<= 16;
+ t |= a >> 16;
+ t = mod_u16(t, d, p, m);
+
+ t <<= 16;
+ t |= a & 0xffff;
+ t = mod_u16(t, d, p, m);
+ return t;
+}
+
+uint16_t bn_mod_u16_consttime(const BIGNUM *bn, uint16_t d) {
+ if (d <= 1) {
+ return 0;
+ }
+
+ // Compute the "magic numbers" for |d|. See steps 1 and 2.
+ // This computes p = ceil(log_2(d)).
+ uint32_t p = BN_num_bits_word(d - 1);
+ // This operation is not constant-time, but |p| and |d| are public values.
+ // Note that |p| is at most 16, so the computation fits in |uint64_t|.
+ assert(p <= 16);
+ uint32_t m = ((UINT64_C(1) << (32 + p)) + d - 1) / d;
+
+ uint16_t ret = 0;
+ for (int i = bn->width - 1; i >= 0; i--) {
+#if BN_BITS2 == 32
+ ret = shift_and_add_mod_u16(ret, bn->d[i], d, p, m);
+#elif BN_BITS2 == 64
+ ret = shift_and_add_mod_u16(ret, bn->d[i] >> 32, d, p, m);
+ ret = shift_and_add_mod_u16(ret, bn->d[i] & 0xffffffff, d, p, m);
+#else
+#error "Unknown BN_ULONG size"
+#endif
+ }
+ return ret;
+}
+
+static int bn_trial_division(uint16_t *out, const BIGNUM *bn) {
+ for (int i = 1; i < NUMPRIMES; i++) {
+ if (bn_mod_u16_consttime(bn, primes[i]) == 0) {
+ *out = primes[i];
return 1;
- default:
- *is_probably_prime = 0;
- return 0;
+ }
}
+ return 0;
}
-int BN_is_prime_ex(const BIGNUM *candidate, int checks, BN_CTX *ctx, BN_GENCB *cb) {
- return BN_is_prime_fasttest_ex(candidate, checks, ctx, 0, cb);
+int bn_odd_number_is_obviously_composite(const BIGNUM *bn) {
+ uint16_t prime;
+ return bn_trial_division(&prime, bn) && !BN_is_word(bn, prime);
}
-int BN_is_prime_fasttest_ex(const BIGNUM *a, int checks, BN_CTX *ctx,
- int do_trial_division, BN_GENCB *cb) {
- if (BN_cmp(a, BN_value_one()) <= 0) {
- return 0;
+int BN_primality_test(int *is_probably_prime, const BIGNUM *w,
+ int iterations, BN_CTX *ctx, int do_trial_division,
+ BN_GENCB *cb) {
+ *is_probably_prime = 0;
+
+ // To support RSA key generation, this function should treat |w| as secret if
+ // it is a large prime. Composite numbers are discarded, so they may return
+ // early.
+
+ if (BN_cmp(w, BN_value_one()) <= 0) {
+ return 1;
}
- // first look for small factors
- if (!BN_is_odd(a)) {
- // a is even => a is prime if and only if a == 2
- return BN_is_word(a, 2);
+ if (!BN_is_odd(w)) {
+ // The only even prime is two.
+ *is_probably_prime = BN_is_word(w, 2);
+ return 1;
}
- // Enhanced Miller-Rabin does not work for three.
- if (BN_is_word(a, 3)) {
+ // Miller-Rabin does not work for three.
+ if (BN_is_word(w, 3)) {
+ *is_probably_prime = 1;
return 1;
}
if (do_trial_division) {
- for (int i = 1; i < NUMPRIMES; i++) {
- BN_ULONG mod = BN_mod_word(a, primes[i]);
- if (mod == (BN_ULONG)-1) {
- return -1;
- }
- if (mod == 0) {
- return BN_is_word(a, primes[i]);
- }
+ // Perform additional trial division checks to discard small primes.
+ uint16_t prime;
+ if (bn_trial_division(&prime, w)) {
+ *is_probably_prime = BN_is_word(w, prime);
+ return 1;
}
-
if (!BN_GENCB_call(cb, 1, -1)) {
- return -1;
+ return 0;
}
}
- int ret = -1;
- BN_CTX *ctx_allocated = NULL;
+ if (iterations == BN_prime_checks) {
+ iterations = BN_prime_checks_for_size(BN_num_bits(w));
+ }
+
+ BN_CTX *new_ctx = NULL;
if (ctx == NULL) {
- ctx_allocated = BN_CTX_new();
- if (ctx_allocated == NULL) {
- return -1;
+ new_ctx = BN_CTX_new();
+ if (new_ctx == NULL) {
+ return 0;
}
- ctx = ctx_allocated;
+ ctx = new_ctx;
}
- enum bn_primality_result_t result;
- if (!BN_enhanced_miller_rabin_primality_test(&result, a, checks, ctx, cb)) {
+ // See C.3.1 from FIPS 186-4.
+ int ret = 0;
+ BN_MONT_CTX *mont = NULL;
+ BN_CTX_start(ctx);
+ BIGNUM *w1 = BN_CTX_get(ctx);
+ if (w1 == NULL ||
+ !bn_usub_consttime(w1, w, BN_value_one())) {
goto err;
}
- ret = (result == bn_probably_prime);
+ // Write w1 as m * 2^a (Steps 1 and 2).
+ int w_len = BN_num_bits(w);
+ int a = BN_count_low_zero_bits(w1);
+ BIGNUM *m = BN_CTX_get(ctx);
+ if (m == NULL ||
+ !bn_rshift_secret_shift(m, w1, a, ctx)) {
+ goto err;
+ }
+
+ // Montgomery setup for computations mod w. Additionally, compute 1 and w - 1
+ // in the Montgomery domain for later comparisons.
+ BIGNUM *b = BN_CTX_get(ctx);
+ BIGNUM *z = BN_CTX_get(ctx);
+ BIGNUM *one_mont = BN_CTX_get(ctx);
+ BIGNUM *w1_mont = BN_CTX_get(ctx);
+ mont = BN_MONT_CTX_new_for_modulus(w, ctx);
+ if (b == NULL || z == NULL || one_mont == NULL || w1_mont == NULL ||
+ mont == NULL ||
+ !bn_one_to_montgomery(one_mont, mont, ctx) ||
+ // w - 1 is -1 mod w, so we can compute it in the Montgomery domain, -R,
+ // with a subtraction. (|one_mont| cannot be zero.)
+ !bn_usub_consttime(w1_mont, w, one_mont)) {
+ goto err;
+ }
+
+ // The following loop performs in inner iteration of the Miller-Rabin
+ // Primality test (Step 4).
+ //
+ // The algorithm as specified in FIPS 186-4 leaks information on |w|, the RSA
+ // private key. Instead, we run through each iteration unconditionally,
+ // performing modular multiplications, masking off any effects to behave
+ // equivalently to the specified algorithm.
+ //
+ // We also blind the number of values of |b| we try. Steps 4.1–4.2 say to
+ // discard out-of-range values. To avoid leaking information on |w|, we use
+ // |bn_rand_secret_range| which, rather than discarding bad values, adjusts
+ // them to be in range. Though not uniformly selected, these adjusted values
+ // are still usable as Rabin-Miller checks.
+ //
+ // Rabin-Miller is already probabilistic, so we could reach the desired
+ // confidence levels by just suitably increasing the iteration count. However,
+ // to align with FIPS 186-4, we use a more pessimal analysis: we do not count
+ // the non-uniform values towards the iteration count. As a result, this
+ // function is more complex and has more timing risk than necessary.
+ //
+ // We count both total iterations and uniform ones and iterate until we've
+ // reached at least |BN_PRIME_CHECKS_BLINDED| and |iterations|, respectively.
+ // If the latter is large enough, it will be the limiting factor with high
+ // probability and we won't leak information.
+ //
+ // Note this blinding does not impact most calls when picking primes because
+ // composites are rejected early. Only the two secret primes see extra work.
+
+ crypto_word_t uniform_iterations = 0;
+ // Using |constant_time_lt_w| seems to prevent the compiler from optimizing
+ // this into two jumps.
+ for (int i = 1; (i <= BN_PRIME_CHECKS_BLINDED) |
+ constant_time_lt_w(uniform_iterations, iterations);
+ i++) {
+ int is_uniform;
+ if (// Step 4.1-4.2
+ !bn_rand_secret_range(b, &is_uniform, 2, w1) ||
+ // Step 4.3
+ !BN_mod_exp_mont_consttime(z, b, m, w, ctx, mont)) {
+ goto err;
+ }
+ uniform_iterations += is_uniform;
+
+ // loop_done is all ones if the loop has completed and all zeros otherwise.
+ crypto_word_t loop_done = 0;
+ // next_iteration is all ones if we should continue to the next iteration
+ // (|b| is not a composite witness for |w|). This is equivalent to going to
+ // step 4.7 in the original algorithm.
+ crypto_word_t next_iteration = 0;
+
+ // Step 4.4. If z = 1 or z = w-1, mask off the loop and continue to the next
+ // iteration (go to step 4.7).
+ loop_done = BN_equal_consttime(z, BN_value_one()) |
+ BN_equal_consttime(z, w1);
+ loop_done = 0 - loop_done; // Make it all zeros or all ones.
+ next_iteration = loop_done; // Go to step 4.7 if |loop_done|.
+
+ // Step 4.5. We use Montgomery-encoding for better performance and to avoid
+ // timing leaks.
+ if (!BN_to_montgomery(z, z, mont, ctx)) {
+ goto err;
+ }
+
+ // To avoid leaking |a|, we run the loop to |w_len| and mask off all
+ // iterations once |j| = |a|.
+ for (int j = 1; j < w_len; j++) {
+ loop_done |= constant_time_eq_int(j, a);
+
+ // Step 4.5.1.
+ if (!BN_mod_mul_montgomery(z, z, z, mont, ctx)) {
+ goto err;
+ }
+
+ // Step 4.5.2. If z = w-1 and the loop is not done, run through the next
+ // iteration.
+ crypto_word_t z_is_w1_mont = BN_equal_consttime(z, w1_mont) & ~loop_done;
+ z_is_w1_mont = 0 - z_is_w1_mont; // Make it all zeros or all ones.
+ loop_done |= z_is_w1_mont;
+ next_iteration |= z_is_w1_mont; // Go to step 4.7 if |z_is_w1_mont|.
+
+ // Step 4.5.3. If z = 1 and the loop is not done, w is composite and we
+ // may exit in variable time.
+ if (BN_equal_consttime(z, one_mont) & ~loop_done) {
+ assert(!next_iteration);
+ break;
+ }
+ }
+
+ if (!next_iteration) {
+ // Step 4.6. We did not see z = w-1 before z = 1, so w must be composite.
+ // (For any prime, the value of z immediately preceding 1 must be -1.
+ // There are no non-trivial square roots of 1 modulo a prime.)
+ *is_probably_prime = 0;
+ ret = 1;
+ goto err;
+ }
+
+ // Step 4.7
+ if (!BN_GENCB_call(cb, 1, i)) {
+ goto err;
+ }
+ }
+
+ assert(uniform_iterations >= (crypto_word_t)iterations);
+ *is_probably_prime = 1;
+ ret = 1;
err:
- BN_CTX_free(ctx_allocated);
+ BN_MONT_CTX_free(mont);
+ BN_CTX_end(ctx);
+ BN_CTX_free(new_ctx);
return ret;
}
+int BN_is_prime_ex(const BIGNUM *candidate, int checks, BN_CTX *ctx, BN_GENCB *cb) {
+ return BN_is_prime_fasttest_ex(candidate, checks, ctx, 0, cb);
+}
+
+int BN_is_prime_fasttest_ex(const BIGNUM *a, int checks, BN_CTX *ctx,
+ int do_trial_division, BN_GENCB *cb) {
+ int is_probably_prime;
+ if (!BN_primality_test(&is_probably_prime, a, checks, ctx, do_trial_division,
+ cb)) {
+ return -1;
+ }
+ return is_probably_prime;
+}
+
int BN_enhanced_miller_rabin_primality_test(
enum bn_primality_result_t *out_result, const BIGNUM *w, int iterations,
BN_CTX *ctx, BN_GENCB *cb) {
@@ -585,7 +868,7 @@ int BN_enhanced_miller_rabin_primality_test(
goto err;
}
- // Montgomery setup for computations mod A
+ // Montgomery setup for computations mod w
mont = BN_MONT_CTX_new_for_modulus(w, ctx);
if (mont == NULL) {
goto err;
@@ -689,11 +972,7 @@ again:
// we now have a random number 'rnd' to test.
for (i = 1; i < NUMPRIMES; i++) {
- BN_ULONG mod = BN_mod_word(rnd, (BN_ULONG)primes[i]);
- if (mod == (BN_ULONG)-1) {
- return 0;
- }
- mods[i] = (uint16_t)mod;
+ mods[i] = bn_mod_u16_consttime(rnd, primes[i]);
}
// If bits is so small that it fits into a single word then we
// additionally don't want to exceed that many bits.
@@ -793,11 +1072,7 @@ static int probable_prime_dh(BIGNUM *rnd, int bits, const BIGNUM *add,
loop:
for (i = 1; i < NUMPRIMES; i++) {
// check that rnd is a prime
- BN_ULONG mod = BN_mod_word(rnd, (BN_ULONG)primes[i]);
- if (mod == (BN_ULONG)-1) {
- goto err;
- }
- if (mod <= 1) {
+ if (bn_mod_u16_consttime(rnd, primes[i]) <= 1) {
if (!BN_add(rnd, rnd, add)) {
goto err;
}
@@ -869,12 +1144,8 @@ loop:
// check that p and q are prime
// check that for p and q
// gcd(p-1,primes) == 1 (except for 2)
- BN_ULONG pmod = BN_mod_word(p, (BN_ULONG)primes[i]);
- BN_ULONG qmod = BN_mod_word(q, (BN_ULONG)primes[i]);
- if (pmod == (BN_ULONG)-1 || qmod == (BN_ULONG)-1) {
- goto err;
- }
- if (pmod == 0 || qmod == 0) {
+ if (bn_mod_u16_consttime(p, primes[i]) == 0 ||
+ bn_mod_u16_consttime(q, primes[i]) == 0) {
if (!BN_add(p, p, padd)) {
goto err;
}
diff --git a/src/crypto/fipsmodule/bn/random.c b/src/crypto/fipsmodule/bn/random.c
index c6f9f089..e41a0efd 100644
--- a/src/crypto/fipsmodule/bn/random.c
+++ b/src/crypto/fipsmodule/bn/random.c
@@ -108,10 +108,10 @@
#include <openssl/bn.h>
+#include <limits.h>
#include <string.h>
#include <openssl/err.h>
-#include <openssl/mem.h>
#include <openssl/rand.h>
#include <openssl/type_check.h>
@@ -121,9 +121,6 @@
int BN_rand(BIGNUM *rnd, int bits, int top, int bottom) {
- uint8_t *buf = NULL;
- int ret = 0, bit, bytes, mask;
-
if (rnd == NULL) {
return 0;
}
@@ -144,63 +141,57 @@ int BN_rand(BIGNUM *rnd, int bits, int top, int bottom) {
return 1;
}
- bytes = (bits + 7) / 8;
- bit = (bits - 1) % 8;
- mask = 0xff << (bit + 1);
-
- buf = OPENSSL_malloc(bytes);
- if (buf == NULL) {
- OPENSSL_PUT_ERROR(BN, ERR_R_MALLOC_FAILURE);
- goto err;
+ if (bits > INT_MAX - (BN_BITS2 - 1)) {
+ OPENSSL_PUT_ERROR(BN, BN_R_BIGNUM_TOO_LONG);
+ return 0;
}
- // Make a random number and set the top and bottom bits.
- RAND_bytes(buf, bytes);
+ int words = (bits + BN_BITS2 - 1) / BN_BITS2;
+ int bit = (bits - 1) % BN_BITS2;
+ const BN_ULONG kOne = 1;
+ const BN_ULONG kThree = 3;
+ BN_ULONG mask = bit < BN_BITS2 - 1 ? (kOne << (bit + 1)) - 1 : BN_MASK2;
+ if (!bn_wexpand(rnd, words)) {
+ return 0;
+ }
+ RAND_bytes((uint8_t *)rnd->d, words * sizeof(BN_ULONG));
+ rnd->d[words - 1] &= mask;
if (top != BN_RAND_TOP_ANY) {
if (top == BN_RAND_TOP_TWO && bits > 1) {
if (bit == 0) {
- buf[0] = 1;
- buf[1] |= 0x80;
+ rnd->d[words - 1] |= 1;
+ rnd->d[words - 2] |= kOne << (BN_BITS2 - 1);
} else {
- buf[0] |= (3 << (bit - 1));
+ rnd->d[words - 1] |= kThree << (bit - 1);
}
} else {
- buf[0] |= (1 << bit);
+ rnd->d[words - 1] |= kOne << bit;
}
}
-
- buf[0] &= ~mask;
-
- // Set the bottom bit if requested,
- if (bottom == BN_RAND_BOTTOM_ODD) {
- buf[bytes - 1] |= 1;
- }
-
- if (!BN_bin2bn(buf, bytes, rnd)) {
- goto err;
+ if (bottom == BN_RAND_BOTTOM_ODD) {
+ rnd->d[0] |= 1;
}
- ret = 1;
-
-err:
- OPENSSL_free(buf);
- return ret;
+ rnd->neg = 0;
+ rnd->width = words;
+ return 1;
}
int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom) {
return BN_rand(rnd, bits, top, bottom);
}
-// bn_less_than_word returns one if the number represented by |len| words at |a|
-// is less than |b| and zero otherwise. It performs this computation in time
-// independent of the value of |a|. |b| is assumed public.
-static int bn_less_than_word(const BN_ULONG *a, size_t len, BN_ULONG b) {
+// bn_less_than_word_mask returns a mask of all ones if the number represented
+// by |len| words at |a| is less than |b| and zero otherwise. It performs this
+// computation in time independent of the value of |a|. |b| is assumed public.
+static crypto_word_t bn_less_than_word_mask(const BN_ULONG *a, size_t len,
+ BN_ULONG b) {
if (b == 0) {
- return 0;
+ return CONSTTIME_FALSE_W;
}
if (len == 0) {
- return 1;
+ return CONSTTIME_TRUE_W;
}
// |a| < |b| iff a[1..len-1] are all zero and a[0] < b.
@@ -213,25 +204,19 @@ static int bn_less_than_word(const BN_ULONG *a, size_t len, BN_ULONG b) {
// |mask| is now zero iff a[1..len-1] are all zero.
mask = constant_time_is_zero_w(mask);
mask &= constant_time_lt_w(a[0], b);
- return constant_time_select_int(mask, 1, 0);
+ return mask;
}
int bn_in_range_words(const BN_ULONG *a, BN_ULONG min_inclusive,
const BN_ULONG *max_exclusive, size_t len) {
- return bn_less_than_words(a, max_exclusive, len) &&
- !bn_less_than_word(a, len, min_inclusive);
+ crypto_word_t mask = ~bn_less_than_word_mask(a, len, min_inclusive);
+ return mask & bn_less_than_words(a, max_exclusive, len);
}
-int bn_rand_range_words(BN_ULONG *out, BN_ULONG min_inclusive,
- const BN_ULONG *max_exclusive, size_t len,
- const uint8_t additional_data[32]) {
- // This function implements the equivalent of steps 4 through 7 of FIPS 186-4
- // appendices B.4.2 and B.5.2. When called in those contexts, |max_exclusive|
- // is n and |min_inclusive| is one.
-
- // Compute the bit length of |max_exclusive| (step 1), in terms of a number of
- // |words| worth of entropy to fill and a mask of bits to clear in the top
- // word.
+static int bn_range_to_mask(size_t *out_words, BN_ULONG *out_mask,
+ size_t min_inclusive, const BN_ULONG *max_exclusive,
+ size_t len) {
+ // The magnitude of |max_exclusive| is assumed public.
size_t words = len;
while (words > 0 && max_exclusive[words - 1] == 0) {
words--;
@@ -252,6 +237,27 @@ int bn_rand_range_words(BN_ULONG *out, BN_ULONG min_inclusive,
mask |= mask >> 32;
#endif
+ *out_words = words;
+ *out_mask = mask;
+ return 1;
+}
+
+int bn_rand_range_words(BN_ULONG *out, BN_ULONG min_inclusive,
+ const BN_ULONG *max_exclusive, size_t len,
+ const uint8_t additional_data[32]) {
+ // This function implements the equivalent of steps 4 through 7 of FIPS 186-4
+ // appendices B.4.2 and B.5.2. When called in those contexts, |max_exclusive|
+ // is n and |min_inclusive| is one.
+
+ // Compute the bit length of |max_exclusive| (step 1), in terms of a number of
+ // |words| worth of entropy to fill and a mask of bits to clear in the top
+ // word.
+ size_t words;
+ BN_ULONG mask;
+ if (!bn_range_to_mask(&words, &mask, min_inclusive, max_exclusive, len)) {
+ return 0;
+ }
+
// Fill any unused words with zero.
OPENSSL_memset(out + words, 0, (len - words) * sizeof(BN_ULONG));
@@ -288,6 +294,44 @@ int BN_rand_range_ex(BIGNUM *r, BN_ULONG min_inclusive,
return 1;
}
+int bn_rand_secret_range(BIGNUM *r, int *out_is_uniform, BN_ULONG min_inclusive,
+ const BIGNUM *max_exclusive) {
+ size_t words;
+ BN_ULONG mask;
+ if (!bn_range_to_mask(&words, &mask, min_inclusive, max_exclusive->d,
+ max_exclusive->width) ||
+ !bn_wexpand(r, words)) {
+ return 0;
+ }
+
+ assert(words > 0);
+ assert(mask != 0);
+ // The range must be large enough for bit tricks to fix invalid values.
+ if (words == 1 && min_inclusive > mask >> 1) {
+ OPENSSL_PUT_ERROR(BN, BN_R_INVALID_RANGE);
+ return 0;
+ }
+
+ // Select a uniform random number with num_bits(max_exclusive) bits.
+ RAND_bytes((uint8_t *)r->d, words * sizeof(BN_ULONG));
+ r->d[words - 1] &= mask;
+
+ // Check, in constant-time, if the value is in range.
+ *out_is_uniform =
+ bn_in_range_words(r->d, min_inclusive, max_exclusive->d, words);
+ crypto_word_t in_range = *out_is_uniform;
+ in_range = 0 - in_range;
+
+ // If the value is not in range, force it to be in range.
+ r->d[0] |= constant_time_select_w(in_range, 0, min_inclusive);
+ r->d[words - 1] &= constant_time_select_w(in_range, BN_MASK2, mask >> 1);
+ assert(bn_in_range_words(r->d, min_inclusive, max_exclusive->d, words));
+
+ r->neg = 0;
+ r->width = words;
+ return 1;
+}
+
int BN_rand_range(BIGNUM *r, const BIGNUM *range) {
return BN_rand_range_ex(r, 0, range);
}
diff --git a/src/crypto/fipsmodule/bn/shift.c b/src/crypto/fipsmodule/bn/shift.c
index d8dfe5f4..ccf7141a 100644
--- a/src/crypto/fipsmodule/bn/shift.c
+++ b/src/crypto/fipsmodule/bn/shift.c
@@ -59,6 +59,7 @@
#include <string.h>
#include <openssl/err.h>
+#include <openssl/type_check.h>
#include "internal.h"
@@ -132,99 +133,88 @@ int BN_lshift1(BIGNUM *r, const BIGNUM *a) {
return 1;
}
-int BN_rshift(BIGNUM *r, const BIGNUM *a, int n) {
- int i, j, nw, lb, rb;
- BN_ULONG *t, *f;
- BN_ULONG l, tmp;
+void bn_rshift_words(BN_ULONG *r, const BN_ULONG *a, unsigned shift,
+ size_t num) {
+ unsigned shift_bits = shift % BN_BITS2;
+ size_t shift_words = shift / BN_BITS2;
+ if (shift_words >= num) {
+ OPENSSL_memset(r, 0, num * sizeof(BN_ULONG));
+ return;
+ }
+ if (shift_bits == 0) {
+ OPENSSL_memmove(r, a + shift_words, (num - shift_words) * sizeof(BN_ULONG));
+ } else {
+ for (size_t i = shift_words; i < num - 1; i++) {
+ r[i - shift_words] =
+ (a[i] >> shift_bits) | (a[i + 1] << (BN_BITS2 - shift_bits));
+ }
+ r[num - 1 - shift_words] = a[num - 1] >> shift_bits;
+ }
+ OPENSSL_memset(r + num - shift_words, 0, shift_words * sizeof(BN_ULONG));
+}
+int BN_rshift(BIGNUM *r, const BIGNUM *a, int n) {
if (n < 0) {
OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
return 0;
}
- int a_width = bn_minimal_width(a);
- nw = n / BN_BITS2;
- rb = n % BN_BITS2;
- lb = BN_BITS2 - rb;
- if (nw >= a_width || a_width == 0) {
- BN_zero(r);
- return 1;
- }
- i = (BN_num_bits(a) - n + (BN_BITS2 - 1)) / BN_BITS2;
- if (r != a) {
- r->neg = a->neg;
- if (!bn_wexpand(r, i)) {
- return 0;
- }
- } else {
- if (n == 0) {
- return 1; // or the copying loop will go berserk
- }
+ if (!bn_wexpand(r, a->width)) {
+ return 0;
}
+ bn_rshift_words(r->d, a->d, n, a->width);
+ r->neg = a->neg;
+ r->width = a->width;
+ bn_set_minimal_width(r);
+ return 1;
+}
- f = &(a->d[nw]);
- t = r->d;
- j = a_width - nw;
- r->width = i;
-
- if (rb == 0) {
- for (i = j; i != 0; i--) {
- *(t++) = *(f++);
- }
- } else {
- l = *(f++);
- for (i = j - 1; i != 0; i--) {
- tmp = l >> rb;
- l = *(f++);
- *(t++) = tmp | (l << lb);
- }
- l >>= rb;
- if (l) {
- *(t) = l;
- }
+int bn_rshift_secret_shift(BIGNUM *r, const BIGNUM *a, unsigned n,
+ BN_CTX *ctx) {
+ int ret = 0;
+ BN_CTX_start(ctx);
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ if (tmp == NULL ||
+ !BN_copy(r, a) ||
+ !bn_wexpand(tmp, r->width)) {
+ goto err;
}
- if (r->width == 0) {
- r->neg = 0;
+ // Shift conditionally by powers of two.
+ unsigned max_bits = BN_BITS2 * r->width;
+ for (unsigned i = 0; (max_bits >> i) != 0; i++) {
+ BN_ULONG mask = (n >> i) & 1;
+ mask = 0 - mask;
+ bn_rshift_words(tmp->d, r->d, 1u << i, r->width);
+ bn_select_words(r->d, mask, tmp->d /* apply shift */,
+ r->d /* ignore shift */, r->width);
}
- return 1;
-}
+ ret = 1;
-int BN_rshift1(BIGNUM *r, const BIGNUM *a) {
- BN_ULONG *ap, *rp, t, c;
- int i, j;
+err:
+ BN_CTX_end(ctx);
+ return ret;
+}
- if (BN_is_zero(a)) {
- BN_zero(r);
- return 1;
- }
- i = bn_minimal_width(a);
- ap = a->d;
- j = i - (ap[i - 1] == 1);
- if (a != r) {
- if (!bn_wexpand(r, j)) {
- return 0;
- }
- r->neg = a->neg;
+void bn_rshift1_words(BN_ULONG *r, const BN_ULONG *a, size_t num) {
+ if (num == 0) {
+ return;
}
- rp = r->d;
- t = ap[--i];
- c = t << (BN_BITS2 - 1);
- if (t >>= 1) {
- rp[i] = t;
- }
- while (i > 0) {
- t = ap[--i];
- rp[i] = (t >> 1) | c;
- c = t << (BN_BITS2 - 1);
+ for (size_t i = 0; i < num - 1; i++) {
+ r[i] = (a[i] >> 1) | (a[i + 1] << (BN_BITS2 - 1));
}
- r->width = j;
+ r[num - 1] = a[num - 1] >> 1;
+}
- if (r->width == 0) {
- r->neg = 0;
+int BN_rshift1(BIGNUM *r, const BIGNUM *a) {
+ if (!bn_wexpand(r, a->width)) {
+ return 0;
}
-
+ bn_rshift1_words(r->d, a->d, a->width);
+ r->width = a->width;
+ r->neg = a->neg;
+ bn_set_minimal_width(r);
return 1;
}
@@ -305,18 +295,70 @@ int BN_mask_bits(BIGNUM *a, int n) {
return 1;
}
+static int bn_count_low_zero_bits_word(BN_ULONG l) {
+ OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small);
+ OPENSSL_COMPILE_ASSERT(sizeof(int) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small_2);
+ OPENSSL_COMPILE_ASSERT(BN_BITS2 == sizeof(BN_ULONG) * 8,
+ bn_ulong_has_padding_bits);
+ // C has very bizarre rules for types smaller than an int.
+ OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) >= sizeof(int),
+ bn_ulong_is_promoted_to_int);
+
+ crypto_word_t mask;
+ int bits = 0;
+
+#if BN_BITS2 > 32
+ // Check if the lower half of |x| are all zero.
+ mask = constant_time_is_zero_w(l << (BN_BITS2 - 32));
+ // If the lower half is all zeros, it is included in the bit count and we
+ // count the upper half. Otherwise, we count the lower half.
+ bits += 32 & mask;
+ l = constant_time_select_w(mask, l >> 32, l);
+#endif
+
+ // The remaining blocks are analogous iterations at lower powers of two.
+ mask = constant_time_is_zero_w(l << (BN_BITS2 - 16));
+ bits += 16 & mask;
+ l = constant_time_select_w(mask, l >> 16, l);
+
+ mask = constant_time_is_zero_w(l << (BN_BITS2 - 8));
+ bits += 8 & mask;
+ l = constant_time_select_w(mask, l >> 8, l);
+
+ mask = constant_time_is_zero_w(l << (BN_BITS2 - 4));
+ bits += 4 & mask;
+ l = constant_time_select_w(mask, l >> 4, l);
+
+ mask = constant_time_is_zero_w(l << (BN_BITS2 - 2));
+ bits += 2 & mask;
+ l = constant_time_select_w(mask, l >> 2, l);
+
+ mask = constant_time_is_zero_w(l << (BN_BITS2 - 1));
+ bits += 1 & mask;
+
+ return bits;
+}
+
int BN_count_low_zero_bits(const BIGNUM *bn) {
+ OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small);
+ OPENSSL_COMPILE_ASSERT(sizeof(int) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small_2);
+
+ int ret = 0;
+ crypto_word_t saw_nonzero = 0;
for (int i = 0; i < bn->width; i++) {
- if (bn->d[i] != 0) {
- int bits = 0;
- for (BN_ULONG w = bn->d[i]; (w & 1) == 0; w >>= 1) {
- bits++;
- }
- return i * BN_BITS2 + bits;
- }
+ crypto_word_t nonzero = ~constant_time_is_zero_w(bn->d[i]);
+ crypto_word_t first_nonzero = ~saw_nonzero & nonzero;
+ saw_nonzero |= nonzero;
+
+ int bits = bn_count_low_zero_bits_word(bn->d[i]);
+ ret |= first_nonzero & (i * BN_BITS2 + bits);
}
- // We got to the end of |bn| and saw no non-zero words. |bn| is zero, so
- // return zero.
- return 0;
+ // If got to the end of |bn| and saw no non-zero words, |bn| is zero. |ret|
+ // will then remain zero.
+ return ret;
}
diff --git a/src/crypto/fipsmodule/bn/sqrt.c b/src/crypto/fipsmodule/bn/sqrt.c
index 852512cd..23417d14 100644
--- a/src/crypto/fipsmodule/bn/sqrt.c
+++ b/src/crypto/fipsmodule/bn/sqrt.c
@@ -184,7 +184,7 @@ BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) {
// November 1992.)
// t := 2*a
- if (!bn_mod_lshift1_quick_ctx(t, A, p, ctx)) {
+ if (!bn_mod_lshift1_consttime(t, A, p, ctx)) {
goto end;
}
diff --git a/src/crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt b/src/crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt
new file mode 100644
index 00000000..d48a0212
--- /dev/null
+++ b/src/crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt
@@ -0,0 +1,3101 @@
+# This file contains multiples of the base point for various curves. The point
+# at infinity is represented as X = 0, Y = 0.
+#
+# This file is generated by make_ec_scalar_base_mult_tests.go
+
+Curve = P-224
+# N = -64
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c29fd
+X = d9174b3ca6b093dee706b10e1d90309aa58aebf6c9006a37f3716fde
+Y = 5091be99fda790ff9e6ecd2ac66b734f157f46402bf194d3bd8c194d
+
+Curve = P-224
+# N = -63
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c29fe
+X = 2e74dd665404a8900c8e3d4f822b7a9b6dcb64940ef5f5671caba7ef
+Y = f58bc45165c62d4c2c2ad7a8fb7e8f03322ce8ea5dc9c29f77625b14
+
+Curve = P-224
+# N = -62
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c29ff
+X = c671c49a40fdb37ee1afb59c55915461d0c4b2a67cbe4f1f0c747c97
+Y = 467fdfe495f8d2f97b00b4f8b83abdf40dc6c1b666fc5edc29225ed6
+
+Curve = P-224
+# N = -61
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a00
+X = 2396b9ee84e06252585475f54b390553185c05702db27913a80911b8
+Y = 5bb2feab11a9448a5a11ae2b51e4132f0da82d7866b1b971dd85edd2
+
+Curve = P-224
+# N = -60
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a01
+X = 8c64ebb538c109bbd60fd54cf7ff47216d30ecefbac0824c6e50b291
+Y = bfd6736f43c5ebb33959c9ec4444f5ea6c86e645b03dbed955ae402b
+
+Curve = P-224
+# N = -59
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a02
+X = 79fb86be63eed9cf12d44df82123ac91042f888b91b1b916bd3c107e
+Y = 4bac5537dc8a32199840b52e4c4002733b7941c69c711c8248e8e33e
+
+Curve = P-224
+# N = -58
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a03
+X = eb81ac109e13fe579100edba2dd5389945b3fdf247b4036b018acf60
+Y = 778b905f5bd3254728b9105ad7e4c53794201298b40d5fd166a75467
+
+Curve = P-224
+# N = -57
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a04
+X = 868ef00d187fef3010c81e77e214a828b4f9227cf5761d7eed89d916
+Y = dae0eef456786c9592faebd46cf44d711fe16fa66b63bf7e8f70d911
+
+Curve = P-224
+# N = -56
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a05
+X = 7cdfaa27e1972788b9891be32d4491c5a9f7187a05c7d40107b7f0fc
+Y = 46bb23e1eed098c6ac43e6c7e6a48c9e1e9c8169ef82488581f3782a
+
+Curve = P-224
+# N = -55
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a06
+X = a3168f507cc5ca03ec9507ff1fbe5ca00f3a1410948250749639b32a
+Y = 7d83b007949ca192bbd2a691c208fe5e0adacbee0d5bc807cfc44a9e
+
+Curve = P-224
+# N = -54
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a07
+X = e095d7363877c57e22ad1a708b7775ae804cceacecf2e2df16618035
+Y = b58fa951b3d1ce053b38a7cb072e69f64d281efc8cc9f1f42bbfde5e
+
+Curve = P-224
+# N = -53
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a08
+X = df5d3ed85b75fb433d057198debdd036cd9f11f339a4d607eb424932
+Y = a61a1c0ab289b7658439375678b7a2e99b0c292297dbedf22eb912e9
+
+Curve = P-224
+# N = -52
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a09
+X = 6e53e26a8b7b28a7c7a61dd4d53d509514edad71188245929589c788
+Y = f85f23a7c85fd7efc006d3eb13480eb0f6f647fdea5b59d06366d558
+
+Curve = P-224
+# N = -51
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a0a
+X = 5b4ce687825f6a00f83cd3bbc77c67dc14d91bd78d4e47f7e2ce7b0f
+Y = 6a86b2451d4be8409dec03799f680c806bc355e798591857fb8eddd6
+
+Curve = P-224
+# N = -50
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a0b
+X = bce605150a1d4d750c5a043fb4136726f99b4a41f35d3b3832ea583f
+Y = 768e2427050ad575667f8784b7fe8c6b2ae7873a7ff11ded64a13b0c
+
+Curve = P-224
+# N = -49
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a0c
+X = f18721e462d2340c4a88e00130d86691386ba2a83d1fb1dc8b927cca
+Y = b31f4d99a118d1c2c19f0815a89a921305d8d52005b64dd249b6a8e9
+
+Curve = P-224
+# N = -48
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a0d
+X = f087c8764bb082e669a8afbc5db571971898ccc2c5d4baf73cd35e9d
+Y = 72edb9b8154237917e7a05581d1cb2048d4d31c4ab90d005c9b67e4a
+
+Curve = P-224
+# N = -47
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a0e
+X = eca965fd046c7fd242e29ba1a178b71b1ec6e7af6a5b88232a285c92
+Y = 3e526a6b5ecbd24f9308de03fdb757a51a564ebc5872dbef7886cc7c
+
+Curve = P-224
+# N = -46
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a0f
+X = 71f8a2cdf405c7ee499dbd7216a07e5aa61b8faa4fd20b516d2761d4
+Y = 7a3008d5e50050b0ab427b36d15de75c0c190f7eb0b6a130106354f7
+
+Curve = P-224
+# N = -45
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a10
+X = cc96733b052b2f04f1cebb4cb8afb448a21c09821d6288b86cb8a17a
+Y = 159e86c0c38e8f7fc210036054941444c90054fd2047a4eb0dbdbc6e
+
+Curve = P-224
+# N = -44
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a11
+X = d20981b43d053ca0ca30994a5586e7f2342c479b07c6c367d0025900
+Y = 572b87044e041001d988793e9ae35378f7b9121a0d7abc1941b7cf8d
+
+Curve = P-224
+# N = -43
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a12
+X = 2f9a6dce655ee5d9f00b398e756defe1499b98df1e2edac8a784ad75
+Y = bd851fc17271dca923b803a4a4554a949bfc20f14f26feacc2649762
+
+Curve = P-224
+# N = -42
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a13
+X = 0a8b3acff07b4e49ed1d6cd0e8cebbb0ac9f5bc5ec7d65e0adf7b21c
+Y = 6aea047a1d6c9ea9df67e58538a8c88be591728e6c0d1443063199a4
+
+Curve = P-224
+# N = -41
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a14
+X = c772baaa90a968d497e0adbf116453e4f8c21c0acbea0ee34502317c
+Y = de20e75207355906ed957ac40260148fda74b9acf699fa06caf08a62
+
+Curve = P-224
+# N = -40
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a15
+X = 5e4cdfc6fc36ea0cd69a38a7485a317a0aeded6b5f6cd80072826385
+Y = 14afef7672ca22afe13292524ab55dfeef828e7e1e6abd8aadb9f27f
+
+Curve = P-224
+# N = -39
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a16
+X = 9a89bcce6f9b400618997c1184c5099a154a07954cb15d5c9f4492ca
+Y = f48eba6a110031e81a8e50a0c3e5c141e3a66d12ef040e2cd36c4fcd
+
+Curve = P-224
+# N = -38
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a17
+X = 68eee585a12332d41aef6f91a587bc3ca57329508fcc9f5bb3907516
+Y = df09fa68ba98511870b892e5f59c02792aed884376ecc9b081641901
+
+Curve = P-224
+# N = -37
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a18
+X = 9810b8218ec461154d317a954df66f5f81e51dc07ed7421b17d0b8aa
+Y = e166fda56ccb98bbfa8423bcacbcacc05ddac7e88ff0ce13c805a10a
+
+Curve = P-224
+# N = -36
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a19
+X = 6116976ec4511b1b9ee2b839378ff122c2bdb3f58823a82a68aef8e1
+Y = 99b4990881ca11762f616528685fbf94eb1708fa1ae25f010d070beb
+
+Curve = P-224
+# N = -35
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a1a
+X = da200dcda742573ca097e34bb87b356b84541f765cf38d2bf07471b0
+Y = ef05917f8f13fc686a8a0b6b544b0a1ed3488f1dff476a9a9c7cc19e
+
+Curve = P-224
+# N = -34
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a1b
+X = f8188c7623f76642286b8e9e4d4c5d58b8eaf7265b3b0e816076e7e4
+Y = bc6fc80b788a058da4873e54acc733db09105a6775bfb3faa6c549af
+
+Curve = P-224
+# N = -33
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a1c
+X = 224c4a62c8b1028606603cce1d451409b23d680bb063a8e6875d3b5c
+Y = ca97069c235efeb00a05729df91171d17605320950a1cea2e49fd119
+
+Curve = P-224
+# N = -32
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a1d
+X = b700a5c3bb6379de6caea37ff7a47ca909bec01a603c5ec5e1d1e794
+Y = 39e4c2c2e0968ef8a7cd7cabbfd37d0d2335579e72145109b23ce46a
+
+Curve = P-224
+# N = -31
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a1e
+X = 0ecf1b60836e92b7af968bccd69ff8300d55a42b0e855a4ff3546eab
+Y = 4bd76a3b0d1e95b063d22f890c68ebfd2327e3af12611c8f66bc1d21
+
+Curve = P-224
+# N = -30
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a1f
+X = 599b7e7e639bc335eb891295f0d8f4d8d8c76e588f0767741ab07558
+Y = a5aa7d10418290c0f35d3e4fccd02e4b3bc48ac8a87ad052e4cdcc14
+
+Curve = P-224
+# N = -29
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a20
+X = f63f9d855262e9b691f9bb848c78859508a8c1e6fb3246212e146e5c
+Y = f75d5db787bfb5cb199828b1040e7ccd9a20d198d9f82a81001cf9e4
+
+Curve = P-224
+# N = -28
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a21
+X = 1243a602d84f3ea7cf4a56f86ccb93395c1d609af98d6474d8e7afb1
+Y = 42c598ef4d24cb1f640cafc463a244dc4a26c694bf7b4737c8c6ec7c
+
+Curve = P-224
+# N = -27
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a22
+X = 1989153b5f6636b610854bcc50afc929e914c03da51a4a8239f4865b
+Y = 46c7e1923864a71fbbc324ff6e9b7c842baf5973e6e6d0ed9abd8695
+
+Curve = P-224
+# N = -26
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a23
+X = 263ed2217b1f0a813156e647e87e6cf618b9635c3f910f9d92153b04
+Y = ba1722401bfc9e41088cac3974d5ec7ad91fa0cd95b0a3555a23194a
+
+Curve = P-224
+# N = -25
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a24
+X = f1a7ae364dba054b51dff5d5ce16fb28965fb3777dca1ce3c1ad6ad0
+Y = b15ffa68153924545b397331387cee02f86c97f51fb4d28a172db95d
+
+Curve = P-224
+# N = -24
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a25
+X = affdfabc0525468041b2baf73874ec60762a670562c0248e474456a1
+Y = 00828bfdac88b772536123340be2bf21ccec9cda4e9ba00d50db9e30
+
+Curve = P-224
+# N = -23
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a26
+X = 2da9b6b6ec26e646ceef802c560782026da04bf2f2d196f4bca2d074
+Y = 58d6f33f99ff23e92c8a043b47b66ae89f7d30289b35b16aea963966
+
+Curve = P-224
+# N = -22
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a27
+X = c9bdd86f5fb7fe1e54c6ff9026f20add6c1984f1ef22b80a887af62c
+Y = eec590de16f168bde8d375e5dcc22402db091965c0a5bf7f95cf88f5
+
+Curve = P-224
+# N = -21
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a28
+X = a3d482c3e7eec18c534d4ceb1c9b8cefecb369a68a85a4e826165174
+Y = 9b11ffa67c105561b349cb872408dfdc8daf9071a39d9c38fee98408
+
+Curve = P-224
+# N = -20
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a29
+X = fcc7f2b45df1cd5a3c0c0731ca47a8af75cfb0347e8354eefe782455
+Y = f2a28eefd8b345832116f1e574f2c6b2c895aa8c24941f40d8b80ad1
+
+Curve = P-224
+# N = -19
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a2a
+X = a1e81c04f30ce201c7c9ace785ed44cc33b455a022f2acdbc6cae83c
+Y = 230e093c24f638f533dac6e2b6d01da3b5e7f45429315ca93fb8e634
+
+Curve = P-224
+# N = -18
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a2b
+X = c9ff61b040874c0568479216824a15eab1a838a797d189746226e4cc
+Y = 156729f1a003647030666054e208180f8f7b0df2249e44fba5931fff
+
+Curve = P-224
+# N = -17
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a2c
+X = b8357c3a6ceef288310e17b8bfeff9200846ca8c1942497c484403bc
+Y = 00eb610599f95942df1082e4f9426d086fb9c6231ae8b24933aab5db
+
+Curve = P-224
+# N = -16
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a2d
+X = 0b6ec4fe1777382404ef679997ba8d1cc5cd8e85349259f590c4c66d
+Y = cc662b9bcba6f94ee4ff1c9c10bd6ddd0d138df2d099a282152a4b7f
+
+Curve = P-224
+# N = -15
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a2e
+X = baa4d8635511a7d288aebeedd12ce529ff102c91f97f867e21916bf9
+Y = 6865a0b8a607f0b04b13d1cb0aa992a5a97f5ee8ca1849efb9ed8678
+
+Curve = P-224
+# N = -14
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a2f
+X = a53640c83dc208603ded83e4ecf758f24c357d7cf48088b2ce01e9fa
+Y = 2a7eb328dbe663b5a468b5bc97a040a3745396ba636b964370dc3352
+
+Curve = P-224
+# N = -13
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a30
+X = 34e8e17a430e43289793c383fac9774247b40e9ebd3366981fcfaeca
+Y = dad7e608e380480434ea641cc82c82cbc92801469c8db0204f13489a
+
+Curve = P-224
+# N = -12
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a31
+X = 6e31ee1dc137f81b056752e4deab1443a481033e9b4c93a3044f4f7a
+Y = df82220fc7a4021549165325725f94c3410ddb56c54e161fc9ef62ee
+
+Curve = P-224
+# N = -11
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a32
+X = ef53b6294aca431f0f3c22dc82eb9050324f1d88d377e716448e507c
+Y = df4aefffbf6d1699c930481cd102127c9a3d992048ab05929b6e5927
+
+Curve = P-224
+# N = -10
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a33
+X = aea9e17a306517eb89152aa7096d2c381ec813c51aa880e7bee2c0fd
+Y = c644cf154cc81f5ade49345e541b4d4b5c1adb3eb5c01c14ee949aa2
+
+Curve = P-224
+# N = -9
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a34
+X = 2fdcccfee720a77ef6cb3bfbb447f9383117e3daa4a07e36ed15f78d
+Y = c8e8cd1b0be40b0877cfca1958603122f1e6914f84b7e8e968ae8b9e
+
+Curve = P-224
+# N = -8
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a35
+X = 858e6f9cc6c12c31f5df124aa77767b05c8bc021bd683d2b55571550
+Y = fb9232c15a3bc7673a3a03b0253824c53d0fd1411b1cabe2e187fb87
+
+Curve = P-224
+# N = -7
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a36
+X = db2f6be630e246a5cf7d99b85194b123d487e2d466b94b24a03c3e28
+Y = f0c5cff7ab680d09ee11dae84e9c1072ac48ea2e744b1b7f72fd469e
+
+Curve = P-224
+# N = -6
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a37
+X = 1f2483f82572251fca975fea40db821df8ad82a3c002ee6c57112408
+Y = 76050f3348af2664aac3a8b05281304ebc7a7914c6ad50a4b4eac383
+
+Curve = P-224
+# N = -5
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a38
+X = 31c49ae75bce7807cdff22055d94ee9021fedbb5ab51c57526f011aa
+Y = d817400e8ba9ca13a45f360e3d121eaaeb39af82d6001c8186f5f866
+
+Curve = P-224
+# N = -4
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a39
+X = ae99feebb5d26945b54892092a8aee02912930fa41cd114e40447301
+Y = fb7da7f5f13a43b81774373c879cd32d6934c05fa758eeb14fcfab38
+
+Curve = P-224
+# N = -3
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3a
+X = df1b1d66a551d0d31eff822558b9d2cc75c2180279fe0d08fd896d04
+Y = 5c080fc3522f41bbb3f55a97cfecf21f882ce8cbb1e50ca6e67e56dc
+
+Curve = P-224
+# N = -2
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3b
+X = 706a46dc76dcb76798e60e6d89474788d16dc18032d268fd1a704fa6
+Y = e3d4895843da188fd58fb0567976d7b50359d6b78530c8f62d1b1746
+
+Curve = P-224
+# N = -1
+N = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c
+X = b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21
+Y = 42c89c774a08dc04b3dd201932bc8a5ea5f8b89bbb2a7e667aff81cd
+
+Curve = P-224
+# N = 0
+N = 00000000000000000000000000000000000000000000000000000000
+X = 00000000000000000000000000000000000000000000000000000000
+Y = 00000000000000000000000000000000000000000000000000000000
+
+Curve = P-224
+# N = 1
+N = 00000000000000000000000000000000000000000000000000000001
+X = b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21
+Y = bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34
+
+Curve = P-224
+# N = 2
+N = 00000000000000000000000000000000000000000000000000000002
+X = 706a46dc76dcb76798e60e6d89474788d16dc18032d268fd1a704fa6
+Y = 1c2b76a7bc25e7702a704fa986892849fca629487acf3709d2e4e8bb
+
+Curve = P-224
+# N = 3
+N = 00000000000000000000000000000000000000000000000000000003
+X = df1b1d66a551d0d31eff822558b9d2cc75c2180279fe0d08fd896d04
+Y = a3f7f03cadd0be444c0aa56830130ddf77d317344e1af3591981a925
+
+Curve = P-224
+# N = 4
+N = 00000000000000000000000000000000000000000000000000000004
+X = ae99feebb5d26945b54892092a8aee02912930fa41cd114e40447301
+Y = 0482580a0ec5bc47e88bc8c378632cd196cb3fa058a7114eb03054c9
+
+Curve = P-224
+# N = 5
+N = 00000000000000000000000000000000000000000000000000000005
+X = 31c49ae75bce7807cdff22055d94ee9021fedbb5ab51c57526f011aa
+Y = 27e8bff1745635ec5ba0c9f1c2ede15414c6507d29ffe37e790a079b
+
+Curve = P-224
+# N = 6
+N = 00000000000000000000000000000000000000000000000000000006
+X = 1f2483f82572251fca975fea40db821df8ad82a3c002ee6c57112408
+Y = 89faf0ccb750d99b553c574fad7ecfb0438586eb3952af5b4b153c7e
+
+Curve = P-224
+# N = 7
+N = 00000000000000000000000000000000000000000000000000000007
+X = db2f6be630e246a5cf7d99b85194b123d487e2d466b94b24a03c3e28
+Y = 0f3a30085497f2f611ee2517b163ef8c53b715d18bb4e4808d02b963
+
+Curve = P-224
+# N = 8
+N = 00000000000000000000000000000000000000000000000000000008
+X = 858e6f9cc6c12c31f5df124aa77767b05c8bc021bd683d2b55571550
+Y = 046dcd3ea5c43898c5c5fc4fdac7db39c2f02ebee4e3541d1e78047a
+
+Curve = P-224
+# N = 9
+N = 00000000000000000000000000000000000000000000000000000009
+X = 2fdcccfee720a77ef6cb3bfbb447f9383117e3daa4a07e36ed15f78d
+Y = 371732e4f41bf4f7883035e6a79fcedc0e196eb07b48171697517463
+
+Curve = P-224
+# N = 10
+N = 0000000000000000000000000000000000000000000000000000000a
+X = aea9e17a306517eb89152aa7096d2c381ec813c51aa880e7bee2c0fd
+Y = 39bb30eab337e0a521b6cba1abe4b2b3a3e524c14a3fe3eb116b655f
+
+Curve = P-224
+# N = 11
+N = 0000000000000000000000000000000000000000000000000000000b
+X = ef53b6294aca431f0f3c22dc82eb9050324f1d88d377e716448e507c
+Y = 20b510004092e96636cfb7e32efded8265c266dfb754fa6d6491a6da
+
+Curve = P-224
+# N = 12
+N = 0000000000000000000000000000000000000000000000000000000c
+X = 6e31ee1dc137f81b056752e4deab1443a481033e9b4c93a3044f4f7a
+Y = 207dddf0385bfdeab6e9acda8da06b3bbef224a93ab1e9e036109d13
+
+Curve = P-224
+# N = 13
+N = 0000000000000000000000000000000000000000000000000000000d
+X = 34e8e17a430e43289793c383fac9774247b40e9ebd3366981fcfaeca
+Y = 252819f71c7fb7fbcb159be337d37d3336d7feb963724fdfb0ecb767
+
+Curve = P-224
+# N = 14
+N = 0000000000000000000000000000000000000000000000000000000e
+X = a53640c83dc208603ded83e4ecf758f24c357d7cf48088b2ce01e9fa
+Y = d5814cd724199c4a5b974a43685fbf5b8bac69459c9469bc8f23ccaf
+
+Curve = P-224
+# N = 15
+N = 0000000000000000000000000000000000000000000000000000000f
+X = baa4d8635511a7d288aebeedd12ce529ff102c91f97f867e21916bf9
+Y = 979a5f4759f80f4fb4ec2e34f5566d595680a11735e7b61046127989
+
+Curve = P-224
+# N = 16
+N = 00000000000000000000000000000000000000000000000000000010
+X = 0b6ec4fe1777382404ef679997ba8d1cc5cd8e85349259f590c4c66d
+Y = 3399d464345906b11b00e363ef429221f2ec720d2f665d7dead5b482
+
+Curve = P-224
+# N = 17
+N = 00000000000000000000000000000000000000000000000000000011
+X = b8357c3a6ceef288310e17b8bfeff9200846ca8c1942497c484403bc
+Y = ff149efa6606a6bd20ef7d1b06bd92f6904639dce5174db6cc554a26
+
+Curve = P-224
+# N = 18
+N = 00000000000000000000000000000000000000000000000000000012
+X = c9ff61b040874c0568479216824a15eab1a838a797d189746226e4cc
+Y = ea98d60e5ffc9b8fcf999fab1df7e7ef7084f20ddb61bb045a6ce002
+
+Curve = P-224
+# N = 19
+N = 00000000000000000000000000000000000000000000000000000013
+X = a1e81c04f30ce201c7c9ace785ed44cc33b455a022f2acdbc6cae83c
+Y = dcf1f6c3db09c70acc25391d492fe25b4a180babd6cea356c04719cd
+
+Curve = P-224
+# N = 20
+N = 00000000000000000000000000000000000000000000000000000014
+X = fcc7f2b45df1cd5a3c0c0731ca47a8af75cfb0347e8354eefe782455
+Y = 0d5d7110274cba7cdee90e1a8b0d394c376a5573db6be0bf2747f530
+
+Curve = P-224
+# N = 21
+N = 00000000000000000000000000000000000000000000000000000015
+X = a3d482c3e7eec18c534d4ceb1c9b8cefecb369a68a85a4e826165174
+Y = 64ee005983efaa9e4cb63478dbf7202272506f8e5c6263c701167bf9
+
+Curve = P-224
+# N = 22
+N = 00000000000000000000000000000000000000000000000000000016
+X = c9bdd86f5fb7fe1e54c6ff9026f20add6c1984f1ef22b80a887af62c
+Y = 113a6f21e90e9742172c8a1a233ddbfc24f6e69a3f5a40806a30770c
+
+Curve = P-224
+# N = 23
+N = 00000000000000000000000000000000000000000000000000000017
+X = 2da9b6b6ec26e646ceef802c560782026da04bf2f2d196f4bca2d074
+Y = a7290cc06600dc16d375fbc4b84995166082cfd764ca4e951569c69b
+
+Curve = P-224
+# N = 24
+N = 00000000000000000000000000000000000000000000000000000018
+X = affdfabc0525468041b2baf73874ec60762a670562c0248e474456a1
+Y = ff7d74025377488dac9edccbf41d40dd33136325b1645ff2af2461d1
+
+Curve = P-224
+# N = 25
+N = 00000000000000000000000000000000000000000000000000000019
+X = f1a7ae364dba054b51dff5d5ce16fb28965fb3777dca1ce3c1ad6ad0
+Y = 4ea00597eac6dbaba4c68ccec78311fc0793680ae04b2d75e8d246a4
+
+Curve = P-224
+# N = 26
+N = 0000000000000000000000000000000000000000000000000000001a
+X = 263ed2217b1f0a813156e647e87e6cf618b9635c3f910f9d92153b04
+Y = 45e8ddbfe40361bef77353c68b2a138426e05f326a4f5caaa5dce6b7
+
+Curve = P-224
+# N = 27
+N = 0000000000000000000000000000000000000000000000000000001b
+X = 1989153b5f6636b610854bcc50afc929e914c03da51a4a8239f4865b
+Y = b9381e6dc79b58e0443cdb009164837ad450a68c19192f126542796c
+
+Curve = P-224
+# N = 28
+N = 0000000000000000000000000000000000000000000000000000001c
+X = 1243a602d84f3ea7cf4a56f86ccb93395c1d609af98d6474d8e7afb1
+Y = bd3a6710b2db34e09bf3503b9c5dbb22b5d9396b4084b8c837391385
+
+Curve = P-224
+# N = 29
+N = 0000000000000000000000000000000000000000000000000000001d
+X = f63f9d855262e9b691f9bb848c78859508a8c1e6fb3246212e146e5c
+Y = 08a2a24878404a34e667d74efbf1833165df2e672607d57effe3061d
+
+Curve = P-224
+# N = 30
+N = 0000000000000000000000000000000000000000000000000000001e
+X = 599b7e7e639bc335eb891295f0d8f4d8d8c76e588f0767741ab07558
+Y = 5a5582efbe7d6f3f0ca2c1b0332fd1b3c43b753757852fad1b3233ed
+
+Curve = P-224
+# N = 31
+N = 0000000000000000000000000000000000000000000000000000001f
+X = 0ecf1b60836e92b7af968bccd69ff8300d55a42b0e855a4ff3546eab
+Y = b42895c4f2e16a4f9c2dd076f3971401dcd81c50ed9ee3709943e2e0
+
+Curve = P-224
+# N = 32
+N = 00000000000000000000000000000000000000000000000000000020
+X = b700a5c3bb6379de6caea37ff7a47ca909bec01a603c5ec5e1d1e794
+Y = c61b3d3d1f69710758328354402c82f1dccaa8618debaef64dc31b97
+
+Curve = P-224
+# N = 33
+N = 00000000000000000000000000000000000000000000000000000021
+X = 224c4a62c8b1028606603cce1d451409b23d680bb063a8e6875d3b5c
+Y = 3568f963dca1014ff5fa8d6206ee8e2d89facdf6af5e315d1b602ee8
+
+Curve = P-224
+# N = 34
+N = 00000000000000000000000000000000000000000000000000000022
+X = f8188c7623f76642286b8e9e4d4c5d58b8eaf7265b3b0e816076e7e4
+Y = 439037f48775fa725b78c1ab5338cc23f6efa5988a404c05593ab652
+
+Curve = P-224
+# N = 35
+N = 00000000000000000000000000000000000000000000000000000023
+X = da200dcda742573ca097e34bb87b356b84541f765cf38d2bf07471b0
+Y = 10fa6e8070ec03979575f494abb4f5e02cb770e200b8956563833e63
+
+Curve = P-224
+# N = 36
+N = 00000000000000000000000000000000000000000000000000000024
+X = 6116976ec4511b1b9ee2b839378ff122c2bdb3f58823a82a68aef8e1
+Y = 664b66f77e35ee89d09e9ad797a0406a14e8f705e51da0fef2f8f416
+
+Curve = P-224
+# N = 37
+N = 00000000000000000000000000000000000000000000000000000025
+X = 9810b8218ec461154d317a954df66f5f81e51dc07ed7421b17d0b8aa
+Y = 1e99025a93346744057bdc435343533ea2253817700f31ec37fa5ef7
+
+Curve = P-224
+# N = 38
+N = 00000000000000000000000000000000000000000000000000000026
+X = 68eee585a12332d41aef6f91a587bc3ca57329508fcc9f5bb3907516
+Y = 20f605974567aee78f476d1a0a63fd85d51277bc8913364f7e9be700
+
+Curve = P-224
+# N = 39
+N = 00000000000000000000000000000000000000000000000000000027
+X = 9a89bcce6f9b400618997c1184c5099a154a07954cb15d5c9f4492ca
+Y = 0b714595eeffce17e571af5f3c1a3ebd1c5992ed10fbf1d32c93b034
+
+Curve = P-224
+# N = 40
+N = 00000000000000000000000000000000000000000000000000000028
+X = 5e4cdfc6fc36ea0cd69a38a7485a317a0aeded6b5f6cd80072826385
+Y = eb5010898d35dd501ecd6dadb54aa200107d7181e195427552460d82
+
+Curve = P-224
+# N = 41
+N = 00000000000000000000000000000000000000000000000000000029
+X = c772baaa90a968d497e0adbf116453e4f8c21c0acbea0ee34502317c
+Y = 21df18adf8caa6f9126a853bfd9feb6f258b4653096605f9350f759f
+
+Curve = P-224
+# N = 42
+N = 0000000000000000000000000000000000000000000000000000002a
+X = 0a8b3acff07b4e49ed1d6cd0e8cebbb0ac9f5bc5ec7d65e0adf7b21c
+Y = 9515fb85e293615620981a7ac75737731a6e8d7193f2ebbcf9ce665d
+
+Curve = P-224
+# N = 43
+N = 0000000000000000000000000000000000000000000000000000002b
+X = 2f9a6dce655ee5d9f00b398e756defe1499b98df1e2edac8a784ad75
+Y = 427ae03e8d8e2356dc47fc5b5baab56a6403df0eb0d901533d9b689f
+
+Curve = P-224
+# N = 44
+N = 0000000000000000000000000000000000000000000000000000002c
+X = d20981b43d053ca0ca30994a5586e7f2342c479b07c6c367d0025900
+Y = a8d478fbb1fbeffe267786c1651cac860846ede5f28543e6be483074
+
+Curve = P-224
+# N = 45
+N = 0000000000000000000000000000000000000000000000000000002d
+X = cc96733b052b2f04f1cebb4cb8afb448a21c09821d6288b86cb8a17a
+Y = ea61793f3c7170803deffc9fab6bebba36ffab02dfb85b14f2424393
+
+Curve = P-224
+# N = 46
+N = 0000000000000000000000000000000000000000000000000000002e
+X = 71f8a2cdf405c7ee499dbd7216a07e5aa61b8faa4fd20b516d2761d4
+Y = 85cff72a1affaf4f54bd84c92ea218a2f3e6f0814f495ecfef9cab0a
+
+Curve = P-224
+# N = 47
+N = 0000000000000000000000000000000000000000000000000000002f
+X = eca965fd046c7fd242e29ba1a178b71b1ec6e7af6a5b88232a285c92
+Y = c1ad9594a1342db06cf721fc0248a859e5a9b143a78d241087793385
+
+Curve = P-224
+# N = 48
+N = 00000000000000000000000000000000000000000000000000000030
+X = f087c8764bb082e669a8afbc5db571971898ccc2c5d4baf73cd35e9d
+Y = 8d124647eabdc86e8185faa7e2e34dfa72b2ce3b546f2ffa364981b7
+
+Curve = P-224
+# N = 49
+N = 00000000000000000000000000000000000000000000000000000031
+X = f18721e462d2340c4a88e00130d86691386ba2a83d1fb1dc8b927cca
+Y = 4ce0b2665ee72e3d3e60f7ea57656debfa272adffa49b22db6495718
+
+Curve = P-224
+# N = 50
+N = 00000000000000000000000000000000000000000000000000000032
+X = bce605150a1d4d750c5a043fb4136726f99b4a41f35d3b3832ea583f
+Y = 8971dbd8faf52a8a9980787b48017393d51878c5800ee2129b5ec4f5
+
+Curve = P-224
+# N = 51
+N = 00000000000000000000000000000000000000000000000000000033
+X = 5b4ce687825f6a00f83cd3bbc77c67dc14d91bd78d4e47f7e2ce7b0f
+Y = 95794dbae2b417bf6213fc866097f37e943caa1867a6e7a80471222b
+
+Curve = P-224
+# N = 52
+N = 00000000000000000000000000000000000000000000000000000034
+X = 6e53e26a8b7b28a7c7a61dd4d53d509514edad71188245929589c788
+Y = 07a0dc5837a028103ff92c14ecb7f14e0909b80215a4a62f9c992aa9
+
+Curve = P-224
+# N = 53
+N = 00000000000000000000000000000000000000000000000000000035
+X = df5d3ed85b75fb433d057198debdd036cd9f11f339a4d607eb424932
+Y = 59e5e3f54d76489a7bc6c8a987485d1564f3d6dd6824120dd146ed18
+
+Curve = P-224
+# N = 54
+N = 00000000000000000000000000000000000000000000000000000036
+X = e095d7363877c57e22ad1a708b7775ae804cceacecf2e2df16618035
+Y = 4a7056ae4c2e31fac4c75834f8d19608b2d7e10373360e0bd44021a3
+
+Curve = P-224
+# N = 55
+N = 00000000000000000000000000000000000000000000000000000037
+X = a3168f507cc5ca03ec9507ff1fbe5ca00f3a1410948250749639b32a
+Y = 827c4ff86b635e6d442d596e3df701a0f5253411f2a437f8303bb563
+
+Curve = P-224
+# N = 56
+N = 00000000000000000000000000000000000000000000000000000038
+X = 7cdfaa27e1972788b9891be32d4491c5a9f7187a05c7d40107b7f0fc
+Y = b944dc1e112f673953bc1938195b7360e1637e96107db77a7e0c87d7
+
+Curve = P-224
+# N = 57
+N = 00000000000000000000000000000000000000000000000000000039
+X = 868ef00d187fef3010c81e77e214a828b4f9227cf5761d7eed89d916
+Y = 251f110ba987936a6d05142b930bb28de01e9059949c4081708f26f0
+
+Curve = P-224
+# N = 58
+N = 0000000000000000000000000000000000000000000000000000003a
+X = eb81ac109e13fe579100edba2dd5389945b3fdf247b4036b018acf60
+Y = 88746fa0a42cdab8d746efa5281b3ac76bdfed674bf2a02e9958ab9a
+
+Curve = P-224
+# N = 59
+N = 0000000000000000000000000000000000000000000000000000003b
+X = 79fb86be63eed9cf12d44df82123ac91042f888b91b1b916bd3c107e
+Y = b453aac82375cde667bf4ad1b3bffd8bc486be39638ee37db7171cc3
+
+Curve = P-224
+# N = 60
+N = 0000000000000000000000000000000000000000000000000000003c
+X = 8c64ebb538c109bbd60fd54cf7ff47216d30ecefbac0824c6e50b291
+Y = 40298c90bc3a144cc6a63613bbbb0a14937919ba4fc24126aa51bfd6
+
+Curve = P-224
+# N = 61
+N = 0000000000000000000000000000000000000000000000000000003d
+X = 2396b9ee84e06252585475f54b390553185c05702db27913a80911b8
+Y = a44d0154ee56bb75a5ee51d4ae1beccff257d287994e468e227a122f
+
+Curve = P-224
+# N = 62
+N = 0000000000000000000000000000000000000000000000000000003e
+X = c671c49a40fdb37ee1afb59c55915461d0c4b2a67cbe4f1f0c747c97
+Y = b980201b6a072d0684ff4b0747c5420af2393e499903a123d6dda12b
+
+Curve = P-224
+# N = 63
+N = 0000000000000000000000000000000000000000000000000000003f
+X = 2e74dd665404a8900c8e3d4f822b7a9b6dcb64940ef5f5671caba7ef
+Y = 0a743bae9a39d2b3d3d52857048170fbcdd31715a2363d60889da4ed
+
+Curve = P-224
+# N = 64
+N = 00000000000000000000000000000000000000000000000000000040
+X = d9174b3ca6b093dee706b10e1d90309aa58aebf6c9006a37f3716fde
+Y = af6e416602586f00619132d539948cafea80b9bfd40e6b2c4273e6b4
+
+Curve = P-256
+# N = -64
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632511
+X = 0a0643fb8fcc14def67a6a5eb1bf8e9125b35edc7338d816aa4110a6b90ee785
+Y = aacbc7ccb56186ab3adf25325d6df18ff25ef30018fed128453f2eff79e0633d
+
+Curve = P-256
+# N = -63
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632512
+X = 6a9501d85bf5dc802a1f28a08acc7d8fdf53c8af01a7cd3832a290825d8bdac1
+Y = 359bf52d6cb8c8bd7e39391bb5c3a95c07b9e4a4968590cacf500ac2a0e10e5c
+
+Curve = P-256
+# N = -62
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632513
+X = 571c05c84021edcec4b1ac999829ecd80f8216b239c67f269f88ff57ae8cce2b
+Y = 06cda502e8accde7d204c260cfa0b6650033f3085944d22453fed73d7b547eea
+
+Curve = P-256
+# N = -61
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632514
+X = 059ccb19edd3da9a2d3a6b3d8d9900013e7910a08b724fd55939ac380d32af0e
+Y = 44952812352b6ea7259ad7e46cba9c71e9de085dc6a931326d71e1c368016e2e
+
+Curve = P-256
+# N = -60
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632515
+X = 05dae8c2c5a5afba7e53b9efcac1d0b8224559146918d320879bb82d96ef4963
+Y = 44f85bb1fc3f7ee71d087d2f28fcfb310d6617fe2e2c409da96fdc9fae2cbcdb
+
+Curve = P-256
+# N = -59
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632516
+X = 665f1a6ffe0c6437765b2784fca9bdf7e50941119e8dc8eca2b6ea0e0faa4b45
+Y = b6f1d35a6002e73e917335d608153c8082121a417e1dea4391da599fd480b330
+
+Curve = P-256
+# N = -58
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632517
+X = ec247d216208539a58912acd04d6df1f8b0b3c9affdc599e9e2481f254419b1d
+Y = 35ce35beb4dacb8bb916c4998a6a5a22038ea1cf25ce0a67d46ef9d9c53be021
+
+Curve = P-256
+# N = -57
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632518
+X = c116e30ebb4d2865126d45a8ea907f86289d406e2d6c6bd88abd97b1d0f56077
+Y = 16b877db3ca4cf3e474e92644ec4789a82a42a1861a6373acec028025bef3df9
+
+Curve = P-256
+# N = -56
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632519
+X = 6f9a14fbbcf2815a42e8b595d0d5a0eb956463496f67dc0d60a7520a83fb632e
+Y = 37bd614684bfb0d67ab3d21b3889362462780d4822c6b5a46d28a25d0db130d6
+
+Curve = P-256
+# N = -55
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63251a
+X = 079dba7ba068c9267571a109fe7fea2cc2a595b762c1eadadec1dff7df6e60a0
+Y = 23e1e647bc2b2e7fe2ddc062d56a77547c14d20da8ae5c6804f25a514b7db215
+
+Curve = P-256
+# N = -54
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63251b
+X = 6fc0cd2131b9eb0587c40153ae03fcc8f4b7dbe02cdc59fa4aa8b7d7c3a3dc7d
+Y = 2b4889e69b94faed3f1627c40c89ba9e539cf572caf4517a84f9a4e8ce021d5b
+
+Curve = P-256
+# N = -53
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63251c
+X = 6f01bd49c9d952455a47802254b88039982b1ca78de9b983f126ec9f7449d036
+Y = ea9df7ef0e2a2155ee5f0de59f788931587aae413c8b64f7c9fdcc226761e824
+
+Curve = P-256
+# N = -52
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63251d
+X = 194e35c4ec2f25ef537105d2b2e54c1803eb2d0a04492e3d2e1d72d04b978b18
+Y = 50bd98643eb15868424870e97c3bd6b3971582ef17c2a9643fb601dbfc30b89b
+
+Curve = P-256
+# N = -51
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63251e
+X = 672c4a514d9de43eaadee6863c1d68bc95f7eb56e81008ff044360f0018e22b1
+Y = ab73816d695da4029ee21a5b531dfc08ec9db9a868fb26be66cac66e6e0c8efb
+
+Curve = P-256
+# N = -50
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63251f
+X = ba6821cba9bab3ba57a98127482a5de00c108a6ac3241ebb508c58a24d9edba2
+Y = 77be3a209f3b50c578c8a1387342de1818f57ad7d28814b070de74fc987b1edf
+
+Curve = P-256
+# N = -49
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632520
+X = f785b0e098068875bb22b146866e6c0528fb7ea9758fd4fd7d228ce6a5674455
+Y = 8c024f3f9f7f25921ea38980f260a4beb4fb4903a0c559f51843b6f3ef29dbf7
+
+Curve = P-256
+# N = -48
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632521
+X = 9482fb0e492539ec8cce745be070cda11c2e92960a201a61abfb9dc69e4536ca
+Y = cae26357ba0ea807e5a29c7358acb19c09c2a1d7a8f84044e05279c40a733e37
+
+Curve = P-256
+# N = -47
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632522
+X = 42c315cc48958708595361ea83071bbcdd5b31583e19066d51d689227b1c0d7c
+Y = 9b659e30a8e46a7bd6eb2e204485866f8b0e5e1f14780e9b293b58d44d064e46
+
+Curve = P-256
+# N = -46
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632523
+X = b1bb852cfea701fc42caaa1a5b5eb6a5174e94f87c4d3b0612b46293eb5925f8
+Y = e2cc823893878b35b9fb82df902e1932df8a6872f610e8b087c15e067421c34f
+
+Curve = P-256
+# N = -45
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632524
+X = a891d06670bde99b3ecd0f5ddff0672e0f5f609edd29d6d979c78080fae0ba03
+Y = 4a69326cd3405e3fbe65773bfcc3e318e394c710014f0d33103c1237e996cb51
+
+Curve = P-256
+# N = -44
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632525
+X = 4756686a0d7e11cb04211f7813b498c2170bff9af47aab0e652d0380ac8d4f9b
+Y = 31ccb023ab79c591d003da70cb8b3b149a8aa55ac7b76f4bf17db5677d087a2f
+
+Curve = P-256
+# N = -43
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632526
+X = 986ae2506f1ff104d04230861d8f4b498f4bc4c6d009b30f7544dc129b82d28d
+Y = ffc3333e59b9f1f61cd75b2682c3849e279039d863e760dadaeef3bbe44f8168
+
+Curve = P-256
+# N = -42
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632527
+X = 6780c5fc70275e2c7061a0e7877bb174deadeb9887027f3fa83654158ba7f50c
+Y = c34573ca43ca2df27e08cf53e38429299e56bd07f395635aa3aed061b5ffed99
+
+Curve = P-256
+# N = -41
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632528
+X = 67a6bec240dee0651cf258d2e6cfe8aa6067c5c3d4175a593a7de694995d2fa2
+Y = 2196d48edd2ecea893db64b6b9b2bb66eabd3812df653593b63db31ebbe0112a
+
+Curve = P-256
+# N = -40
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632529
+X = bea01e7daad46131bd5b18584d0ee0c457b44d35ae412c0faa74b4da1aac91e6
+Y = de122b18f6b253fb277d212d7e942509ef0460e6d6922326cb38009bce2ae2e3
+
+Curve = P-256
+# N = -39
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63252a
+X = 22a682f7c3996d4d42014976a179046e547b942dd2d138d4a0c199ddfb2776c4
+Y = b0b9f94eefdddc1246e7367ca5abca93686233cffd9a4f97acb809b63455d7a2
+
+Curve = P-256
+# N = -38
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63252b
+X = 971581bdd1356ea1b317d7b29059611176788153b4d38f8d81155d60576d8c46
+Y = 78f3174f20f2a63f622ad0e179abe7955517bcbae7c0cdac7f387b1cd4313a6d
+
+Curve = P-256
+# N = -37
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63252c
+X = 419a6a646ddb817dd6b0978611a826aae0d21379246bfd4473a92894502b3348
+Y = ccdabb2feefd0a7caba3604e6ab3d02aec392f8e0c211e1d24e2937e4f6deb4d
+
+Curve = P-256
+# N = -36
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63252d
+X = da5bd2d111c3731d14281d73d5e965a384e94cbf9664dc227d2a45b9abb598ef
+Y = 9e9903e5c70f93acddb9ad84f8a206dd44ea6da8e84bd95b6dccc3dbdfbb51a0
+
+Curve = P-256
+# N = -35
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63252e
+X = d58d4a589ed27d168ffa3ad7326c48ca94e8e1fe92af9700a12d389033bb291a
+Y = 2baaeb2dfd8d947b89156d9cd238010998d8e3eac4fcb6865a4f36390a79462a
+
+Curve = P-256
+# N = -34
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63252f
+X = 2f9e6ebf717def118d1a092fce97133919cf2d31b7f8be6cfb7fdbe16820999e
+Y = 85115526acd077df1c34e5eb2f0adb09e97d2d6bf51215f28a9477fcf941f4d0
+
+Curve = P-256
+# N = -33
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632530
+X = 9807d699fcd81356fa9aa25b89d9d34ea03b0a533aa872fd65c100f3cb2cd793
+Y = 3d5a6322354ee40e795fe5b2e2f6e4d00019cf4793a787acd09406db869cb50b
+
+Curve = P-256
+# N = -32
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632531
+X = 2377c7d690a242ca6c45074e8ea5beefaa557fd5b68371d9d1475bd52a7ed0e1
+Y = b85ec0457bec5bc7c0726f16406fe48199a75933130b98e9183f984e22472d4d
+
+Curve = P-256
+# N = -31
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632532
+X = 301d9e502dc7e05da85da026a7ae9aa0fac9db7d52a95b3e3e3f9aa0a1b45b8b
+Y = 9aae49084cf9eddd1f2dc3fd94fe828dd672651c9778359e2a7249515e811d98
+
+Curve = P-256
+# N = -30
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632533
+X = 409f8da21aea236a5f5a1904d0310c1c6192a67d0da08936319869a8ad0838a3
+Y = 8f23084d30ff71a9f1d918d42c42bf08c1665f14e9d5986c1e9c2d38d5e170a5
+
+Curve = P-256
+# N = -29
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632534
+X = d6d33adefa195b07a7c36da090853b8cfd8cd1c688b58a41dedd693d1c784def
+Y = 7b5545e811e6a282c087dba0aa75a234f65e9955b46a1212aaf3edba6c2e4359
+
+Curve = P-256
+# N = -28
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632535
+X = 38d86fa55b4fd1586c5f05fae7acfc4d36cbdcf7fa62129339246f69c4300e4e
+Y = c06c47a4b542f0d980095976f618fdd1074603456e276448606cf23f00961b84
+
+Curve = P-256
+# N = -27
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632536
+X = 184ffa5819d80d51deba2fac4611f378576355bd683e54abf2e201173b0883d1
+Y = 3f5991d799770ca75b3926f7d934666aba4213349c0fb6e9df2dbd3d9f6f9190
+
+Curve = P-256
+# N = -26
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632537
+X = f5757c012185a599d1f3958b0ae68aa5dffd3d78e1a2eee67417001857658331
+Y = c6c5912ed3898a4eb4edc72e2d5f702af591a1aff7207bf400acc2018d97213b
+
+Curve = P-256
+# N = -25
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632538
+X = 3a67e2554b0c0bb685f4f52d8c07fa8441652fc5b76f1b2484a4dc45f200d687
+Y = d82f0e77d0e030bdd9250d98e9c504f273e77509ca589e755612e94cfd086cdb
+
+Curve = P-256
+# N = -24
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632539
+X = db474918ec62ad7eb652b8b0af585aba9b2f394723ab103776e27d7d8c2aa4cb
+Y = 7a7ee2c5448bc0d8bc686b9f84f92ad475db63b97cf5c82dde249a936b5854aa
+
+Curve = P-256
+# N = -23
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63253a
+X = 0e91c7239c2640d7d28a3e39d4583fa63c0bc0a5df64a4fe672e573045ca7896
+Y = a209a3c3aaf245dee5dd8cc4471f429281977ccb8a185a51ec7fb9abcebf52aa
+
+Curve = P-256
+# N = -22
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63253b
+X = c0dd241a50d48f99fcc7a186a6d44e0763ec90478e1def8e36f5c4e950d67afb
+Y = 7d798cd0569ab748be583239153f9d2725871a841fc15d29f3432e9427351393
+
+Curve = P-256
+# N = -21
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63253c
+X = 3250fcf686637c7b2e4ac86eb473bca53a582139f42b1523fd76364e67399e83
+Y = bd183cbc9982ca6d684cf6f2e281477376832c3dc4a9957dea21db5f8e2b73f6
+
+Curve = P-256
+# N = -20
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63253d
+X = 83a01a9378395bab9bcd6a0ad03cc56d56e6b19250465a94a234dc4c6b28da9a
+Y = 891b64911d08cdcc5195a14629ed48a360ddfd4596dc0ab007dbf5557909bf47
+
+Curve = P-256
+# N = -19
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63253e
+X = cb6d2861102c0c25ce39b7c17108c507782c452257884895c1fc7b74ab03ed83
+Y = a7289eb3db2610afa3ca18eff292931b5b698e92cf05c1fc1c6eaf8ad4313255
+
+Curve = P-256
+# N = -18
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63253f
+X = 1057e0ab5780f470defc9378d1c7c87437bb4c6f9ea55c63d936266dbd781fda
+Y = 090e9ba4ea341a246056482026911a58233ee4a4a10b0e08727c4cc6c395ba5d
+
+Curve = P-256
+# N = -17
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632540
+X = 47776904c0f1cc3a9c0984b66f75301a5fa68678f0d64af8ba1abce34738a73e
+Y = 55ffa1184a46a8d89dce7a9a889b717c7e4d7fbcd72a8cc0cd0878008e0e0323
+
+Curve = P-256
+# N = -16
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632541
+X = 76a94d138a6b41858b821c629836315fcd28392eff6ca038a5eb4787e1277c6e
+Y = 567a019dcbe0d9f2934f5e4a1ee178df7a665ffcf0387455f162228db473aeef
+
+Curve = P-256
+# N = -15
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632542
+X = f0454dc6971abae7adfb378999888265ae03af92de3a0ef163668c63e59b9d5f
+Y = 4a46c11ba6d1d2e1b19a6b1ae069bc19d5c4de328a4a05c0b81a6321f2fcb0c9
+
+Curve = P-256
+# N = -14
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632543
+X = 54e77a001c3862b97a76647f4336df3cf126acbe7a069c5e5709277324d2920b
+Y = 0a660e43d60bce8bbdede073fa5d183c8e8e15898caf6ff7e45837d09f2f4c8a
+
+Curve = P-256
+# N = -13
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632544
+X = 177c837ae0ac495a61805df2d85ee2fc792e284b65ead58a98e15d9d46072c01
+Y = 9c44a731b1415aa85dbf6e524bf0b18dd911eb3d5e04b20c63bc441d10384027
+
+Curve = P-256
+# N = -12
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632545
+X = 741dd5bda817d95e4626537320e5d55179983028b2f82c99d500c5ee8624e3c4
+Y = f88f4b9463c7a024a98c7caab7784eab71146ed4ca45a358e66a00dd32bb7e2c
+
+Curve = P-256
+# N = -11
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632546
+X = 3ed113b7883b4c590638379db0c21cda16742ed0255048bf433391d374bc21d1
+Y = 6f66df64333b375edb37bc505b0b3975f6f2fb26a16776251d07110317d5c8bf
+
+Curve = P-256
+# N = -10
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632547
+X = cef66d6b2a3a993e591214d1ea223fb545ca6c471c48306e4c36069404c5723f
+Y = 78799d5cd655517091edc32262c4b3efa6f212d7018ae11135cb4455bb50f88c
+
+Curve = P-256
+# N = -9
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632548
+X = ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0
+Y = d5d8bb358d36031978feb569b5715f37b28eb0165b217dc017a5ddb5b22fb705
+
+Curve = P-256
+# N = -8
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632549
+X = 62d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393
+Y = 52a533416e1627dcb00ea288ee98311f5d12ae0a4418958725abf595f0f66a81
+
+Curve = P-256
+# N = -7
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254a
+X = 8e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3
+Y = 8c14e2411fcce7ca92f9607c590a6fffac38c9cd34fbe4de3aa1e5793e0bff4b
+
+Curve = P-256
+# N = -6
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254b
+X = b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9
+Y = 17a3ef8acdc8252b9013f1d20458fc86e3ff0890e381e9420283b7ac7038801d
+
+Curve = P-256
+# N = -5
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254c
+X = 51590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed
+Y = 1f3e82566fb58d83751e40c9407586d9f2fed1002b27f7772e2f44bb025e925b
+
+Curve = P-256
+# N = -4
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254d
+X = e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852
+Y = 1f0ea8a4b39cc339e62011a02579d289b103693d0cf11ffaa3bd3dc0e7b12739
+
+Curve = P-256
+# N = -3
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e
+X = 5ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c
+Y = 78cb9bf2b6670082c8b4f931e59b5d1327d54fcac7b047c265864ed85d82afcd
+
+Curve = P-256
+# N = -2
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f
+X = 7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978
+Y = f888aaee24712fc0d6c26539608bcf244582521ac3167dd661fb4862dd878c2e
+
+Curve = P-256
+# N = -1
+N = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550
+X = 6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
+Y = b01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a
+
+Curve = P-256
+# N = 0
+N = 0000000000000000000000000000000000000000000000000000000000000000
+X = 0000000000000000000000000000000000000000000000000000000000000000
+Y = 0000000000000000000000000000000000000000000000000000000000000000
+
+Curve = P-256
+# N = 1
+N = 0000000000000000000000000000000000000000000000000000000000000001
+X = 6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
+Y = 4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
+
+Curve = P-256
+# N = 2
+N = 0000000000000000000000000000000000000000000000000000000000000002
+X = 7cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978
+Y = 07775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d1
+
+Curve = P-256
+# N = 3
+N = 0000000000000000000000000000000000000000000000000000000000000003
+X = 5ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c
+Y = 8734640c4998ff7e374b06ce1a64a2ecd82ab036384fb83d9a79b127a27d5032
+
+Curve = P-256
+# N = 4
+N = 0000000000000000000000000000000000000000000000000000000000000004
+X = e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852
+Y = e0f1575a4c633cc719dfee5fda862d764efc96c3f30ee0055c42c23f184ed8c6
+
+Curve = P-256
+# N = 5
+N = 0000000000000000000000000000000000000000000000000000000000000005
+X = 51590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed
+Y = e0c17da8904a727d8ae1bf36bf8a79260d012f00d4d80888d1d0bb44fda16da4
+
+Curve = P-256
+# N = 6
+N = 0000000000000000000000000000000000000000000000000000000000000006
+X = b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9
+Y = e85c10743237dad56fec0e2dfba703791c00f7701c7e16bdfd7c48538fc77fe2
+
+Curve = P-256
+# N = 7
+N = 0000000000000000000000000000000000000000000000000000000000000007
+X = 8e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3
+Y = 73eb1dbde03318366d069f83a6f5900053c73633cb041b21c55e1a86c1f400b4
+
+Curve = P-256
+# N = 8
+N = 0000000000000000000000000000000000000000000000000000000000000008
+X = 62d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393
+Y = ad5accbd91e9d8244ff15d771167cee0a2ed51f6bbe76a78da540a6a0f09957e
+
+Curve = P-256
+# N = 9
+N = 0000000000000000000000000000000000000000000000000000000000000009
+X = ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0
+Y = 2a2744c972c9fce787014a964a8ea0c84d714feaa4de823fe85a224a4dd048fa
+
+Curve = P-256
+# N = 10
+N = 000000000000000000000000000000000000000000000000000000000000000a
+X = cef66d6b2a3a993e591214d1ea223fb545ca6c471c48306e4c36069404c5723f
+Y = 878662a229aaae906e123cdd9d3b4c10590ded29fe751eeeca34bbaa44af0773
+
+Curve = P-256
+# N = 11
+N = 000000000000000000000000000000000000000000000000000000000000000b
+X = 3ed113b7883b4c590638379db0c21cda16742ed0255048bf433391d374bc21d1
+Y = 9099209accc4c8a224c843afa4f4c68a090d04da5e9889dae2f8eefce82a3740
+
+Curve = P-256
+# N = 12
+N = 000000000000000000000000000000000000000000000000000000000000000c
+X = 741dd5bda817d95e4626537320e5d55179983028b2f82c99d500c5ee8624e3c4
+Y = 0770b46a9c385fdc567383554887b1548eeb912c35ba5ca71995ff22cd4481d3
+
+Curve = P-256
+# N = 13
+N = 000000000000000000000000000000000000000000000000000000000000000d
+X = 177c837ae0ac495a61805df2d85ee2fc792e284b65ead58a98e15d9d46072c01
+Y = 63bb58cd4ebea558a24091adb40f4e7226ee14c3a1fb4df39c43bbe2efc7bfd8
+
+Curve = P-256
+# N = 14
+N = 000000000000000000000000000000000000000000000000000000000000000e
+X = 54e77a001c3862b97a76647f4336df3cf126acbe7a069c5e5709277324d2920b
+Y = f599f1bb29f4317542121f8c05a2e7c37171ea77735090081ba7c82f60d0b375
+
+Curve = P-256
+# N = 15
+N = 000000000000000000000000000000000000000000000000000000000000000f
+X = f0454dc6971abae7adfb378999888265ae03af92de3a0ef163668c63e59b9d5f
+Y = b5b93ee3592e2d1f4e6594e51f9643e62a3b21ce75b5fa3f47e59cde0d034f36
+
+Curve = P-256
+# N = 16
+N = 0000000000000000000000000000000000000000000000000000000000000010
+X = 76a94d138a6b41858b821c629836315fcd28392eff6ca038a5eb4787e1277c6e
+Y = a985fe61341f260e6cb0a1b5e11e87208599a0040fc78baa0e9ddd724b8c5110
+
+Curve = P-256
+# N = 17
+N = 0000000000000000000000000000000000000000000000000000000000000011
+X = 47776904c0f1cc3a9c0984b66f75301a5fa68678f0d64af8ba1abce34738a73e
+Y = aa005ee6b5b957286231856577648e8381b2804428d5733f32f787ff71f1fcdc
+
+Curve = P-256
+# N = 18
+N = 0000000000000000000000000000000000000000000000000000000000000012
+X = 1057e0ab5780f470defc9378d1c7c87437bb4c6f9ea55c63d936266dbd781fda
+Y = f6f1645a15cbe5dc9fa9b7dfd96ee5a7dcc11b5c5ef4f1f78d83b3393c6a45a2
+
+Curve = P-256
+# N = 19
+N = 0000000000000000000000000000000000000000000000000000000000000013
+X = cb6d2861102c0c25ce39b7c17108c507782c452257884895c1fc7b74ab03ed83
+Y = 58d7614b24d9ef515c35e7100d6d6ce4a496716e30fa3e03e39150752bcecdaa
+
+Curve = P-256
+# N = 20
+N = 0000000000000000000000000000000000000000000000000000000000000014
+X = 83a01a9378395bab9bcd6a0ad03cc56d56e6b19250465a94a234dc4c6b28da9a
+Y = 76e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923f54ff8240aaa86f640b8
+
+Curve = P-256
+# N = 21
+N = 0000000000000000000000000000000000000000000000000000000000000015
+X = 3250fcf686637c7b2e4ac86eb473bca53a582139f42b1523fd76364e67399e83
+Y = 42e7c342667d359397b3090d1d7eb88c897cd3c33b566a8215de24a071d48c09
+
+Curve = P-256
+# N = 22
+N = 0000000000000000000000000000000000000000000000000000000000000016
+X = c0dd241a50d48f99fcc7a186a6d44e0763ec90478e1def8e36f5c4e950d67afb
+Y = 8286732ea96548b841a7cdc6eac062d8da78e57ce03ea2d60cbcd16bd8caec6c
+
+Curve = P-256
+# N = 23
+N = 0000000000000000000000000000000000000000000000000000000000000017
+X = 0e91c7239c2640d7d28a3e39d4583fa63c0bc0a5df64a4fe672e573045ca7896
+Y = 5df65c3b550dba221a22733bb8e0bd6d7e68833575e7a5ae138046543140ad55
+
+Curve = P-256
+# N = 24
+N = 0000000000000000000000000000000000000000000000000000000000000018
+X = db474918ec62ad7eb652b8b0af585aba9b2f394723ab103776e27d7d8c2aa4cb
+Y = 85811d39bb743f28439794607b06d52b8a249c47830a37d221db656c94a7ab55
+
+Curve = P-256
+# N = 25
+N = 0000000000000000000000000000000000000000000000000000000000000019
+X = 3a67e2554b0c0bb685f4f52d8c07fa8441652fc5b76f1b2484a4dc45f200d687
+Y = 27d0f1872f1fcf4326daf267163afb0d8c188af735a7618aa9ed16b302f79324
+
+Curve = P-256
+# N = 26
+N = 000000000000000000000000000000000000000000000000000000000000001a
+X = f5757c012185a599d1f3958b0ae68aa5dffd3d78e1a2eee67417001857658331
+Y = 393a6ed02c7675b24b1238d1d2a08fd50a6e5e5108df840bff533dfe7268dec4
+
+Curve = P-256
+# N = 27
+N = 000000000000000000000000000000000000000000000000000000000000001b
+X = 184ffa5819d80d51deba2fac4611f378576355bd683e54abf2e201173b0883d1
+Y = c0a66e276688f359a4c6d90826cb999545bdeccc63f0491620d242c260906e6f
+
+Curve = P-256
+# N = 28
+N = 000000000000000000000000000000000000000000000000000000000000001c
+X = 38d86fa55b4fd1586c5f05fae7acfc4d36cbdcf7fa62129339246f69c4300e4e
+Y = 3f93b85a4abd0f277ff6a68909e7022ef8b9fcbb91d89bb79f930dc0ff69e47b
+
+Curve = P-256
+# N = 29
+N = 000000000000000000000000000000000000000000000000000000000000001d
+X = d6d33adefa195b07a7c36da090853b8cfd8cd1c688b58a41dedd693d1c784def
+Y = 84aaba16ee195d7e3f78245f558a5dcb09a166ab4b95eded550c124593d1bca6
+
+Curve = P-256
+# N = 30
+N = 000000000000000000000000000000000000000000000000000000000000001e
+X = 409f8da21aea236a5f5a1904d0310c1c6192a67d0da08936319869a8ad0838a3
+Y = 70dcf7b1cf008e570e26e72bd3bd40f73e99a0ec162a6793e163d2c72a1e8f5a
+
+Curve = P-256
+# N = 31
+N = 000000000000000000000000000000000000000000000000000000000000001f
+X = 301d9e502dc7e05da85da026a7ae9aa0fac9db7d52a95b3e3e3f9aa0a1b45b8b
+Y = 6551b6f6b3061223e0d23c026b017d72298d9ae46887ca61d58db6aea17ee267
+
+Curve = P-256
+# N = 32
+N = 0000000000000000000000000000000000000000000000000000000000000020
+X = 2377c7d690a242ca6c45074e8ea5beefaa557fd5b68371d9d1475bd52a7ed0e1
+Y = 47a13fb98413a4393f8d90e9bf901b7e6658a6cdecf46716e7c067b1ddb8d2b2
+
+Curve = P-256
+# N = 33
+N = 0000000000000000000000000000000000000000000000000000000000000021
+X = 9807d699fcd81356fa9aa25b89d9d34ea03b0a533aa872fd65c100f3cb2cd793
+Y = c2a59cdccab11bf286a01a4d1d091b2fffe630b96c5878532f6bf92479634af4
+
+Curve = P-256
+# N = 34
+N = 0000000000000000000000000000000000000000000000000000000000000022
+X = 2f9e6ebf717def118d1a092fce97133919cf2d31b7f8be6cfb7fdbe16820999e
+Y = 7aeeaad8532f8821e3cb1a14d0f524f61682d2950aedea0d756b880306be0b2f
+
+Curve = P-256
+# N = 35
+N = 0000000000000000000000000000000000000000000000000000000000000023
+X = d58d4a589ed27d168ffa3ad7326c48ca94e8e1fe92af9700a12d389033bb291a
+Y = d45514d102726b8576ea92632dc7fef667271c163b034979a5b0c9c6f586b9d5
+
+Curve = P-256
+# N = 36
+N = 0000000000000000000000000000000000000000000000000000000000000024
+X = da5bd2d111c3731d14281d73d5e965a384e94cbf9664dc227d2a45b9abb598ef
+Y = 6166fc1938f06c542246527b075df922bb15925817b426a492333c242044ae5f
+
+Curve = P-256
+# N = 37
+N = 0000000000000000000000000000000000000000000000000000000000000025
+X = 419a6a646ddb817dd6b0978611a826aae0d21379246bfd4473a92894502b3348
+Y = 332544cf1102f584545c9fb1954c2fd513c6d072f3dee1e2db1d6c81b09214b2
+
+Curve = P-256
+# N = 38
+N = 0000000000000000000000000000000000000000000000000000000000000026
+X = 971581bdd1356ea1b317d7b29059611176788153b4d38f8d81155d60576d8c46
+Y = 870ce8afdf0d59c19dd52f1e8654186aaae84346183f325380c784e32bcec592
+
+Curve = P-256
+# N = 39
+N = 0000000000000000000000000000000000000000000000000000000000000027
+X = 22a682f7c3996d4d42014976a179046e547b942dd2d138d4a0c199ddfb2776c4
+Y = 4f4606b0102223eeb918c9835a54356c979dcc310265b0685347f649cbaa285d
+
+Curve = P-256
+# N = 40
+N = 0000000000000000000000000000000000000000000000000000000000000028
+X = bea01e7daad46131bd5b18584d0ee0c457b44d35ae412c0faa74b4da1aac91e6
+Y = 21edd4e6094dac05d882ded2816bdaf610fb9f1a296ddcd934c7ff6431d51d1c
+
+Curve = P-256
+# N = 41
+N = 0000000000000000000000000000000000000000000000000000000000000029
+X = 67a6bec240dee0651cf258d2e6cfe8aa6067c5c3d4175a593a7de694995d2fa2
+Y = de692b7022d131586c249b49464d44991542c7ee209aca6c49c24ce1441feed5
+
+Curve = P-256
+# N = 42
+N = 000000000000000000000000000000000000000000000000000000000000002a
+X = 6780c5fc70275e2c7061a0e7877bb174deadeb9887027f3fa83654158ba7f50c
+Y = 3cba8c34bc35d20e81f730ac1c7bd6d661a942f90c6a9ca55c512f9e4a001266
+
+Curve = P-256
+# N = 43
+N = 000000000000000000000000000000000000000000000000000000000000002b
+X = 986ae2506f1ff104d04230861d8f4b498f4bc4c6d009b30f7544dc129b82d28d
+Y = 003cccc0a6460e0ae328a4d97d3c7b61d86fc6289c189f2525110c441bb07e97
+
+Curve = P-256
+# N = 44
+N = 000000000000000000000000000000000000000000000000000000000000002c
+X = 4756686a0d7e11cb04211f7813b498c2170bff9af47aab0e652d0380ac8d4f9b
+Y = ce334fdb54863a6f2ffc258f3474c4eb65755aa6384890b40e824a9882f785d0
+
+Curve = P-256
+# N = 45
+N = 000000000000000000000000000000000000000000000000000000000000002d
+X = a891d06670bde99b3ecd0f5ddff0672e0f5f609edd29d6d979c78080fae0ba03
+Y = b596cd922cbfa1c1419a88c4033c1ce71c6b38f0feb0f2ccefc3edc8166934ae
+
+Curve = P-256
+# N = 46
+N = 000000000000000000000000000000000000000000000000000000000000002e
+X = b1bb852cfea701fc42caaa1a5b5eb6a5174e94f87c4d3b0612b46293eb5925f8
+Y = 1d337dc66c7874cb46047d206fd1e6cd2075978e09ef174f783ea1f98bde3cb0
+
+Curve = P-256
+# N = 47
+N = 000000000000000000000000000000000000000000000000000000000000002f
+X = 42c315cc48958708595361ea83071bbcdd5b31583e19066d51d689227b1c0d7c
+Y = 649a61ce571b95852914d1dfbb7a799074f1a1e1eb87f164d6c4a72bb2f9b1b9
+
+Curve = P-256
+# N = 48
+N = 0000000000000000000000000000000000000000000000000000000000000030
+X = 9482fb0e492539ec8cce745be070cda11c2e92960a201a61abfb9dc69e4536ca
+Y = 351d9ca745f157f91a5d638ca7534e63f63d5e295707bfbb1fad863bf58cc1c8
+
+Curve = P-256
+# N = 49
+N = 0000000000000000000000000000000000000000000000000000000000000031
+X = f785b0e098068875bb22b146866e6c0528fb7ea9758fd4fd7d228ce6a5674455
+Y = 73fdb0bf6080da6ee15c767f0d9f5b414b04b6fd5f3aa60ae7bc490c10d62408
+
+Curve = P-256
+# N = 50
+N = 0000000000000000000000000000000000000000000000000000000000000032
+X = ba6821cba9bab3ba57a98127482a5de00c108a6ac3241ebb508c58a24d9edba2
+Y = 8841c5de60c4af3b87375ec78cbd21e7e70a85292d77eb4f8f218b036784e120
+
+Curve = P-256
+# N = 51
+N = 0000000000000000000000000000000000000000000000000000000000000033
+X = 672c4a514d9de43eaadee6863c1d68bc95f7eb56e81008ff044360f0018e22b1
+Y = 548c7e9196a25bfe611de5a4ace203f7136246589704d9419935399191f37104
+
+Curve = P-256
+# N = 52
+N = 0000000000000000000000000000000000000000000000000000000000000034
+X = 194e35c4ec2f25ef537105d2b2e54c1803eb2d0a04492e3d2e1d72d04b978b18
+Y = af42679ac14ea798bdb78f1683c4294c68ea7d11e83d569bc049fe2403cf4764
+
+Curve = P-256
+# N = 53
+N = 0000000000000000000000000000000000000000000000000000000000000035
+X = 6f01bd49c9d952455a47802254b88039982b1ca78de9b983f126ec9f7449d036
+Y = 1562080ff1d5deab11a0f21a608776cea78551bfc3749b08360233dd989e17db
+
+Curve = P-256
+# N = 54
+N = 0000000000000000000000000000000000000000000000000000000000000036
+X = 6fc0cd2131b9eb0587c40153ae03fcc8f4b7dbe02cdc59fa4aa8b7d7c3a3dc7d
+Y = d4b77618646b0513c0e9d83bf3764561ac630a8e350bae857b065b1731fde2a4
+
+Curve = P-256
+# N = 55
+N = 0000000000000000000000000000000000000000000000000000000000000037
+X = 079dba7ba068c9267571a109fe7fea2cc2a595b762c1eadadec1dff7df6e60a0
+Y = dc1e19b743d4d1811d223f9d2a9588ab83eb2df35751a397fb0da5aeb4824dea
+
+Curve = P-256
+# N = 56
+N = 0000000000000000000000000000000000000000000000000000000000000038
+X = 6f9a14fbbcf2815a42e8b595d0d5a0eb956463496f67dc0d60a7520a83fb632e
+Y = c8429eb87b404f2a854c2de4c776c9db9d87f2b8dd394a5b92d75da2f24ecf29
+
+Curve = P-256
+# N = 57
+N = 0000000000000000000000000000000000000000000000000000000000000039
+X = c116e30ebb4d2865126d45a8ea907f86289d406e2d6c6bd88abd97b1d0f56077
+Y = e9478823c35b30c2b8b16d9bb13b87657d5bd5e89e59c8c5313fd7fda410c206
+
+Curve = P-256
+# N = 58
+N = 000000000000000000000000000000000000000000000000000000000000003a
+X = ec247d216208539a58912acd04d6df1f8b0b3c9affdc599e9e2481f254419b1d
+Y = ca31ca404b25347546e93b667595a5ddfc715e31da31f5982b9106263ac41fde
+
+Curve = P-256
+# N = 59
+N = 000000000000000000000000000000000000000000000000000000000000003b
+X = 665f1a6ffe0c6437765b2784fca9bdf7e50941119e8dc8eca2b6ea0e0faa4b45
+Y = 490e2ca49ffd18c26e8cca29f7eac37f7dede5bf81e215bc6e25a6602b7f4ccf
+
+Curve = P-256
+# N = 60
+N = 000000000000000000000000000000000000000000000000000000000000003c
+X = 05dae8c2c5a5afba7e53b9efcac1d0b8224559146918d320879bb82d96ef4963
+Y = bb07a44d03c08119e2f782d0d70304cef299e802d1d3bf625690236051d34324
+
+Curve = P-256
+# N = 61
+N = 000000000000000000000000000000000000000000000000000000000000003d
+X = 059ccb19edd3da9a2d3a6b3d8d9900013e7910a08b724fd55939ac380d32af0e
+Y = bb6ad7eccad49159da65281b9345638e1621f7a33956cecd928e1e3c97fe91d1
+
+Curve = P-256
+# N = 62
+N = 000000000000000000000000000000000000000000000000000000000000003e
+X = 571c05c84021edcec4b1ac999829ecd80f8216b239c67f269f88ff57ae8cce2b
+Y = f9325afc175332192dfb3d9f305f499affcc0cf8a6bb2ddbac0128c284ab8115
+
+Curve = P-256
+# N = 63
+N = 000000000000000000000000000000000000000000000000000000000000003f
+X = 6a9501d85bf5dc802a1f28a08acc7d8fdf53c8af01a7cd3832a290825d8bdac1
+Y = ca640ad19347374381c6c6e44a3c56a3f8461b5c697a6f3530aff53d5f1ef1a3
+
+Curve = P-256
+# N = 64
+N = 0000000000000000000000000000000000000000000000000000000000000040
+X = 0a0643fb8fcc14def67a6a5eb1bf8e9125b35edc7338d816aa4110a6b90ee785
+Y = 553438324a9e7955c520dacda2920e700da10d00e7012ed7bac0d100861f9cc2
+
+Curve = P-384
+# N = -64
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52933
+X = c1e179aa178a780046e35841766073612ea5e5abd26608f259a599fc9a8425a3ea639d94cbe63fde1d69c70f9327605f
+Y = 3c7bd091b2435929f6459e8e9afc8e4d523f89bf5dbdf1540817d6e1505de4a8110ce79e3df2214774ba0f4c7a90715a
+
+Curve = P-384
+# N = -63
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52934
+X = b32e89eb942aab112cae508f5164ca6cac334769de358b333c968cfbb83c8f7b941560583def5431afd574f7e008f8af
+Y = 4e1c518e99268efcdb1d40969fc002a4588f03b748c123f986322285fb66ff69ca2511ffa618b5e537e541f7bb1ee1d3
+
+Curve = P-384
+# N = -62
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52935
+X = fad756f52978ca376b086258bfaaac8a3a42d10c5d6cb507c8ca4c2efce2a9692f29d569dcf86b74aacc18a3c5d13f84
+Y = 4854ecff8a943566631934121d70f1731bcb8ad0a8252d22476f6b5234263363d2b9795b85a991d341160c06a5f07a38
+
+Curve = P-384
+# N = -61
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52936
+X = 0a12151e620a81a2870b7900c1c476895f729644b4fa011fa92542dbf5872edcb5e5a6d1fb4071db5c24cc10bda2cc75
+Y = 7a837fd69f1514481eaffdb8a43f57f3bb862b38a44e260e309a6cbae565f9667b046797980473b75ec1ea823a5c17c3
+
+Curve = P-384
+# N = -60
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52937
+X = 22f2d6f4248636552c2f806ca08d835bf6c15cd3df2f83cff6ea265604a50599458e0d6d3f0d7daf3a81734e1a5b276a
+Y = 910e7499f3d19f7e17795004df2b676ff5c5a78378f7b9b224be2290a949370b8d94c16873b89b4dd3d06a0c837e418f
+
+Curve = P-384
+# N = -59
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52938
+X = cc42cb6ad9f7161a4296f482176d1b0191e7691569202b98a2b93e41e5a75d8abbf7b15b29f59772ba77d70f99fe20b0
+Y = 44294494527ae92c6e9b919af960597a15bddc0bb61c76a13dce973204ab2145e7d6e98a9cc787e2258b2acca1db29c0
+
+Curve = P-384
+# N = -58
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52939
+X = 2dba210e4d863e5dd6b431f835c444c8fa54c48cf585a1ee081f91a2141db69c5ee6baa05a4f63e8ecedcd4f9e4d8bcd
+Y = c61e2b24da041cfb4ebda9c5c6714b9022230ce6d948d6e1dde5350528839065e76d7485acf41db61ed4d72097c54f59
+
+Curve = P-384
+# N = -57
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5293a
+X = 8654ddc7eb10d5a84f1cec9879cabece6c3eb8b272461fc778baa6599653227907fd3823a2b10416709d6a38b6ecb8bd
+Y = ced8f993eef6d1777cfa985089c1362f8bda6619b702815b82e2ec2e55e381be77292429544664ae479841581ca87ab6
+
+Curve = P-384
+# N = -56
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5293b
+X = 2d56e829a6fb82c64a4697d960af951e9067ae91f926d34306da48a8aaeb7869b6a67e6d27426e0442aa03b36e1988df
+Y = 09c9aeb11e30f80a45b4c00d5f99ba5182ce2a79952ce55c2db914f4ce881165348a9939e9318b7f7599706f0f3b8035
+
+Curve = P-384
+# N = -55
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5293c
+X = a059cd75571b3507307d064e7750ab08f36d925a4f0112f5f22f519eeae1eb0130d0a0e2e2ee7c873bb25771ff2b4f85
+Y = bf09519d3a58a08ffd92731403203cd103ceecaeaa9bf700f05feb82787dca54577f389377b6432a5137855b96372933
+
+Curve = P-384
+# N = -54
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5293d
+X = bb0ff1b32114d1740890f276e61f8041c55c05c89ed8af7c8776542902a392ad231a5a3dda24dffe6b2de3474fadeb4e
+Y = b901067fedd0040c7a7bb4f04d26d41ea59972fa56a814a00d6483d2cfef638e0d3c59fe7879bf6c2efbe2f0bcb08fcd
+
+Curve = P-384
+# N = -53
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5293e
+X = a0e5cd10144167c592288353932cf40888e935bdd5f169ba8f41cbe07c3e20b6ff2ca2a90bdc08ac90903c9d776e9fa3
+Y = edf21868e72eab0155abd0a3e1831b4129c6936bc5e9fdb118240f51e80802f4f362341462be685503a3aaa62161a781
+
+Curve = P-384
+# N = -52
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5293f
+X = e217fd47cff91786a8eb87b0cf545dca6a0eca0fa0bbdc7aaad84294bd6fa726a3131880b817b7828611cae7d96a1985
+Y = 3d7cac441254d845583048417d1e352491edc33ee84baf721a7dd7ea8bf2d7b8fc0bb988295f932a20b4b1279d4dafc8
+
+Curve = P-384
+# N = -51
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52940
+X = 8bc6057da46fdafba86d2c9e23c2f5bc0b4a2a1247e8f2cc7126ed3b510b0341a618a8cc08d1b992ffe06fbae0068fb0
+Y = 015e60d80977e59f040d5dd02e0e67a2888018a6a66b51f2bd012dfd663168f9ab392f7a229f880a934a68481dd3d316
+
+Curve = P-384
+# N = -50
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52941
+X = ae98b89e108d372ac57c52eed44931f735cc34c5b98037cff20c1c93b6706101dd56a283b13f9a29cf0106d9107660d7
+Y = 9d1683e51907e1dbd6faf8f01e91c1f8ee06a1725e6ab30079b01b8a421fcc17cf797d7c65631d615bc6ee7176fd83b6
+
+Curve = P-384
+# N = -49
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52942
+X = a6c36459d1243ff7f504f32fc1211afd7486cc53003ed182dd74d976a49117348828f5b191d03d1c9da43e8e5a0e8cf2
+Y = a7d8603206938e26e3142efa3d758e2ff428daac05cc9a4bdd7660a87de52da3c75e43f33a847609d192248a739c4bf5
+
+Curve = P-384
+# N = -48
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52943
+X = eb967d99bfe2cdfec7d895a5b4edde398642eb77303050301fcabe3798c46ace66629c442ca537cc68701396222c0089
+Y = 16c1a2d91b21bbd5c0dc58e8829be6255aa5f03624f0075953e4d5d4ce5bee0fbf26bc3989866def86d295d1734a70b9
+
+Curve = P-384
+# N = -47
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52944
+X = 1273f595eaba5df1a06f4ca5fa30e0bcc870bf64c76762c0e2a9530742a384f121e9e8ccc22e6b2cd5254db0689a30fe
+Y = 9d8c803701f7e7345f24aad3597d4b605e501cd51f357c3dec331dbdea7eb79258eb85fbd120374f3425066ac2a70b31
+
+Curve = P-384
+# N = -46
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52945
+X = ebdbf7a1fbd628764fbb7a168061b8c7c6e3211321841d9ae8e9bece5debbbd3a33fac59a8097c72fa4c28f0bb83b8f8
+Y = cfe62132004c17e30f47f7c1df7f8c3a5492cbc2d80f4c402ffaee9f590f43e240db9b44620c62190fa011cd61426fba
+
+Curve = P-384
+# N = -45
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52946
+X = 6ce3bf11431d1d9c3bce43bec4f4609eadd9c644fd8393e3258449c2e28cd17cb03d491de84ea016a5b68a56bbfef50f
+Y = 3ccda1d8b4dc9fb18389f8aac6abd5426e77ff1c84b1b557a728b01d4378881e7efa25ccbde1a301c922166f83a8fb8e
+
+Curve = P-384
+# N = -44
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52947
+X = 3a67a337e9f9801a467c5ab6d69a1f94757634a28a2d794c0abe75b32c89fc4182cfcdb920e29ee6228d345d53f529c0
+Y = 37898b4b7dddd62bda553e265a26fac28c7ac1eba5deced32c0ce45c03619f1e97115781da4e285e28ec0536475fcf1c
+
+Curve = P-384
+# N = -43
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52948
+X = 9c3adb9111fe252ca5a2ff485cf5a64f4de52ef9689a5eb5aba8ddfa4745a605aa49a4148fbebc6780dddc249a26a33d
+Y = 7c1726a07ec50b24af4607f4dd4b7d65962347bad0fa899cb3963b3fef7595cc2451a47d8a508bdfcbbfff24b8caaf9e
+
+Curve = P-384
+# N = -42
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52949
+X = e035e8b33efae0987f6b994529285be4a9d55093d2b9c155c086e2b3c025a8c1c860097bb529b268b7ff95719518ac0a
+Y = 6813bd89af29ff11934cb32ad35f6c64984eab605bd14d4a943ed686e985f6fe8aaef1ff43c6a5b491fb413d5c13b516
+
+Curve = P-384
+# N = -41
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5294a
+X = 2ce37b2917ad0d5c411a5ed920a88b095185d456b279a18410d10234b609b8c7077b05ca6a94f76d10f3f39e87c76293
+Y = 40d81c1f845f14462542190b7b523fb0454a4847707fcab87df709247a6dd843b225b74a05a2e202307582851d589f81
+
+Curve = P-384
+# N = -40
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5294b
+X = 37cf990dee523641d57328fb34cdf4dbd3795a0b7df5072f1c67e1a4991b2d388809a316e9abf06d301d2ae1c1283ec1
+Y = 90e175521359a8c1f8cfa7ba6156909141487770fb18cc8591bbefb36f8188bf55cd4cf7aad898a4e98909e0812764ef
+
+Curve = P-384
+# N = -39
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5294c
+X = 9bc6b654362621678224c2dcc11702de31c65d0bd2d3642c35b72c15f264347c4983e9bc8a47b24d15ac797ea8f8fedb
+Y = bb7d0f6c7b679b138eb8112f6e104c97245e7de7e7a88f38dd1a76cfbc5db73f952866502a7a27773f686cfc5443032b
+
+Curve = P-384
+# N = -38
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5294d
+X = 081dd3666a57be69b8d22bf15ec27b014b32060b20f018c3f0467a5eb8c359725c7bb4e179bb42b5acf9732879a8bc72
+Y = 9f80d2b90a0c1732b4c8a0f7efa69d775b9e406dfd1830652b29e905a98a994b656da22b3761da3503baec634f505fc1
+
+Curve = P-384
+# N = -37
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5294e
+X = 1221b1e1be96dfe8fdb4300af2deaadcd850ac1e45c8fcb941c36c0122a10654e56ba2d9cafcb4482a07272d05de389d
+Y = af4dda0575a127c7e527b9f1d1de99e7fa66ac22769a26824dccedbe772e8143a0b0d56cce4098dcacc004df6c622d8c
+
+Curve = P-384
+# N = -36
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5294f
+X = bc7f0d480a8627fcdcbdf8371a720e49499d70a40ba07ccda1159432b4aa3c9ff05c7a3bcce6bb083bf618c1f6729597
+Y = 4160dbbfbf7e58db7284ca4ab192f733d5f0fd6c5cb4ee4b03254fc66a04b4c0712c0771bf0ef33964cd85e70d98a284
+
+Curve = P-384
+# N = -35
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52950
+X = 77c12dceca498ff99b9863d46535a5abc3675bf4f728f3ac55a0270e5859145dc998b7fd7b3813ca29e4ae10ae1410ea
+Y = 111cae43cbd892c52cfd0f2f42efd961c691804769b0bcfbffb785b7311d4dd23df3a0f3410dd98cca8b81f863411088
+
+Curve = P-384
+# N = -34
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52951
+X = 1aee48c2f0f4678b942a66ccd2385edbd1831e58722e5d974aa2569489b3a75599741028cd1141c4266e9778df65df0f
+Y = 28f7476e2976bac460f2ba40e239b8ec74e0cb6dc4ed61c0b8994c4a78bbbf10ab8982d842466b3a02f077b05ef09afd
+
+Curve = P-384
+# N = -33
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52952
+X = 38dbd58238c21bbca92758f2773bb6c955492715879f54bdaf246b3f70bec90d631eaed97add3fe2aefd7fec482c802a
+Y = 7d73d996b207e776ed5881eb7c8c2cf0100ab58e9aee0a0e84790d5456426ad72ceba1575440bc8fa665f999d92ccc44
+
+Curve = P-384
+# N = -32
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52953
+X = 7f7356c5e0fbc6678bab99df1fd9b2b49f81618d6d99af63612ccf2cc4acaf5c44819b88e217ecd3cce82fe55ff86ed6
+Y = 1ffc4ce21dfaf5b5bc2e5a0464b3599dd43aa1a21f3c190d1594bf66a69738ad38a2eaeb559eb43dac11d318969e7884
+
+Curve = P-384
+# N = -31
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52954
+X = f2e6f06f0ea533e804fcfa12c087d446c73e3336394c98c59d3993124d374e62061fa485bd1b8a650c8361dba9eb28b9
+Y = 70791b9b3dff43b9061bbd9ed030042eb5fbe579ae87f2cec04f4653eebdae31c177fd382d036140cead0a4687ad97f3
+
+Curve = P-384
+# N = -30
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52955
+X = d43bef39667b4579be8d64327f27b3e7a5e64b4d7661e143ba3b0775f23dd1321ffb816b13a131da163d59596752a5bf
+Y = f92d78d6ff41148d6e9a4b62f9601a2578a0eeabf5bfbb769f9ccee1b84eaf1134e089b68cbf1b85f490fa51d00bcaac
+
+Curve = P-384
+# N = -29
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52956
+X = f1bc35f9656c6eead7b75810ce554ee56e0af4f41cbcde50fa46d04f4d4dfca2ad69e407f57b1e27977bb83b1eb1a373
+Y = dba7b685aa64323308cc3ab83f0d4c2372851388926b038face08f69f5cf17c6c00170adb87b75f1ac1f2fa20d7050d6
+
+Curve = P-384
+# N = -28
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52957
+X = 1f1eebd69cfac8d8d63fd685f9fe8d6bd99ba6fb33270854c8bade1c652b661b64f1d4fefa439e57c7e6a187c296ce28
+Y = c02bdccd50fa56c5db396a43873b710003c53eb89ee1534e93fd3b71efa10f2c26c041a74cd5d508b92835a334cf81eb
+
+Curve = P-384
+# N = -27
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52958
+X = 9c5fa2c13f418e623c316d5a82c8b70508e3acaa2b4a8d3a4acb49c0d7ba04e220761bc15898e1b06b4eedfa23e2e546
+Y = 09be21f34f8a7ae5a5017eafc348409e6b020e482561a6aa90fea9ae426de71bf10d3b4cdec0ae64ad604a9c860b78ad
+
+Curve = P-384
+# N = -26
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52959
+X = 88dae96dc76ab8cb2b88d363d1fceec6a4373c71c90399a7cebbb25a07792c4314fcdf3bc85652769c2cd48bb6526e80
+Y = 428937bdbbf05cb444ac12edfef35e9f329a3bb9658f5663958beceb349ee7315e7bcdc0f33b556f86f9e7ae745a6865
+
+Curve = P-384
+# N = -25
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5295a
+X = e4c6d5cb9183a6709ee9eb526794676207c4d76fab5ce0a3555746bd28fda97431a8a58b3dcb2ca446f41be83716be9a
+Y = 5c8dbaadc55a87ba3ffd80e9a228b356a6bd1e72ea6dd0df9d7f47501a295a5cf656a84353b68dd30050ab2919553768
+
+Curve = P-384
+# N = -24
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5295b
+X = 0b2b78bdb399c17bb3c12c1dff5b51a9f826f0160455eccb7f4ba75043f7ce6b133f171d399bb367b8fc503a52c7119d
+Y = c4a419bfa1e78f2e9ca0ea7ab18a86b216b0e5d6be6572ac42ee8111bb92f50d3f5609cd790994ae7b3142bcb93b97cf
+
+Curve = P-384
+# N = -23
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5295c
+X = 3cb647a5df014c23e741a4a8000d5eb822c0102fd4293a78a68749c30c7f9aa484a900a91f7c4ab51e555ebc684aac81
+Y = d7cffb865707722fb69907a1423e734b9efc384fde7437c885040faa81529c96f641de08beb4641d8649a016f2a5d4eb
+
+Curve = P-384
+# N = -22
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5295d
+X = 1a3abc4dfb1d29d848746b20fe44656bc320904bf33c4192c00e43cbf53606dee42d354877204a5831f375ccf9ada337
+Y = 1cb6a3ded2b6c6ef8e3f5dfa47a6f662073bc2e5247f500d43ebc3e866286bbcd2ff8f93ff77d8a257468af04c292fa6
+
+Curve = P-384
+# N = -21
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5295e
+X = 27935df4e25c6f47c7817121057d46e1606290985f8283f5f9936136bf42cdb746a37313df88fd64626b4c175eb77422
+Y = c7f5e5c4b7672b32654e5c8672e1cadb5b0d2b513f863948a7a263b1939ea4abd4907266c710f3ec62a9f915ef39607b
+
+Curve = P-384
+# N = -20
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5295f
+X = 605508ec02c534bceee9484c86086d2139849e2b11c1a9ca1e2808dec2eaf161ac8a105d70d4f85c50599be5800a623f
+Y = aea7117869d53947e0ff5efc47abc5f8c7e489c65c59a0ecac510ee48ccef92116d16647721c984b71dc73c825271122
+
+Curve = P-384
+# N = -19
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52960
+X = 8d481dab912bc8ab16858a211d750b77e07dbecca86cd9b012390b430467aabf59c8651060801c0e9599e68713f5d41b
+Y = 5ea6d00fedeb9f7a841660d59f996faf4dd6e4975efc655fa6b4cd028523f172ee0045a8f7ffb19b966a4f828a1addba
+
+Curve = P-384
+# N = -18
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52961
+X = dfb1fe3a40f7ac9b64c41d39360a7423828b97cb088a4903315e402a7089fa0f8b6c2355169cc9c99dfb44692a9b93dd
+Y = bac535edbc4a1394bdc5975daa781e9ec59cb3e3bd2d118193a80bb65e36e2366e9748fb913f580c85c99e7bdcc13add
+
+Curve = P-384
+# N = -17
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52962
+X = 4099952208b4889600a5ebbcb13e1a32692befb0733b41e6dcc614e42e5805f817012a991af1f486caf3a9add9ffcc03
+Y = a1306b8887ccfa67c6b8ba6b509fca67e9c52c07ff752f32648682d880dc774bfb25b2cf55697f13059af10b1dc4f65f
+
+Curve = P-384
+# N = -16
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52963
+X = d5d89c3b5282369c5fbd88e2b231511a6b80dff0e5152cf6a464fa9428a8583bac8ebc773d157811a462b892401dafcf
+Y = 27eadd621ed6f92dbe7e92a1656bb70e2be2b03bf1d5c42463545a81bbf585442edef3460b640ddc97dd48ab1454c98c
+
+Curve = P-384
+# N = -15
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52964
+X = b3d13fc8b32b01058cc15c11d813525522a94156fff01c205b21f9f7da7c4e9ca849557a10b6383b4b88701a9606860b
+Y = ead6e618206e9d59e4fb64dac9e9b4e411453b5ee28b650b7b2eeebc8c2040257c72db064d7b50af67a2a773cc08429d
+
+Curve = P-384
+# N = -14
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52965
+X = e8c8f94d44fbc2396bbeac481b89d2b0877b1dffd23e7dc95de541eb651cca2c41aba24dbc02de6637209accf0f59ea0
+Y = 76e51bbca903751f6cd4340921ad3756cc479e6e188d728637ce6bec5f62f0b603b9745eaaf621dd2811a362e4984777
+
+Curve = P-384
+# N = -13
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52966
+X = a567ba97b67aea5bafdaf5002ffcc6ab9632bff9f01f873f6267bcd1f0f11c139ee5f441abd99f1baaf1ca1e3b5cbce7
+Y = 21e4c74c6760cce79bb1beb850e9b133ae7aa6afb96cd13f79cd641fa87a82988894347c8dde75829bdc5ed9c90bd633
+
+Curve = P-384
+# N = -12
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52967
+X = 952a7a349bd49289ab3ac421dcf683d08c2ed5e41f6d0e21648af2691a481406da4a5e22da817cb466da2ea77d2a7022
+Y = 5fcdf0507b4a43fa9cfad215190990d1f6047fc931e75f1446fd74f69e694af1fce559b9768bc1dd610945341de42e91
+
+Curve = P-384
+# N = -11
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52968
+X = 099056e27da7b998da1eeec2904816c57fe935ed5837c37456c9fd14892d3f8c4749b66e3afb81d626356f3b55b4ddd8
+Y = d1b3f3dcb1cf5469977afaabb53a1fc6903b1127203c9c02bc006c0be4ad5cd9ab992aec9c5500ca82a2457fc73a1f44
+
+Curve = P-384
+# N = -10
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52969
+X = a669c5563bd67eec678d29d6ef4fde864f372d90b79b9e88931d5c29291238cced8e85ab507bf91aa9cb2d13186658fb
+Y = 567748d5183ed860dd26f7c24a0f132208fee6aaf3e7c3ce3afd20873c48fa56d6927e69db7d77266887b09648c5de22
+
+Curve = P-384
+# N = -9
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296a
+X = 8f0a39a4049bcb3ef1bf29b8b025b78f2216f7291e6fd3bac6cb1ee285fb6e21c388528bfee2b9535c55e4461079118b
+Y = 9d3881ebc749fe29bad3b5acdd3c56866564c2835c3bff39489877ab51264cfc618bc100202ae497d9d25b075399b507
+
+Curve = P-384
+# N = -8
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296b
+X = 1692778ea596e0be75114297a6fa383445bf227fbe58190a900c3c73256f11fb5a3258d6f403d5ece6e9b269d822c87d
+Y = 232dc9a8ff2bef957cac7745c24702f1ddaab52392ade32b42e3cf3d13f113e594521e15322e8f729095405cfdd4f52d
+
+Curve = P-384
+# N = -7
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296c
+X = 283c1d7365ce4788f29f8ebf234edffead6fe997fbea5ffa2d58cc9dfa7b1c508b05526f55b9ebb2040f05b48fb6d0e1
+Y = 6b8a366f9e1be47745ad102473e96fb8e59e2798128668d62636d32fe242dda8cf27b120cd5870619b99b3263aed1073
+
+Curve = P-384
+# N = -6
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296d
+X = 627be1acd064d2b2226fe0d26f2d15d3c33ebcbb7f0f5da51cbd41f26257383021317d7202ff30e50937f0854e35c5df
+Y = f68995b34c074e3de41922593eb0ea8a4d36acad9bb088b36679b09ec8eabbe8fb3ba4717b1e9acee8cc5bf82c0f06cb
+
+Curve = P-384
+# N = -5
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296e
+X = 11de24a2c251c777573cac5ea025e467f208e51dbff98fc54f6661cbe56583b037882f4a1ca297e60abcdbc3836d84bc
+Y = 705969388bbf06d2f0a7c816f5ff183ad7b4bb88ab2a211773679acc496fe513ce889791f51704cce7bbeb55193e8ec5
+
+Curve = P-384
+# N = -4
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296f
+X = 138251cd52ac9298c1c8aad977321deb97e709bd0b4ca0aca55dc8ad51dcfc9d1589a1597e3a5120e1efd631c63e1835
+Y = 35351d679659d1e9ce175d7e7e54a99e923ba26e7543f60c54f19c3086d55b22128c7840c8445a96ab60e3fe4d8f1298
+
+Curve = P-384
+# N = -3
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52970
+X = 077a41d4606ffa1464793c7e5fdc7d98cb9d3910202dcd06bea4f240d3566da6b408bbae5026580d02d7e5c70500c831
+Y = 366a0835f4f3bd7c82f44169fd5603667adf4be37aeea55a0897b3f123eee1523db542931b4a2d6749a0d7a0f5d0e20e
+
+Curve = P-384
+# N = -2
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52971
+X = 08d999057ba3d2d969260045c55b97f089025959a6f434d651d207d19fb96e9e4fe0e86ebe0e64f85b96a9c75295df61
+Y = 717f0e05a4e4c312484017200292458b4d8a278a43933bc16fb1afa0da954bd9a002bc15b2c61dd29eafe190f56bf17f
+
+Curve = P-384
+# N = -1
+N = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52972
+X = aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7
+Y = c9e821b569d9d390a26167406d6d23d6070be242d765eb831625ceec4a0f473ef59f4e30e2817e6285bce2846f15f1a0
+
+Curve = P-384
+# N = 0
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+X = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+Y = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+Curve = P-384
+# N = 1
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
+X = aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7
+Y = 3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f
+
+Curve = P-384
+# N = 2
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002
+X = 08d999057ba3d2d969260045c55b97f089025959a6f434d651d207d19fb96e9e4fe0e86ebe0e64f85b96a9c75295df61
+Y = 8e80f1fa5b1b3cedb7bfe8dffd6dba74b275d875bc6cc43e904e505f256ab4255ffd43e94d39e22d61501e700a940e80
+
+Curve = P-384
+# N = 3
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
+X = 077a41d4606ffa1464793c7e5fdc7d98cb9d3910202dcd06bea4f240d3566da6b408bbae5026580d02d7e5c70500c831
+Y = c995f7ca0b0c42837d0bbe9602a9fc998520b41c85115aa5f7684c0edc111eacc24abd6be4b5d298b65f28600a2f1df1
+
+Curve = P-384
+# N = 4
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
+X = 138251cd52ac9298c1c8aad977321deb97e709bd0b4ca0aca55dc8ad51dcfc9d1589a1597e3a5120e1efd631c63e1835
+Y = cacae29869a62e1631e8a28181ab56616dc45d918abc09f3ab0e63cf792aa4dced7387be37bba569549f1c02b270ed67
+
+Curve = P-384
+# N = 5
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005
+X = 11de24a2c251c777573cac5ea025e467f208e51dbff98fc54f6661cbe56583b037882f4a1ca297e60abcdbc3836d84bc
+Y = 8fa696c77440f92d0f5837e90a00e7c5284b447754d5dee88c986533b6901aeb3177686d0ae8fb33184414abe6c1713a
+
+Curve = P-384
+# N = 6
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006
+X = 627be1acd064d2b2226fe0d26f2d15d3c33ebcbb7f0f5da51cbd41f26257383021317d7202ff30e50937f0854e35c5df
+Y = 09766a4cb3f8b1c21be6dda6c14f1575b2c95352644f774c99864f613715441604c45b8d84e165311733a408d3f0f934
+
+Curve = P-384
+# N = 7
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007
+X = 283c1d7365ce4788f29f8ebf234edffead6fe997fbea5ffa2d58cc9dfa7b1c508b05526f55b9ebb2040f05b48fb6d0e1
+Y = 9475c99061e41b88ba52efdb8c1690471a61d867ed799729d9c92cd01dbd225630d84ede32a78f9e64664cdac512ef8c
+
+Curve = P-384
+# N = 8
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008
+X = 1692778ea596e0be75114297a6fa383445bf227fbe58190a900c3c73256f11fb5a3258d6f403d5ece6e9b269d822c87d
+Y = dcd2365700d4106a835388ba3db8fd0e22554adc6d521cd4bd1c30c2ec0eec196bade1e9cdd1708d6f6abfa4022b0ad2
+
+Curve = P-384
+# N = 9
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+X = 8f0a39a4049bcb3ef1bf29b8b025b78f2216f7291e6fd3bac6cb1ee285fb6e21c388528bfee2b9535c55e4461079118b
+Y = 62c77e1438b601d6452c4a5322c3a9799a9b3d7ca3c400c6b7678854aed9b3029e743efedfd51b68262da4f9ac664af8
+
+Curve = P-384
+# N = 10
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a
+X = a669c5563bd67eec678d29d6ef4fde864f372d90b79b9e88931d5c29291238cced8e85ab507bf91aa9cb2d13186658fb
+Y = a988b72ae7c1279f22d9083db5f0ecddf70119550c183c31c502df78c3b705a8296d8195248288d997784f6ab73a21dd
+
+Curve = P-384
+# N = 11
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b
+X = 099056e27da7b998da1eeec2904816c57fe935ed5837c37456c9fd14892d3f8c4749b66e3afb81d626356f3b55b4ddd8
+Y = 2e4c0c234e30ab96688505544ac5e0396fc4eed8dfc363fd43ff93f41b52a3255466d51263aaff357d5dba8138c5e0bb
+
+Curve = P-384
+# N = 12
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c
+X = 952a7a349bd49289ab3ac421dcf683d08c2ed5e41f6d0e21648af2691a481406da4a5e22da817cb466da2ea77d2a7022
+Y = a0320faf84b5bc0563052deae6f66f2e09fb8036ce18a0ebb9028b096196b50d031aa64589743e229ef6bacce21bd16e
+
+Curve = P-384
+# N = 13
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d
+X = a567ba97b67aea5bafdaf5002ffcc6ab9632bff9f01f873f6267bcd1f0f11c139ee5f441abd99f1baaf1ca1e3b5cbce7
+Y = de1b38b3989f3318644e4147af164ecc5185595046932ec086329be057857d66776bcb8272218a7d6423a12736f429cc
+
+Curve = P-384
+# N = 14
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e
+X = e8c8f94d44fbc2396bbeac481b89d2b0877b1dffd23e7dc95de541eb651cca2c41aba24dbc02de6637209accf0f59ea0
+Y = 891ae44356fc8ae0932bcbf6de52c8a933b86191e7728d79c8319413a09d0f48fc468ba05509de22d7ee5c9e1b67b888
+
+Curve = P-384
+# N = 15
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f
+X = b3d13fc8b32b01058cc15c11d813525522a94156fff01c205b21f9f7da7c4e9ca849557a10b6383b4b88701a9606860b
+Y = 152919e7df9162a61b049b2536164b1beebac4a11d749af484d1114373dfbfd9838d24f8b284af50985d588d33f7bd62
+
+Curve = P-384
+# N = 16
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010
+X = d5d89c3b5282369c5fbd88e2b231511a6b80dff0e5152cf6a464fa9428a8583bac8ebc773d157811a462b892401dafcf
+Y = d815229de12906d241816d5e9a9448f1d41d4fc40e2a3bdb9caba57e440a7abad1210cb8f49bf2236822b755ebab3673
+
+Curve = P-384
+# N = 17
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011
+X = 4099952208b4889600a5ebbcb13e1a32692befb0733b41e6dcc614e42e5805f817012a991af1f486caf3a9add9ffcc03
+Y = 5ecf94777833059839474594af603598163ad3f8008ad0cd9b797d277f2388b304da4d2faa9680ecfa650ef5e23b09a0
+
+Curve = P-384
+# N = 18
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012
+X = dfb1fe3a40f7ac9b64c41d39360a7423828b97cb088a4903315e402a7089fa0f8b6c2355169cc9c99dfb44692a9b93dd
+Y = 453aca1243b5ec6b423a68a25587e1613a634c1c42d2ee7e6c57f449a1c91dc89168b7036ec0a7f37a366185233ec522
+
+Curve = P-384
+# N = 19
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013
+X = 8d481dab912bc8ab16858a211d750b77e07dbecca86cd9b012390b430467aabf59c8651060801c0e9599e68713f5d41b
+Y = a1592ff0121460857be99f2a60669050b2291b68a1039aa0594b32fd7adc0e8c11ffba5608004e646995b07e75e52245
+
+Curve = P-384
+# N = 20
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014
+X = 605508ec02c534bceee9484c86086d2139849e2b11c1a9ca1e2808dec2eaf161ac8a105d70d4f85c50599be5800a623f
+Y = 5158ee87962ac6b81f00a103b8543a07381b7639a3a65f1353aef11b733106dde92e99b78de367b48e238c38dad8eedd
+
+Curve = P-384
+# N = 21
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015
+X = 27935df4e25c6f47c7817121057d46e1606290985f8283f5f9936136bf42cdb746a37313df88fd64626b4c175eb77422
+Y = 380a1a3b4898d4cd9ab1a3798d1e3524a4f2d4aec079c6b7585d9c4e6c615b532b6f8d9838ef0c139d5606eb10c69f84
+
+Curve = P-384
+# N = 22
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016
+X = 1a3abc4dfb1d29d848746b20fe44656bc320904bf33c4192c00e43cbf53606dee42d354877204a5831f375ccf9ada337
+Y = e3495c212d49391071c0a205b859099df8c43d1adb80aff2bc143c1799d794422d00706b0088275da8b97510b3d6d059
+
+Curve = P-384
+# N = 23
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017
+X = 3cb647a5df014c23e741a4a8000d5eb822c0102fd4293a78a68749c30c7f9aa484a900a91f7c4ab51e555ebc684aac81
+Y = 28300479a8f88dd04966f85ebdc18cb46103c7b0218bc8377afbf0557ead636809be21f6414b9be279b65fea0d5a2b14
+
+Curve = P-384
+# N = 24
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018
+X = 0b2b78bdb399c17bb3c12c1dff5b51a9f826f0160455eccb7f4ba75043f7ce6b133f171d399bb367b8fc503a52c7119d
+Y = 3b5be6405e1870d1635f15854e75794de94f1a29419a8d53bd117eee446d0af1c0a9f63186f66b5184cebd4446c46830
+
+Curve = P-384
+# N = 25
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019
+X = e4c6d5cb9183a6709ee9eb526794676207c4d76fab5ce0a3555746bd28fda97431a8a58b3dcb2ca446f41be83716be9a
+Y = a37245523aa57845c0027f165dd74ca95942e18d15922f206280b8afe5d6a5a209a957bbac49722cffaf54d7e6aac897
+
+Curve = P-384
+# N = 26
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a
+X = 88dae96dc76ab8cb2b88d363d1fceec6a4373c71c90399a7cebbb25a07792c4314fcdf3bc85652769c2cd48bb6526e80
+Y = bd76c842440fa34bbb53ed12010ca160cd65c4469a70a99c6a741314cb6118cda184323e0cc4aa90790618528ba5979a
+
+Curve = P-384
+# N = 27
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b
+X = 9c5fa2c13f418e623c316d5a82c8b70508e3acaa2b4a8d3a4acb49c0d7ba04e220761bc15898e1b06b4eedfa23e2e546
+Y = f641de0cb075851a5afe81503cb7bf6194fdf1b7da9e59556f015651bd9218e30ef2c4b2213f519b529fb56479f48752
+
+Curve = P-384
+# N = 28
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c
+X = 1f1eebd69cfac8d8d63fd685f9fe8d6bd99ba6fb33270854c8bade1c652b661b64f1d4fefa439e57c7e6a187c296ce28
+Y = 3fd42332af05a93a24c695bc78c48efffc3ac147611eacb16c02c48e105ef0d2d93fbe57b32a2af746d7ca5dcb307e14
+
+Curve = P-384
+# N = 29
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d
+X = f1bc35f9656c6eead7b75810ce554ee56e0af4f41cbcde50fa46d04f4d4dfca2ad69e407f57b1e27977bb83b1eb1a373
+Y = 2458497a559bcdccf733c547c0f2b3dc8d7aec776d94fc70531f70960a30e8383ffe8f5147848a0e53e0d05ef28faf29
+
+Curve = P-384
+# N = 30
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e
+X = d43bef39667b4579be8d64327f27b3e7a5e64b4d7661e143ba3b0775f23dd1321ffb816b13a131da163d59596752a5bf
+Y = 06d2872900beeb729165b49d069fe5da875f11540a4044896063311e47b150edcb1f76487340e47a0b6f05af2ff43553
+
+Curve = P-384
+# N = 31
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f
+X = f2e6f06f0ea533e804fcfa12c087d446c73e3336394c98c59d3993124d374e62061fa485bd1b8a650c8361dba9eb28b9
+Y = 8f86e464c200bc46f9e442612fcffbd14a041a8651780d313fb0b9ac114251cd3e8802c6d2fc9ebf3152f5ba7852680c
+
+Curve = P-384
+# N = 32
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020
+X = 7f7356c5e0fbc6678bab99df1fd9b2b49f81618d6d99af63612ccf2cc4acaf5c44819b88e217ecd3cce82fe55ff86ed6
+Y = e003b31de2050a4a43d1a5fb9b4ca6622bc55e5de0c3e6f2ea6b40995968c751c75d1513aa614bc253ee2ce86961877b
+
+Curve = P-384
+# N = 33
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021
+X = 38dbd58238c21bbca92758f2773bb6c955492715879f54bdaf246b3f70bec90d631eaed97add3fe2aefd7fec482c802a
+Y = 828c26694df8188912a77e148373d30feff54a716511f5f17b86f2aba9bd9527d3145ea7abbf4370599a066726d333bb
+
+Curve = P-384
+# N = 34
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022
+X = 1aee48c2f0f4678b942a66ccd2385edbd1831e58722e5d974aa2569489b3a75599741028cd1141c4266e9778df65df0f
+Y = d708b891d689453b9f0d45bf1dc647138b1f34923b129e3f4766b3b5874440ee54767d26bdb994c5fd0f8850a10f6502
+
+Curve = P-384
+# N = 35
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023
+X = 77c12dceca498ff99b9863d46535a5abc3675bf4f728f3ac55a0270e5859145dc998b7fd7b3813ca29e4ae10ae1410ea
+Y = eee351bc34276d3ad302f0d0bd10269e396e7fb8964f430400487a48cee2b22cc20c5f0bbef2267335747e089cbeef77
+
+Curve = P-384
+# N = 36
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024
+X = bc7f0d480a8627fcdcbdf8371a720e49499d70a40ba07ccda1159432b4aa3c9ff05c7a3bcce6bb083bf618c1f6729597
+Y = be9f24404081a7248d7b35b54e6d08cc2a0f0293a34b11b4fcdab03995fb4b3e8ed3f88d40f10cc69b327a19f2675d7b
+
+Curve = P-384
+# N = 37
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025
+X = 1221b1e1be96dfe8fdb4300af2deaadcd850ac1e45c8fcb941c36c0122a10654e56ba2d9cafcb4482a07272d05de389d
+Y = 50b225fa8a5ed8381ad8460e2e216618059953dd8965d97db233124188d17ebb5f4f2a9231bf6723533ffb21939dd273
+
+Curve = P-384
+# N = 38
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026
+X = 081dd3666a57be69b8d22bf15ec27b014b32060b20f018c3f0467a5eb8c359725c7bb4e179bb42b5acf9732879a8bc72
+Y = 607f2d46f5f3e8cd4b375f0810596288a461bf9202e7cf9ad4d616fa567566b39a925dd3c89e25cafc45139db0afa03e
+
+Curve = P-384
+# N = 39
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027
+X = 9bc6b654362621678224c2dcc11702de31c65d0bd2d3642c35b72c15f264347c4983e9bc8a47b24d15ac797ea8f8fedb
+Y = 4482f093849864ec7147eed091efb368dba18218185770c722e5893043a248bf6ad799aed585d888c0979304abbcfcd4
+
+Curve = P-384
+# N = 40
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028
+X = 37cf990dee523641d57328fb34cdf4dbd3795a0b7df5072f1c67e1a4991b2d388809a316e9abf06d301d2ae1c1283ec1
+Y = 6f1e8aadeca6573e073058459ea96f6ebeb7888f04e7337a6e44104c907e773faa32b3075527675b1676f6207ed89b10
+
+Curve = P-384
+# N = 41
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029
+X = 2ce37b2917ad0d5c411a5ed920a88b095185d456b279a18410d10234b609b8c7077b05ca6a94f76d10f3f39e87c76293
+Y = bf27e3e07ba0ebb9dabde6f484adc04fbab5b7b88f8035478208f6db859227bb4dda48b4fa5d1dfdcf8a7d7be2a7607e
+
+Curve = P-384
+# N = 42
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a
+X = e035e8b33efae0987f6b994529285be4a9d55093d2b9c155c086e2b3c025a8c1c860097bb529b268b7ff95719518ac0a
+Y = 97ec427650d600ee6cb34cd52ca0939b67b1549fa42eb2b56bc12979167a090075510dffbc395a4b6e04bec3a3ec4ae9
+
+Curve = P-384
+# N = 43
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b
+X = 9c3adb9111fe252ca5a2ff485cf5a64f4de52ef9689a5eb5aba8ddfa4745a605aa49a4148fbebc6780dddc249a26a33d
+Y = 83e8d95f813af4db50b9f80b22b4829a69dcb8452f0576634c69c4c0108a6a32dbae5b8175af7420344000dc47355061
+
+Curve = P-384
+# N = 44
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c
+X = 3a67a337e9f9801a467c5ab6d69a1f94757634a28a2d794c0abe75b32c89fc4182cfcdb920e29ee6228d345d53f529c0
+Y = c87674b4822229d425aac1d9a5d9053d73853e145a21312cd3f31ba3fc9e60e068eea87d25b1d7a1d713facab8a030e3
+
+Curve = P-384
+# N = 45
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d
+X = 6ce3bf11431d1d9c3bce43bec4f4609eadd9c644fd8393e3258449c2e28cd17cb03d491de84ea016a5b68a56bbfef50f
+Y = c3325e274b23604e7c76075539542abd918800e37b4e4aa858d74fe2bc8777e08105da32421e5cfe36dde9917c570471
+
+Curve = P-384
+# N = 46
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e
+X = ebdbf7a1fbd628764fbb7a168061b8c7c6e3211321841d9ae8e9bece5debbbd3a33fac59a8097c72fa4c28f0bb83b8f8
+Y = 3019decdffb3e81cf0b8083e208073c5ab6d343d27f0b3bfd0051160a6f0bc1cbf2464ba9df39de6f05fee339ebd9045
+
+Curve = P-384
+# N = 47
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f
+X = 1273f595eaba5df1a06f4ca5fa30e0bcc870bf64c76762c0e2a9530742a384f121e9e8ccc22e6b2cd5254db0689a30fe
+Y = 62737fc8fe0818cba0db552ca682b49fa1afe32ae0ca83c213cce2421581486ca7147a032edfc8b0cbdaf9963d58f4ce
+
+Curve = P-384
+# N = 48
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030
+X = eb967d99bfe2cdfec7d895a5b4edde398642eb77303050301fcabe3798c46ace66629c442ca537cc68701396222c0089
+Y = e93e5d26e4de442a3f23a7177d6419daa55a0fc9db0ff8a6ac1b2a2b31a411ef40d943c576799210792d6a2f8cb58f46
+
+Curve = P-384
+# N = 49
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031
+X = a6c36459d1243ff7f504f32fc1211afd7486cc53003ed182dd74d976a49117348828f5b191d03d1c9da43e8e5a0e8cf2
+Y = 58279fcdf96c71d91cebd105c28a71d00bd72553fa3365b422899f57821ad25b38a1bc0bc57b89f62e6ddb768c63b40a
+
+Curve = P-384
+# N = 50
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032
+X = ae98b89e108d372ac57c52eed44931f735cc34c5b98037cff20c1c93b6706101dd56a283b13f9a29cf0106d9107660d7
+Y = 62e97c1ae6f81e242905070fe16e3e0711f95e8da1954cff864fe475bde033e7308682829a9ce29ea439118f89027c49
+
+Curve = P-384
+# N = 51
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033
+X = 8bc6057da46fdafba86d2c9e23c2f5bc0b4a2a1247e8f2cc7126ed3b510b0341a618a8cc08d1b992ffe06fbae0068fb0
+Y = fea19f27f6881a60fbf2a22fd1f1985d777fe7595994ae0d42fed20299ce970554c6d084dd6077f56cb597b8e22c2ce9
+
+Curve = P-384
+# N = 52
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034
+X = e217fd47cff91786a8eb87b0cf545dca6a0eca0fa0bbdc7aaad84294bd6fa726a3131880b817b7828611cae7d96a1985
+Y = c28353bbedab27baa7cfb7be82e1cadb6e123cc117b4508de5822815740d284603f44676d6a06cd5df4b4ed962b25037
+
+Curve = P-384
+# N = 53
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
+X = a0e5cd10144167c592288353932cf40888e935bdd5f169ba8f41cbe07c3e20b6ff2ca2a90bdc08ac90903c9d776e9fa3
+Y = 120de79718d154feaa542f5c1e7ce4bed6396c943a16024ee7dbf0ae17f7fd0a0c9dcbea9d4197aafc5c555ade9e587e
+
+Curve = P-384
+# N = 54
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
+X = bb0ff1b32114d1740890f276e61f8041c55c05c89ed8af7c8776542902a392ad231a5a3dda24dffe6b2de3474fadeb4e
+Y = 46fef980122ffbf385844b0fb2d92be15a668d05a957eb5ff29b7c2d30109c70f2c3a60087864093d1041d10434f7032
+
+Curve = P-384
+# N = 55
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037
+X = a059cd75571b3507307d064e7750ab08f36d925a4f0112f5f22f519eeae1eb0130d0a0e2e2ee7c873bb25771ff2b4f85
+Y = 40f6ae62c5a75f70026d8cebfcdfc32efc311351556408ff0fa0147d878235aaa880c76b8849bcd5aec87aa569c8d6cc
+
+Curve = P-384
+# N = 56
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038
+X = 2d56e829a6fb82c64a4697d960af951e9067ae91f926d34306da48a8aaeb7869b6a67e6d27426e0442aa03b36e1988df
+Y = f636514ee1cf07f5ba4b3ff2a06645ae7d31d5866ad31aa3d246eb0b3177ee99cb7566c516ce74808a668f91f0c47fca
+
+Curve = P-384
+# N = 57
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039
+X = 8654ddc7eb10d5a84f1cec9879cabece6c3eb8b272461fc778baa6599653227907fd3823a2b10416709d6a38b6ecb8bd
+Y = 3127066c11092e88830567af763ec9d0742599e648fd7ea47d1d13d1aa1c7e4088d6dbd5abb99b51b867bea8e3578549
+
+Curve = P-384
+# N = 58
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a
+X = 2dba210e4d863e5dd6b431f835c444c8fa54c48cf585a1ee081f91a2141db69c5ee6baa05a4f63e8ecedcd4f9e4d8bcd
+Y = 39e1d4db25fbe304b142563a398eb46fdddcf31926b7291e221acafad77c6f9918928b79530be249e12b28e0683ab0a6
+
+Curve = P-384
+# N = 59
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b
+X = cc42cb6ad9f7161a4296f482176d1b0191e7691569202b98a2b93e41e5a75d8abbf7b15b29f59772ba77d70f99fe20b0
+Y = bbd6bb6bad8516d391646e65069fa685ea4223f449e3895ec23168cdfb54deb9182916746338781dda74d5345e24d63f
+
+Curve = P-384
+# N = 60
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c
+X = 22f2d6f4248636552c2f806ca08d835bf6c15cd3df2f83cff6ea265604a50599458e0d6d3f0d7daf3a81734e1a5b276a
+Y = 6ef18b660c2e6081e886affb20d498900a3a587c8708464ddb41dd6f56b6c8f3726b3e968c4764b22c2f95f47c81be70
+
+Curve = P-384
+# N = 61
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d
+X = 0a12151e620a81a2870b7900c1c476895f729644b4fa011fa92542dbf5872edcb5e5a6d1fb4071db5c24cc10bda2cc75
+Y = 857c802960eaebb7e15002475bc0a80c4479d4c75bb1d9f1cf6593451a9a069884fb986767fb8c48a13e157ec5a3e83c
+
+Curve = P-384
+# N = 62
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e
+X = fad756f52978ca376b086258bfaaac8a3a42d10c5d6cb507c8ca4c2efce2a9692f29d569dcf86b74aacc18a3c5d13f84
+Y = b7ab1300756bca999ce6cbede28f0e8ce434752f57dad2ddb89094adcbd9cc9b2d4686a37a566e2cbee9f3fa5a0f85c7
+
+Curve = P-384
+# N = 63
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f
+X = b32e89eb942aab112cae508f5164ca6cac334769de358b333c968cfbb83c8f7b941560583def5431afd574f7e008f8af
+Y = b1e3ae7166d9710324e2bf69603ffd5ba770fc48b73edc0679cddd7a0499009535daedff59e74a1ac81abe0944e11e2c
+
+Curve = P-384
+# N = 64
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
+X = c1e179aa178a780046e35841766073612ea5e5abd26608f259a599fc9a8425a3ea639d94cbe63fde1d69c70f9327605f
+Y = c3842f6e4dbca6d609ba6171650371b2adc07640a2420eabf7e8291eafa21b56eef31860c20ddeb88b45f0b4856f8ea5
+
+Curve = P-521
+# N = -64
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863c9
+X = 01ab9aa17dce1112ecd14f3d7c0392fec2a67ebdbba81860bbacb614b9ccf8872d271bebd3c5efda3773a7c02c1603e7001df5aab8029a04fd41b53dc38ce320f742
+Y = 018b728f147b716dfa8c55efde83a846a792644e3374d04e23e2f39c45c96178547453e9a45eaf72b4ef0b58461c506a7fbc3e3e5a245f9ac22ba50e8fa75a0720a4
+
+Curve = P-521
+# N = -63
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ca
+X = 01c132753b64640c457fe82f799acb0a6e8e1ca21b5affa0ddc1e7f54fe4d51b08453ae99661308e125ec63996847586265e75af7d025770ac8d0f5beb6fce8888e5
+Y = 00acb81e7be685fac7e8927e53764dfa2869e561f6cd79bebff9a6a173cac55ddc7ffa2c242313e76943ec08707d0ff8ed7c509a84a4099b5d53162ea52d5fc245ea
+
+Curve = P-521
+# N = -62
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863cb
+X = 01f605dada534c5c8ae020f6ed49f27734e2378b0b7bc177aeaf943ae59581dd5885a38bf246fec516fd213f3fe32bfd4e19f0a8c971ef2a16969627fe31114dc85e
+Y = 00b006927d9674872cfa5ddad385b41c75c80d4f50296ca0b555af197a332e9b5ddf9d7b57ce094d691534ea9491898e1485225d281f8d5a875ce60effdddb55a84a
+
+Curve = P-521
+# N = -61
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863cc
+X = 0115544c4a011407425c92ca7c732a1c7d8f8b6cd17d5b660d1a4cd724b1be8b17177193c11a77dc0b39a5e7fce59a7b32d7952bad1671543c41cb53d8cfcfc376a1
+Y = 00eac206393f539b7a4cf86692d5c66378d38650b7a010bdd34013f685c512a7395ffc0872ab480aac5757cb009fc505fcd4a90dd1163c01a1db47100732b5e89fb4
+
+Curve = P-521
+# N = -60
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863cd
+X = 00038a508e954435eff6ccb82b562ed32ff236f33caef52de9fdf79836e37bbe118392681661adc786c57fb6bf92d04b42fc051c3f6061c252749fd17ffbfca45bdc
+Y = 01724278274e1842b2d524858c6646ce18e964f13071c8455f1666820e5ad6a9f5a5a124e1859fa5402fb4e3320597cd7a4b28b708c8660c1a2b07b9b52c6ef16add
+
+Curve = P-521
+# N = -59
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ce
+X = 0035aeb454ad31876773063c60342df29eadc7223c5d98901999aae5c2934c222920055693c9b1344c691a90342d64373a8acba8524ec347208863be8a26eb16686b
+Y = 01e7813413eb8181bc4b24f4f35f500e4580ea5b724c99f6fc0b1dd0d10c349102ba473073bb437c2f473602a02229b0013c027d3c7305001ae530b55fc3ac7e05d1
+
+Curve = P-521
+# N = -58
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863cf
+X = 008bc46df1c09851ab92d53b6ad821b5026be61385c801f5277c9871b54c6764d3fdb96a728ad56c650d342a03e174a3106a5479d3ccb04647ad0f583210d81ab70d
+Y = 0026b7006d87b166b95d85b3a72bf404f1f489af8c0e0658ce55ad4ac1b01979ecb01187b41370357e58c4753c5ba35e4899321396e6f406b28e05f74d6d6d3edfa0
+
+Curve = P-521
+# N = -57
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d0
+X = 00dc53c3e7fcf3c902e04463da75efb25ffa5a8defe72e6dbf85ef3c6d77a521b9f84af300bbde9118b2f66fd5bc64b8a5208372c8fff95c84503475330a4e9a13e2
+Y = 018b1770332b024266d497f8c1193b2b07084ec2f22d3506267649d1814c6f200c15cb7dacf2f2c42795b5c07dfa956d608c56b6ce6f39d5aacdcc528080263bdb70
+
+Curve = P-521
+# N = -56
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d1
+X = 003cd775038527f32baf7e9056e95b0cbef7638c12398e4b8b019cc29b3435be97f601378e253fef51d25730e56267acca241df04ea2e96c1840b3f739d5ae2df998
+Y = 0187640830bdf6a3a4e88f1e5ca9e03c1edcd4fbcf1eeb0985c683e1a4ab6780b5d74ad58c83455f005d3bce79c3e57340ea1459f04a274b8dc23ef268f90bf7c345
+
+Curve = P-521
+# N = -55
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d2
+X = 004b52fc4b6d310ce7c4b551e4155c6daca97cb03e9fd9d0a79d6472d4028e8da1a18cca93917cad27e6be17486b1e0b549a7fe9ab4bda96ac4e84ad7ccebd470f5e
+Y = 01ca7a1ab017eb9e51dec7a1586f85e584d19e60bbcee5e95f49ff04eeb582451eaaef557a76a3a5f7b32969f61cba4aca7905fc5dcff95f691585e6932dcf5c910d
+
+Curve = P-521
+# N = -54
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d3
+X = 01938e65f2a40e6899c6414997e998e0e3e1cb09728861ea138fc6d866fdc6c98505393cf55982451c97dac4214e4dbf052bf707cab63228fdfcf5ae2048a0ce100c
+Y = 009c2aa922f6be3186b163d797596d1762406bcb7ae791df8b17ddd1f1266015b42f39f96057fe824741368d46611f280343f09f82988b6d9a6405b84674472e4bee
+
+Curve = P-521
+# N = -53
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d4
+X = 0143f6e249195ee693f77b3d0ffd310306dff8688be916895fb727e82f6f948100c44e210e637209a78ed3af581fba4fe12b08153da9c98808166285684cccb69906
+Y = 00fac1703369e714d58b3a9ee5fda8aef6788238c0221bd3de96cabc817fac2ef34eb0209b0afc30c44cf09585621c4b3073fbf5c2043b541195029225222bf3879e
+
+Curve = P-521
+# N = -52
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d5
+X = 001f32e875ce65d1e6d4c39a55b518749158a4dae03e0c1b4b4a58aef6be769f0693b9259354dcee33a4f00784311b6e5993955114f2afcab7a186177e368b29f6f3
+Y = 01a5c371890487b6e7f476a1574dd11e5903d3406c4451f0b52b5f3ed92ce73c686ee18c5f9e47f6756eeeda88896328810d4294ba06760d6d256cfe66d2b7e4f807
+
+Curve = P-521
+# N = -51
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d6
+X = 0168395ba51e278415a2025d93b68145f3ccfdf9f5bde34fe9ba3ba316cbf9beea26c2edfe6af07390df1321ae1b1054cbc0fba689ef1d7be2dda8d916fffbcc9504
+Y = 018e33ef0c35fbe5dba9c626ace6bd2c0a86ad227870de3b7f31db1a4e8b521464c36843d49745249ff37b63c9f691c5145af9ff55412c5761e771463ba122a3f784
+
+Curve = P-521
+# N = -50
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d7
+X = 017176204957627b7a120baf0dbb1aa3f9ca5312b292e34a0cfbd8384cf1be365bc684804ab26d84fe02250a859beaced3f356b6ddab593aebf35386773de84d4766
+Y = 00538ec2a3d004f1eee67c1f815a09774fc22cfcd2c56baf4af3a210e24b7a64afb35073ecde449372a0526c2d1a462f18acc9d671de6c608fb0c0e953952237b070
+
+Curve = P-521
+# N = -49
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d8
+X = 00a5cb98fa3c0b8c1d57cba40a63bbaba0d39d45c347ae499839f581b8b111dea9eb6bc2a2df52f6284291d8c84e2e345fa87258dab1ac4c04ca0c8cb45049efc0ad
+Y = 01ea236a9abf6f288468ebf64fe408d1eae48b611b987881a4814f8d3c626257407ca22688aabc2a8a3df3ef0d311448d44bb469c0404f4774e2f4640b95d3777b4b
+
+Curve = P-521
+# N = -48
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863d9
+X = 01fb35de3b21557462ac8158e5caec62c8ccae7d6d6672102c334b3cc8a5803e72f42da0980f3f6fbfacdedc21b10ff08ee6641752bd214aea2c87027d612ad879ad
+Y = 0072cb5a53ea7866d21257aa4613b44c0a3e890d8036b2f61d3378ed6b09933d1e4519deb29b5e2681c8483ebd1f04f1c3d7a690db7fb7be35184e6a7e6c2da92b94
+
+Curve = P-521
+# N = -47
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863da
+X = 00afe31f8907048afd5f9cf5083ecc35882ce4e4fe2714033ff897233106c71dd0b2381864a0b9e922ac9cee75a3a9c4dd660a56a130d1ecbb672fda63c9abd59d11
+Y = 01ed06a2379a8d8a01624bd6f3444ab009659fa0f86cb6df6077131580cd890f9b686d89f7f46fb67d5914489f0b8727da18ee35bea660ef98c42cf9a2d0e26f1e29
+
+Curve = P-521
+# N = -46
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863db
+X = 00bbdb162d8284a910021998e2d14f33a8c51a9cfb9a69a8fc5709f753aaf1c4d248a6ddb5626540f81d07c09231d2ec1549f672b6d2bd57d22f64f2ae8093e738a5
+Y = 00c4790f2a6dede9101fe016383543ef4ff547e36042698beb62d131bdb4879093c8d5e7ceeed0abecd282c50bd2373281e848137f4f605a60f1bd3ebd76a69eda65
+
+Curve = P-521
+# N = -45
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863dc
+X = 004969d186aca09e53b879596b8fff2bc0207a58f7f28c147cc07b2df5e3f197286779189b7df8a1f2d9dc8ea1ace5703f0fc2954607b66b70c4a32dec600fc95c1a
+Y = 0017e1364af3272c7b82b2953de0ceb60384ea79d5bdcd8f77becaa7b5e44324bf56472a4d7fd152517a19126178641cd71ecb9c0a26f4c8a9b06b675832fff6fc56
+
+Curve = P-521
+# N = -44
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863dd
+X = 013fdb51df11f0b29b485f0c48a04cca16cfe1cecbe9fd57f6507c3cfcbef88c88cf761e3e73e6509ec7e77fffe912d2b367b25aa02cb1d33a5378f253cdcde738d2
+Y = 002eb373e7b58dd758fcb1b09a4c2fcc709985536f74c6530c63614123fbad483d0d5bb9fe7955dd8e2ca88cc15dc28d7b575edcbdb0665df4adbdcbc4a3450599e5
+
+Curve = P-521
+# N = -43
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863de
+X = 018c0148a165ec58e4fed55022d90544d8a070678aa2ec1419af8ef0f94438fea396fc66ad7aed7d37dda3a74e7a9b3d85ca51f0b62fdee07d20713e3083224f497e
+Y = 00f54455e0f661877965140857c3855c40dbd98816e24e2ebb3285cfc87e60e5f41398029f677864e30e6cd29fd4ab831e856b1246d08949772d76a7d6f9897c5240
+
+Curve = P-521
+# N = -42
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863df
+X = 0161960dcd7bd7263ed37a0a1c0aa146f918874f472a2a5de6f2f5633364979e0bda2868fa8595d78243ea20d4e83c72305e420bebfa3767dcd4b7902612a9491855
+Y = 00953e3022bcc7ea9975a3951f2efce43ede5ff19594a72e113bd259fd0fe33bc48108d72983b1c62179cdb34595d3b331f7beb02295282f8ddcc75af1cad52ade10
+
+Curve = P-521
+# N = -41
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e0
+X = 0175a140ed79e85e24a763ebfd9d2883082552a523e4b28998a685b85f23864a60c816b931eaf495ccce4c08c3ddc8297bfaddfda1a522cc2af68e00ce952624381e
+Y = 004f696928e7aa1d2b12c90decbc35c6641854a61a7f1611a9cddc217de4dd37deee4c61f1ee130cd825fad591aae05d714208a228a7180c5c13141489f13efd7130
+
+Curve = P-521
+# N = -40
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e1
+X = 002efdef97e46cd544553a0c67f41886ad969f67883679409022c4ad6090a169b596f85d832dc9767b2bb513eb4d0d01e18f067fffb5ac53dbcfaf456a57824c9d93
+Y = 012771d1c022d7ca6d293b3d5b97819a5957dc0039ef92006908a44cebe2f20ae901b95aca84de23ddbfb0b10861bb55684dd3efe0138a27e1f64a6e0c98c75171a3
+
+Curve = P-521
+# N = -39
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e2
+X = 0124a0b8f411fbad60755264126356a499029e9661a49b5b907238d9fd62359c6ea7256b0cc58626a1e2ac0bf434e5fa31795b4fa1d48083af670b704119ee33b77c
+Y = 01efd69e91238cca231c9fd565bf9442c663976c84557869eab323f4edbf396f24b7e723a6014697da555c350dbea6bb189d0ded18b49671bf542723239edd749e59
+
+Curve = P-521
+# N = -38
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e3
+X = 0195aeb1530c7d932a18922563d71ef6e7449078207b610c2c77690815bcef8f312718413823ed8d4ba112ac2ecc9d4e688346f120cb19ae965d853028d72a5f4a8b
+Y = 011f3bd2c33f6cdbdfff3f13f68a77b44fcf79ee2742b649f6f5f93a42082f1eabb7ae0645a69d6e10e11c1c365ffd4867a45d61ae2e6c48fe1f3781e53286842ecf
+
+Curve = P-521
+# N = -37
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e4
+X = 00944f64a6c21832b67139e798352ded5867c35c5d56c1d59e4229ed0ae6af9d5749bd445763a28856040e58e26ee0980df28c64e76ae4780273f1ac4d59b557a36f
+Y = 00af4811e4fdfd7548bef3b469db0d934e6d01b1900de79a07a6d9ce82594866ecfa234243db8e37c07e47a3a9b66a5fa83cce5b41bfa9677d69ced4067c6b04d5fc
+
+Curve = P-521
+# N = -36
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e5
+X = 01ad81c98659bd21e082514d5e71894c417b2a25afe471eb8ad9e81a91dac8198aa9f385da9cc679bb96e724672821b585e05323bdc821e585edae56cbf798bd6ef0
+Y = 00de8fd9193071903b189e5125a8fa4c3f073580b8abbedd84689ed7a4392370fe50e50af4f44ebffec58a2469c27967ede22d3b45e8a65674bc5388ed07baa36610
+
+Curve = P-521
+# N = -35
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e6
+X = 00ddc300757549630a78398b62069e0ba392a267a642b593e0bfc4a780b56f97392bb4806c84fda74023ee4a618608f6b2cea92ff614b5dcc8d8dc1039c9ccd7d718
+Y = 013e42d2f80953803a33e47cbce0f3cf24405aef6c934a27a430b9d869a6e5ce24af654ee8742d38febda3dedadd119618682145b24bdd9bf5b2c14e2c10dbe1f80b
+
+Curve = P-521
+# N = -34
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e7
+X = 0122f66451d0843fb59448c58962f5b7c130eb8db26b14dbf8bf0e7d84e588581cdea866a98b697d432cc1f1f8993db2173b55ac0435b8317a8ddfeab253d54e4d87
+Y = 00412ec846bddb66cd0c3e0edebe511eac755c8c0a511f5765b72ca8283fb69a4faeaf68d6c1b078595bece7b26417156ebf9455addc248a8e432562457d0265f923
+
+Curve = P-521
+# N = -33
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e8
+X = 00284195f0978fb969e68c76eb4e3c76f58e52086d482be92613f381421a74236df0abdbea7e2b77ef1ae9a519a57aa53ac2a5ec59186b2ed12d5ea2e1fc649f308d
+Y = 0181bc14f739a9239c948485c43796fa92c5db2aec541f9c399c61fe98969eb53a0d189a91340c2ec740ff428de93e9214c5771002a3dd74a70c9416a5c22c1ee3b2
+
+Curve = P-521
+# N = -32
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863e9
+X = 0166ba3596d20a3a469443707af94ba15b343e3e16654e068038dba7ecefb9e5ebd6ea04a35dc73596ec67d69bd6ba5d1f949cf7475cd6c5c78247915ab876e56a9e
+Y = 01b884f00b2e6bf547eee9ca18a00c1f7d9700842740a00fe6983e20fe39a3d228821dfb3247e732294648b21e3815f0c9203267369a5469e9c96a356566eb5ea955
+
+Curve = P-521
+# N = -31
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ea
+X = 00d8e9920cf30f0c6615006a58cdb7307b7db574589657c2a6617260a83ca5e7726cd65543a904c9d429fc14c1ac09cd6b220dcfb5e488e248122257d0e0c16a8803
+Y = 00d84fdc4abab599c67820f63ec5deb5cf1374abf9cd70ef37f9f51048d3412e55cf089473cdbb2eb86fec65797351b33d9c55e4268384abce7535b98838c686d2e6
+
+Curve = P-521
+# N = -30
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863eb
+X = 00d087d70d11074b36bb0b6913261570a6b33cf015e1f913a610ea52dbcbae2a3e4435573f35d14754c6352756cc169eac6bf7d9b10f1b0af5956117fab72ec4b081
+Y = 013b2f8073c35023e9c97ee2846a5953d2314321e326b504242c1b8fea99f2d60eacab2a4662e282b5250d861011a279f416f96122b900fe303849b29509a862412c
+
+Curve = P-521
+# N = -29
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ec
+X = 0174c644d6c94b68287db3c1fbfcb65a085ff9f45131e86265ec28bef38f7664a1305ca9bc06876d72914003ab8e847ffb9ef33d8595d51f6d962c3927618eda25dc
+Y = 00e50c583d0780be6197fa5336a4be48487dfdcc1070349bbe034a30a1486a52cb4841f1de6ad31f26f4af3d8cd28f92cfc0f9866d83d9144042e784443b7de5f3cf
+
+Curve = P-521
+# N = -28
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ed
+X = 019195646effed8e5f6fec5c29642dd68d1a988bb828d895ed5d116d7f87032133e2949da6c395738534b93220fe2ad07f0b291adc92433ee2dee160709ed9985b0c
+Y = 0088532ac4ff360d60c131d74b8db07cb0b69f92c0cb9a95ad10abbc3a26de951c774cba47d7374f65d4d1b6a30d5232294a7790b212e8bebda53ce37cd458b112c7
+
+Curve = P-521
+# N = -27
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ee
+X = 0160373edf8218f9b6a762a4d4eb889e646f8739535d0e4f862c33f35187e135854d80b2123da719d48351353aeda0d3163cb215604492ec4568357643017002d68b
+Y = 010ea68faffeb2301e3a1a7d7bfe53f95c05602e6c3a30ad3c44b5a90871e5e4ddfee105b6e126d143abec478b0b5744a8d1b9c0018f62ba534c0c195135a26f48bf
+
+Curve = P-521
+# N = -26
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ef
+X = 017b61bd55cc8b533222d9857bb0c04dcd1331a02407e9a8576609bc2cbefa11d6aef686bfc27593b717007102d5dd038ed768dd29c10c73e41060d9e9a7e8c685c6
+Y = 003cb25fa15b4621c4415fe693f97d41b9cc374b53860a5d645d34238db8f61e0bd94b22475be6e5b9b728b4a4d53cb82ecca4740093fa700a8d18b5533a464b7bb4
+
+Curve = P-521
+# N = -25
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f0
+X = 0154e84c6d5c5a9a1834a35874a6f5dff9d20073102651b107b11c772c8c5499994aa91856917c36ee4d56e87e2aa648ff27e41e0eb1c25d3fb13f8c2460bf70ace0
+Y = 0132f02316e8e6efb8c51dd5d707f449f2c0ea56dc14ffdd5f380993d777eae99959211cabca8d043e5f2e1dc8f595a68d0a1121d5ab98d19492bcda431bfb387dcf
+
+Curve = P-521
+# N = -24
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f1
+X = 00968e98d1008e9d525db447ce5d4930becf0e49a0e6ec40736691391a8862c2cd7f93ad7a9b2d0e4f666fdefaa4a86f6559c710838fa0a4198f44a18c28b69bcef2
+Y = 001e581690d6c08aee831bbb899a75cad5585aaa32d09b5d4fcd4b32d47e77f5569d5b70b6d30c2f8ac97c7a1f605ecb086cfa8a319a2282260275f4ac85f4bb98d6
+
+Curve = P-521
+# N = -23
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f2
+X = 01ae0b275d7290159376db1cacec5f547b8eee7a39ba785fb282490d234d0cdd45101f6f83fc9af5b0afe9e3f8e9a5ec53e207e1f1ac26ae5806a1c6a5ece2af535c
+Y = 010d36857da1ac7ac7f9975d7a703f97b8e3828a60076f2fa7cd4bce9b87bb3d23f1ef7bcc9e1168030bc3d84d844f822f3ddec4bbaf572dc7a927ea3cac905f2fff
+
+Curve = P-521
+# N = -22
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f3
+X = 009700ce6190c6d36ca5f1f6e66dc1eae621a7facca319d4b7aabde3122096c4089f74cbfe1018ffcd40adfffcca8b28ca1f904b3a12cae0af934e38650bb6f5a561
+Y = 0004877d81eca96ca94f9e3870529deffefb41b572db1bea063e6a1f02f93a4b97db0bf536325201fd75d0dd13ecd12eb1a3a3bcbd810159def29bce652a5aca5352
+
+Curve = P-521
+# N = -21
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f4
+X = 01a1cbb2c11a742bd2fb3cc8550859ed3b10268bb98c2ed15e9819523e759c467fa946257eae4d62a309d9497eaed827d75b37ac920393a46d2d42ef399693c8c9ed
+Y = 00e5df6282b07114c2b7d3ed2583a1bf319d17c3ffa63ae6c193e7f5d69094505808208be5ac25b7cd6a414bc5c8d39c113a8364f0e89b63349e19f43bc36345b20a
+
+Curve = P-521
+# N = -20
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f5
+X = 018bdd7f1b889598a4653deeae39cc6f8cc2bd767c2ab0d93fb12e968fbed342b51709506339cb1049cb11dd48b9bdb3cd5cad792e43b74e16d8e2603bfb11b0344f
+Y = 013a552419c09735a49496f7d696a640f50761180ad4bef46bbbab93aaf6ad2ceedfb25c4222392b1518120513efcf257107c8334dd11163036b22cd78012f66f06b
+
+Curve = P-521
+# N = -19
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f6
+X = 00998dcce486419c3487c0f948c2d5a1a07245b77e0755df547efff0acdb3790e7f1fa3b3096362669679232557d7a45970dfecf431e725bbde478ff0b2418d6a19b
+Y = 00c82a25f9d95fde12a33c6bdb68aca4dba2982d7511d48430b533af111c9aba88d88c5269c00d1473064f13c666e9ce3c880a5b2761560401bb56f6e596a402fa8a
+
+Curve = P-521
+# N = -18
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f7
+X = 01bc33425e72a12779eacb2edcc5b63d1281f7e86dbc7bf99a7abd0cfe367de4666d6edbb8525bffe5222f0702c3096dec0884ce572f5a15c423fdf44d01dd99c61d
+Y = 00f2f9166677a49caca21c18b2cc2619c2fdb04f831f2e690daad371b5ff537b3fbbdcb514dfe0856ecc6ea2e4b4badf646258601ea4e607b02eca27be1d27065795
+
+Curve = P-521
+# N = -17
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f8
+X = 01b00ddb707f130eda13a0b874645923906a99ee9e269fa2b3b4d66524f269250858760a69e674fe0287df4e799b5681380ff8c3042af0d1a41076f817a853110ae0
+Y = 017a97c0e2824e9a89243eee2b1b51222ef94866acb30966ef56729753d4dd5ecdc20625a9b10922f40f2d098a80e9520bdf196faa6b3d48aa0aca4634838f19b9b8
+
+Curve = P-521
+# N = -16
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f9
+X = 01d17d10d8a89c8ad05dda97da26ac743b0b2a87f66192fd3f3dd632f8d20b188a52943ff18861ca00a0e5965da7985630df0dbf5c8007dcdc533a6c508f81a8402f
+Y = 0185c8cbc3a7d288ffe038eb4e72c2c1968decca1b3c47ff2af13835cf36b4947d3e3e7d1ec6724ab855f4cf8a53626677ad61cffb2d72e79cccad1d8d076438c541
+
+Curve = P-521
+# N = -15
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863fa
+X = 006b6ad89abcb92465f041558fc546d4300fb8fbcc30b40a0852d697b532df128e11b91cce27dbd00ffe7875bd1c8fc0331d9b8d96981e3f92bde9afe337bcb8db55
+Y = 004b9725d8ea8ec6e2958319b2dccc12409c20fb6956452df345b49def9668b7a12a816f9d3766b8f57fdeb71ddcd369366db9026f33bb954226a9cbcb7f5eb8ab9a
+
+Curve = P-521
+# N = -14
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863fb
+X = 01875bc7dc551b1b65a9e1b8ccfaaf84ded1958b401494116a2fd4fb0babe0b3199974fc06c8b897222d79df3e4b7bc744aa6767f6b812efbf5d2c9e682dd3432d74
+Y = 01a35b6dca8a2534a42d299d6f44544b42047b8fedd471ae7d95f7b831647928129d2f887e4e4b0ca7b3ee17640e2ecc23f2a496f0ac57837b41be99607ad8ff2ab5
+
+Curve = P-521
+# N = -13
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863fc
+X = 007e3e98f984c396ad9cd7865d2b4924861a93f736cde1b4c2384eedd2beaf5b866132c45908e03c996a3550a5e79ab88ee94bec3b00ab38eff81887848d32fbcda7
+Y = 00f711a7149287e01256e5e6d9255c12a5f7312af5c792abde3963859851a3e1ded53e42a2a7c74389c0d92022cae340443c9e6615506ee81608d6e5fe04fdd58e36
+
+Curve = P-521
+# N = -12
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863fd
+X = 01c0d9dcec93f8221c5de4fae9749c7fde1e81874157958457b6107cf7a5967713a644e90b7c3fb81b31477fee9a60e938013774c75c530928b17be69571bf842d8c
+Y = 00bfb74a6b95b6d83f01c31e2efc597d35b89c019a548eb6b25ba1bfb54095e83f68292e77bc2790324933ef5906ae4649cf77b458dddb0a519386184e5cd7e4e80f
+
+Curve = P-521
+# N = -11
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863fe
+X = 008a75841259fdedff546f1a39573b4315cfed5dc7ed7c17849543ef2c54f2991652f3dbc5332663da1bd19b1aebe3191085015c024fa4c9a902ecc0e02dda0cdb9a
+Y = 016904cfc03445ded67b62f35788fab04dd6c522a99def42fb6c12d16a2b1f4647d4e43756f174bd5b54c76dcce6eb56acc923537f1c0b7e64a2a778b06d31b737f7
+
+Curve = P-521
+# N = -10
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863ff
+X = 0190eb8f22bda61f281dfcfe7bb6721ec4cd901d879ac09ac7c34a9246b11ada8910a2c7c178fcc263299daa4da9842093f37c2e411f1a8e819a87ff09a04f2f3320
+Y = 0014a26947b6e9eb456245154c4f35d4589f3d114debbdae4df4568028759d109d2d40acb62bb2679b44ac909e9c23a814100c9769c68c6055e8d6ab4367eca138a6
+
+Curve = P-521
+# N = -9
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386400
+X = 01585389e359e1e21826a2f5bf157156d488ed34541b988746992c4ab145b8c6b6657429e1396134da35f3c556df725a318f4f50babd85cd28661f45627967cbe207
+Y = 01d5d19e736575120c60f4aaaa85d8516c71cf7759ab11e3144937da45d9c224bb91f2961a8a9fa8537bf00a9130b54027828c93d516d777f0cbc55f15794652d5b1
+
+Curve = P-521
+# N = -8
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386401
+X = 000822c40fb6301f7262a8348396b010e25bd4e29d8a9b003e0a8b8a3b05f826298f5bfea5b8579f49f08b598c1bc8d79e1ab56289b5a6f4040586f9ea54aa78ce68
+Y = 009cce6ee2aabd03b7dfb7025491877ac465bb0712161d3f8ea4af7c219ef988570e76163f55a6ee4b400f45f20f9a3a879660c456bff6b8ecac7529bd0ee0e87fe3
+
+Curve = P-521
+# N = -7
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386402
+X = 0056d5d1d99d5b7f6346eeb65fda0b073a0c5f22e0e8f5483228f018d2c2f7114c5d8c308d0abfc698d8c9a6df30dce3bbc46f953f50fdc2619a01cead882816ecd4
+Y = 01c2d2e48264555d5eef2e27ce85c6297b874a3a7d2fd7db0f228e242675d93421aa942f0d6c321361d46adc5cba6e31e5a061898ed5a2210384a3947436fadadae4
+
+Curve = P-521
+# N = -6
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386403
+X = 01ee4569d6cdb59219532eff34f94480d195623d30977fd71cf3981506ade4ab01525fbcca16153f7394e0727a239531be8c2f66e95657f380ae23731bedf79206b9
+Y = 0021fdaa52f339b0a7951d22d8fab91c4eeed554448c25a57f718dbf56d9dfe575693548d2f1a99b7362069367b21d8b0ddfc238474aa35f2521e1533287a72bb0e8
+
+Curve = P-521
+# N = -5
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386404
+X = 00652bf3c52927a432c73dbc3391c04eb0bf7a596efdb53f0d24cf03dab8f177ace4383c0c6d5e3014237112feaf137e79a329d7e1e6d8931738d5ab5096ec8f3078
+Y = 00a41910e42299fe291375d48ceeb57eed6ee327017178d1ffae1227e8365fcb8f7844976836f8d30c8bceeabfdee30a00862e0ff8da8cab0807e8c33c17214f6f34
+
+Curve = P-521
+# N = -4
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386405
+X = 0035b5df64ae2ac204c354b483487c9070cdc61c891c5ff39afc06c5d55541d3ceac8659e24afe3d0750e8b88e9f078af066a1d5025b08e5a5e2fbc87412871902f3
+Y = 017df6907bd9ed862d498c1fe8714f4b5449aade5109191cd1e4a519c01d0e66f80d860d7c1ab45c7abfaddb08af56a47a114480510fb9662e261de0b803cb91b2f2
+
+Curve = P-521
+# N = -3
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386406
+X = 01a73d352443de29195dd91d6a64b5959479b52a6e5b123d9ab9e5ad7a112d7a8dd1ad3f164a3a4832051da6bd16b59fe21baeb490862c32ea05a5919d2ede37ad7d
+Y = 00c164fc4682059d2226686079393547eb0d0eaa8057d562fce82d0754e05caa3113d1d22b30723a8a4fd2a5312e213c38f30efa36436c5a6fbda0a7735e11793f1a
+
+Curve = P-521
+# N = -2
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386407
+X = 00433c219024277e7e682fcb288148c282747403279b1ccc06352c6e5505d769be97b3b204da6ef55507aa104a3a35c5af41cf2fa364d60fd967f43e3933ba6d783d
+Y = 010b44733807924d98ff580c1311112c0f4a394aef83b25688bf54de5d66f93bd2444c1c882160dae0946c6c805665cdb70b1503416a123f0b08e41ca9299e0be4fd
+
+Curve = P-521
+# N = -1
+N = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386408
+X = 00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66
+Y = 00e7c6d6958765c43ffba375a04bd382e426670abbb6a864bb97e85042e8d8c199d368118d66a10bd9bf3aaf46fec052f89ecac38f795d8d3dbf77416b89602e99af
+
+Curve = P-521
+# N = 0
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+X = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+Y = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+Curve = P-521
+# N = 1
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
+X = 00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66
+Y = 011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650
+
+Curve = P-521
+# N = 2
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002
+X = 00433c219024277e7e682fcb288148c282747403279b1ccc06352c6e5505d769be97b3b204da6ef55507aa104a3a35c5af41cf2fa364d60fd967f43e3933ba6d783d
+Y = 00f4bb8cc7f86db26700a7f3eceeeed3f0b5c6b5107c4da97740ab21a29906c42dbbb3e377de9f251f6b93937fa99a3248f4eafcbe95edc0f4f71be356d661f41b02
+
+Curve = P-521
+# N = 3
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
+X = 01a73d352443de29195dd91d6a64b5959479b52a6e5b123d9ab9e5ad7a112d7a8dd1ad3f164a3a4832051da6bd16b59fe21baeb490862c32ea05a5919d2ede37ad7d
+Y = 013e9b03b97dfa62ddd9979f86c6cab814f2f1557fa82a9d0317d2f8ab1fa355ceec2e2dd4cf8dc575b02d5aced1dec3c70cf105c9bc93a590425f588ca1ee86c0e5
+
+Curve = P-521
+# N = 4
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
+X = 0035b5df64ae2ac204c354b483487c9070cdc61c891c5ff39afc06c5d55541d3ceac8659e24afe3d0750e8b88e9f078af066a1d5025b08e5a5e2fbc87412871902f3
+Y = 0082096f84261279d2b673e0178eb0b4abb65521aef6e6e32e1b5ae63fe2f19907f279f283e54ba385405224f750a95b85eebb7faef04699d1d9e21f47fc346e4d0d
+
+Curve = P-521
+# N = 5
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005
+X = 00652bf3c52927a432c73dbc3391c04eb0bf7a596efdb53f0d24cf03dab8f177ace4383c0c6d5e3014237112feaf137e79a329d7e1e6d8931738d5ab5096ec8f3078
+Y = 015be6ef1bdd6601d6ec8a2b73114a8112911cd8fe8e872e0051edd817c9a0347087bb6897c9072cf374311540211cf5ff79d1f007257354f7f8173cc3e8deb090cb
+
+Curve = P-521
+# N = 6
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006
+X = 01ee4569d6cdb59219532eff34f94480d195623d30977fd71cf3981506ade4ab01525fbcca16153f7394e0727a239531be8c2f66e95657f380ae23731bedf79206b9
+Y = 01de0255ad0cc64f586ae2dd270546e3b1112aabbb73da5a808e7240a926201a8a96cab72d0e56648c9df96c984de274f2203dc7b8b55ca0dade1eaccd7858d44f17
+
+Curve = P-521
+# N = 7
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007
+X = 0056d5d1d99d5b7f6346eeb65fda0b073a0c5f22e0e8f5483228f018d2c2f7114c5d8c308d0abfc698d8c9a6df30dce3bbc46f953f50fdc2619a01cead882816ecd4
+Y = 003d2d1b7d9baaa2a110d1d8317a39d68478b5c582d02824f0dd71dbd98a26cbde556bd0f293cdec9e2b9523a34591ce1a5f9e76712a5ddefc7b5c6b8bc90525251b
+
+Curve = P-521
+# N = 8
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008
+X = 000822c40fb6301f7262a8348396b010e25bd4e29d8a9b003e0a8b8a3b05f826298f5bfea5b8579f49f08b598c1bc8d79e1ab56289b5a6f4040586f9ea54aa78ce68
+Y = 016331911d5542fc482048fdab6e78853b9a44f8ede9e2c0715b5083de610677a8f189e9c0aa5911b4bff0ba0df065c578699f3ba940094713538ad642f11f17801c
+
+Curve = P-521
+# N = 9
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+X = 01585389e359e1e21826a2f5bf157156d488ed34541b988746992c4ab145b8c6b6657429e1396134da35f3c556df725a318f4f50babd85cd28661f45627967cbe207
+Y = 002a2e618c9a8aedf39f0b55557a27ae938e3088a654ee1cebb6c825ba263ddb446e0d69e5756057ac840ff56ecf4abfd87d736c2ae928880f343aa0ea86b9ad2a4e
+
+Curve = P-521
+# N = 10
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a
+X = 0190eb8f22bda61f281dfcfe7bb6721ec4cd901d879ac09ac7c34a9246b11ada8910a2c7c178fcc263299daa4da9842093f37c2e411f1a8e819a87ff09a04f2f3320
+Y = 01eb5d96b8491614ba9dbaeab3b0ca2ba760c2eeb2144251b20ba97fd78a62ef62d2bf5349d44d9864bb536f6163dc57ebeff3689639739faa172954bc98135ec759
+
+Curve = P-521
+# N = 11
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b
+X = 008a75841259fdedff546f1a39573b4315cfed5dc7ed7c17849543ef2c54f2991652f3dbc5332663da1bd19b1aebe3191085015c024fa4c9a902ecc0e02dda0cdb9a
+Y = 0096fb303fcbba2129849d0ca877054fb2293add566210bd0493ed2e95d4e0b9b82b1bc8a90e8b42a4ab3892331914a95336dcac80e3f4819b5d58874f92ce48c808
+
+Curve = P-521
+# N = 12
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c
+X = 01c0d9dcec93f8221c5de4fae9749c7fde1e81874157958457b6107cf7a5967713a644e90b7c3fb81b31477fee9a60e938013774c75c530928b17be69571bf842d8c
+Y = 014048b5946a4927c0fe3ce1d103a682ca4763fe65ab71494da45e404abf6a17c097d6d18843d86fcdb6cc10a6f951b9b630884ba72224f5ae6c79e7b1a3281b17f0
+
+Curve = P-521
+# N = 13
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d
+X = 007e3e98f984c396ad9cd7865d2b4924861a93f736cde1b4c2384eedd2beaf5b866132c45908e03c996a3550a5e79ab88ee94bec3b00ab38eff81887848d32fbcda7
+Y = 0108ee58eb6d781feda91a1926daa3ed5a08ced50a386d5421c69c7a67ae5c1e212ac1bd5d5838bc763f26dfdd351cbfbbc36199eaaf9117e9f7291a01fb022a71c9
+
+Curve = P-521
+# N = 14
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e
+X = 01875bc7dc551b1b65a9e1b8ccfaaf84ded1958b401494116a2fd4fb0babe0b3199974fc06c8b897222d79df3e4b7bc744aa6767f6b812efbf5d2c9e682dd3432d74
+Y = 005ca4923575dacb5bd2d66290bbabb4bdfb8470122b8e51826a0847ce9b86d7ed62d07781b1b4f3584c11e89bf1d133dc0d5b690f53a87c84be41669f852700d54a
+
+Curve = P-521
+# N = 15
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f
+X = 006b6ad89abcb92465f041558fc546d4300fb8fbcc30b40a0852d697b532df128e11b91cce27dbd00ffe7875bd1c8fc0331d9b8d96981e3f92bde9afe337bcb8db55
+Y = 01b468da271571391d6a7ce64d2333edbf63df0496a9bad20cba4b62106997485ed57e9062c899470a802148e2232c96c99246fd90cc446abdd956343480a1475465
+
+Curve = P-521
+# N = 16
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010
+X = 01d17d10d8a89c8ad05dda97da26ac743b0b2a87f66192fd3f3dd632f8d20b188a52943ff18861ca00a0e5965da7985630df0dbf5c8007dcdc533a6c508f81a8402f
+Y = 007a37343c582d77001fc714b18d3d3e69721335e4c3b800d50ec7ca30c94b6b82c1c182e1398db547aa0b3075ac9d9988529e3004d28d18633352e272f89bc73abe
+
+Curve = P-521
+# N = 17
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011
+X = 01b00ddb707f130eda13a0b874645923906a99ee9e269fa2b3b4d66524f269250858760a69e674fe0287df4e799b5681380ff8c3042af0d1a41076f817a853110ae0
+Y = 0085683f1d7db16576dbc111d4e4aeddd106b799534cf69910a98d68ac2b22a1323df9da564ef6dd0bf0d2f6757f16adf420e6905594c2b755f535b9cb7c70e64647
+
+Curve = P-521
+# N = 18
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012
+X = 01bc33425e72a12779eacb2edcc5b63d1281f7e86dbc7bf99a7abd0cfe367de4666d6edbb8525bffe5222f0702c3096dec0884ce572f5a15c423fdf44d01dd99c61d
+Y = 010d06e999885b63535de3e74d33d9e63d024fb07ce0d196f2552c8e4a00ac84c044234aeb201f7a9133915d1b4b45209b9da79fe15b19f84fd135d841e2d8f9a86a
+
+Curve = P-521
+# N = 19
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013
+X = 00998dcce486419c3487c0f948c2d5a1a07245b77e0755df547efff0acdb3790e7f1fa3b3096362669679232557d7a45970dfecf431e725bbde478ff0b2418d6a19b
+Y = 0137d5da0626a021ed5cc3942497535b245d67d28aee2b7bcf4acc50eee36545772773ad963ff2eb8cf9b0ec39991631c377f5a4d89ea9fbfe44a9091a695bfd0575
+
+Curve = P-521
+# N = 20
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014
+X = 018bdd7f1b889598a4653deeae39cc6f8cc2bd767c2ab0d93fb12e968fbed342b51709506339cb1049cb11dd48b9bdb3cd5cad792e43b74e16d8e2603bfb11b0344f
+Y = 00c5aadbe63f68ca5b6b6908296959bf0af89ee7f52b410b9444546c550952d311204da3bdddc6d4eae7edfaec1030da8ef837ccb22eee9cfc94dd3287fed0990f94
+
+Curve = P-521
+# N = 21
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015
+X = 01a1cbb2c11a742bd2fb3cc8550859ed3b10268bb98c2ed15e9819523e759c467fa946257eae4d62a309d9497eaed827d75b37ac920393a46d2d42ef399693c8c9ed
+Y = 011a209d7d4f8eeb3d482c12da7c5e40ce62e83c0059c5193e6c180a296f6bafa7f7df741a53da483295beb43a372c63eec57c9b0f17649ccb61e60bc43c9cba4df5
+
+Curve = P-521
+# N = 22
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016
+X = 009700ce6190c6d36ca5f1f6e66dc1eae621a7facca319d4b7aabde3122096c4089f74cbfe1018ffcd40adfffcca8b28ca1f904b3a12cae0af934e38650bb6f5a561
+Y = 01fb78827e13569356b061c78fad62100104be4a8d24e415f9c195e0fd06c5b46824f40ac9cdadfe028a2f22ec132ed14e5c5c43427efea6210d64319ad5a535acad
+
+Curve = P-521
+# N = 23
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017
+X = 01ae0b275d7290159376db1cacec5f547b8eee7a39ba785fb282490d234d0cdd45101f6f83fc9af5b0afe9e3f8e9a5ec53e207e1f1ac26ae5806a1c6a5ece2af535c
+Y = 00f2c97a825e5385380668a2858fc068471c7d759ff890d05832b431647844c2dc0e10843361ee97fcf43c27b27bb07dd0c2213b4450a8d23856d815c3536fa0d000
+
+Curve = P-521
+# N = 24
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018
+X = 00968e98d1008e9d525db447ce5d4930becf0e49a0e6ec40736691391a8862c2cd7f93ad7a9b2d0e4f666fdefaa4a86f6559c710838fa0a4198f44a18c28b69bcef2
+Y = 01e1a7e96f293f75117ce44476658a352aa7a555cd2f64a2b032b4cd2b81880aa962a48f492cf3d075368385e09fa134f7930575ce65dd7dd9fd8a0b537a0b446729
+
+Curve = P-521
+# N = 25
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019
+X = 0154e84c6d5c5a9a1834a35874a6f5dff9d20073102651b107b11c772c8c5499994aa91856917c36ee4d56e87e2aa648ff27e41e0eb1c25d3fb13f8c2460bf70ace0
+Y = 00cd0fdce9171910473ae22a28f80bb60d3f15a923eb0022a0c7f66c2888151666a6dee3543572fbc1a0d1e2370a6a5972f5eede2a54672e6b6d4325bce404c78230
+
+Curve = P-521
+# N = 26
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a
+X = 017b61bd55cc8b533222d9857bb0c04dcd1331a02407e9a8576609bc2cbefa11d6aef686bfc27593b717007102d5dd038ed768dd29c10c73e41060d9e9a7e8c685c6
+Y = 01c34da05ea4b9de3bbea0196c0682be4633c8b4ac79f5a29ba2cbdc724709e1f426b4ddb8a4191a4648d74b5b2ac347d1335b8bff6c058ff572e74aacc5b9b4844b
+
+Curve = P-521
+# N = 27
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b
+X = 0160373edf8218f9b6a762a4d4eb889e646f8739535d0e4f862c33f35187e135854d80b2123da719d48351353aeda0d3163cb215604492ec4568357643017002d68b
+Y = 00f1597050014dcfe1c5e5828401ac06a3fa9fd193c5cf52c3bb4a56f78e1a1b22011efa491ed92ebc5413b874f4a8bb572e463ffe709d45acb3f3e6aeca5d90b740
+
+Curve = P-521
+# N = 28
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c
+X = 019195646effed8e5f6fec5c29642dd68d1a988bb828d895ed5d116d7f87032133e2949da6c395738534b93220fe2ad07f0b291adc92433ee2dee160709ed9985b0c
+Y = 0177acd53b00c9f29f3ece28b4724f834f49606d3f34656a52ef5443c5d9216ae388b345b828c8b09a2b2e495cf2adcdd6b5886f4ded1741425ac31c832ba74eed38
+
+Curve = P-521
+# N = 29
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d
+X = 0174c644d6c94b68287db3c1fbfcb65a085ff9f45131e86265ec28bef38f7664a1305ca9bc06876d72914003ab8e847ffb9ef33d8595d51f6d962c3927618eda25dc
+Y = 011af3a7c2f87f419e6805acc95b41b7b7820233ef8fcb6441fcb5cf5eb795ad34b7be0e21952ce0d90b50c2732d706d303f0679927c26ebbfbd187bbbc4821a0c30
+
+Curve = P-521
+# N = 30
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e
+X = 00d087d70d11074b36bb0b6913261570a6b33cf015e1f913a610ea52dbcbae2a3e4435573f35d14754c6352756cc169eac6bf7d9b10f1b0af5956117fab72ec4b081
+Y = 00c4d07f8c3cafdc1636811d7b95a6ac2dcebcde1cd94afbdbd3e47015660d29f15354d5b99d1d7d4adaf279efee5d860be9069edd46ff01cfc7b64d6af6579dbed3
+
+Curve = P-521
+# N = 31
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f
+X = 00d8e9920cf30f0c6615006a58cdb7307b7db574589657c2a6617260a83ca5e7726cd65543a904c9d429fc14c1ac09cd6b220dcfb5e488e248122257d0e0c16a8803
+Y = 0127b023b5454a663987df09c13a214a30ec8b5406328f10c8060aefb72cbed1aa30f76b8c3244d14790139a868cae4cc263aa1bd97c7b54318aca4677c739792d19
+
+Curve = P-521
+# N = 32
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020
+X = 0166ba3596d20a3a469443707af94ba15b343e3e16654e068038dba7ecefb9e5ebd6ea04a35dc73596ec67d69bd6ba5d1f949cf7475cd6c5c78247915ab876e56a9e
+Y = 00477b0ff4d1940ab8111635e75ff3e08268ff7bd8bf5ff01967c1df01c65c2dd77de204cdb818cdd6b9b74de1c7ea0f36dfcd98c965ab96163695ca9a9914a156aa
+
+Curve = P-521
+# N = 33
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021
+X = 00284195f0978fb969e68c76eb4e3c76f58e52086d482be92613f381421a74236df0abdbea7e2b77ef1ae9a519a57aa53ac2a5ec59186b2ed12d5ea2e1fc649f308d
+Y = 007e43eb08c656dc636b7b7a3bc869056d3a24d513abe063c6639e016769614ac5f2e7656ecbf3d138bf00bd7216c16deb3a88effd5c228b58f36be95a3dd3e11c4d
+
+Curve = P-521
+# N = 34
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022
+X = 0122f66451d0843fb59448c58962f5b7c130eb8db26b14dbf8bf0e7d84e588581cdea866a98b697d432cc1f1f8993db2173b55ac0435b8317a8ddfeab253d54e4d87
+Y = 01bed137b942249932f3c1f12141aee1538aa373f5aee0a89a48d357d7c04965b0515097293e4f87a6a413184d9be8ea91406baa5223db7571bcda9dba82fd9a06dc
+
+Curve = P-521
+# N = 35
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023
+X = 00ddc300757549630a78398b62069e0ba392a267a642b593e0bfc4a780b56f97392bb4806c84fda74023ee4a618608f6b2cea92ff614b5dcc8d8dc1039c9ccd7d718
+Y = 00c1bd2d07f6ac7fc5cc1b83431f0c30dbbfa510936cb5d85bcf462796591a31db509ab1178bd2c701425c212522ee69e797deba4db422640a4d3eb1d3ef241e07f4
+
+Curve = P-521
+# N = 36
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024
+X = 01ad81c98659bd21e082514d5e71894c417b2a25afe471eb8ad9e81a91dac8198aa9f385da9cc679bb96e724672821b585e05323bdc821e585edae56cbf798bd6ef0
+Y = 01217026e6cf8e6fc4e761aeda5705b3c0f8ca7f475441227b9761285bc6dc8f01af1af50b0bb140013a75db963d8698121dd2c4ba1759a98b43ac7712f8455c99ef
+
+Curve = P-521
+# N = 37
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025
+X = 00944f64a6c21832b67139e798352ded5867c35c5d56c1d59e4229ed0ae6af9d5749bd445763a28856040e58e26ee0980df28c64e76ae4780273f1ac4d59b557a36f
+Y = 0150b7ee1b02028ab7410c4b9624f26cb192fe4e6ff21865f85926317da6b7991305dcbdbc2471c83f81b85c564995a057c331a4be4056988296312bf98394fb2a03
+
+Curve = P-521
+# N = 38
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026
+X = 0195aeb1530c7d932a18922563d71ef6e7449078207b610c2c77690815bcef8f312718413823ed8d4ba112ac2ecc9d4e688346f120cb19ae965d853028d72a5f4a8b
+Y = 00e0c42d3cc093242000c0ec0975884bb0308611d8bd49b6090a06c5bdf7d0e1544851f9ba596291ef1ee3e3c9a002b7985ba29e51d193b701e0c87e1acd797bd130
+
+Curve = P-521
+# N = 39
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027
+X = 0124a0b8f411fbad60755264126356a499029e9661a49b5b907238d9fd62359c6ea7256b0cc58626a1e2ac0bf434e5fa31795b4fa1d48083af670b704119ee33b77c
+Y = 001029616edc7335dce3602a9a406bbd399c68937baa8796154cdc0b1240c690db4818dc59feb96825aaa3caf2415944e762f212e74b698e40abd8dcdc61228b61a6
+
+Curve = P-521
+# N = 40
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028
+X = 002efdef97e46cd544553a0c67f41886ad969f67883679409022c4ad6090a169b596f85d832dc9767b2bb513eb4d0d01e18f067fffb5ac53dbcfaf456a57824c9d93
+Y = 00d88e2e3fdd283592d6c4c2a4687e65a6a823ffc6106dff96f75bb3141d0df516fe46a5357b21dc22404f4ef79e44aa97b22c101fec75d81e09b591f36738ae8e5c
+
+Curve = P-521
+# N = 41
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029
+X = 0175a140ed79e85e24a763ebfd9d2883082552a523e4b28998a685b85f23864a60c816b931eaf495ccce4c08c3ddc8297bfaddfda1a522cc2af68e00ce952624381e
+Y = 01b09696d71855e2d4ed36f21343ca399be7ab59e580e9ee563223de821b22c82111b39e0e11ecf327da052a6e551fa28ebdf75dd758e7f3a3ecebeb760ec1028ecf
+
+Curve = P-521
+# N = 42
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a
+X = 0161960dcd7bd7263ed37a0a1c0aa146f918874f472a2a5de6f2f5633364979e0bda2868fa8595d78243ea20d4e83c72305e420bebfa3767dcd4b7902612a9491855
+Y = 016ac1cfdd433815668a5c6ae0d1031bc121a00e6a6b58d1eec42da602f01cc43b7ef728d67c4e39de86324cba6a2c4cce08414fdd6ad7d0722338a50e352ad521ef
+
+Curve = P-521
+# N = 43
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b
+X = 018c0148a165ec58e4fed55022d90544d8a070678aa2ec1419af8ef0f94438fea396fc66ad7aed7d37dda3a74e7a9b3d85ca51f0b62fdee07d20713e3083224f497e
+Y = 010abbaa1f099e78869aebf7a83c7aa3bf242677e91db1d144cd7a3037819f1a0bec67fd6098879b1cf1932d602b547ce17a94edb92f76b688d2895829067683adbf
+
+Curve = P-521
+# N = 44
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c
+X = 013fdb51df11f0b29b485f0c48a04cca16cfe1cecbe9fd57f6507c3cfcbef88c88cf761e3e73e6509ec7e77fffe912d2b367b25aa02cb1d33a5378f253cdcde738d2
+Y = 01d14c8c184a7228a7034e4f65b3d0338f667aac908b39acf39c9ebedc0452b7c2f2a4460186aa2271d357733ea23d7284a8a123424f99a20b5242343b5cbafa661a
+
+Curve = P-521
+# N = 45
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d
+X = 004969d186aca09e53b879596b8fff2bc0207a58f7f28c147cc07b2df5e3f197286779189b7df8a1f2d9dc8ea1ace5703f0fc2954607b66b70c4a32dec600fc95c1a
+Y = 01e81ec9b50cd8d3847d4d6ac21f3149fc7b15862a423270884135584a1bbcdb40a9b8d5b2802eadae85e6ed9e879be328e13463f5d90b37564f9498a7cd000903a9
+
+Curve = P-521
+# N = 46
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e
+X = 00bbdb162d8284a910021998e2d14f33a8c51a9cfb9a69a8fc5709f753aaf1c4d248a6ddb5626540f81d07c09231d2ec1549f672b6d2bd57d22f64f2ae8093e738a5
+Y = 013b86f0d5921216efe01fe9c7cabc10b00ab81c9fbd9674149d2ece424b786f6c372a1831112f54132d7d3af42dc8cd7e17b7ec80b09fa59f0e42c142895961259a
+
+Curve = P-521
+# N = 47
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f
+X = 00afe31f8907048afd5f9cf5083ecc35882ce4e4fe2714033ff897233106c71dd0b2381864a0b9e922ac9cee75a3a9c4dd660a56a130d1ecbb672fda63c9abd59d11
+Y = 0012f95dc8657275fe9db4290cbbb54ff69a605f079349209f88ecea7f3276f064979276080b904982a6ebb760f478d825e711ca41599f10673bd3065d2f1d90e1d6
+
+Curve = P-521
+# N = 48
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030
+X = 01fb35de3b21557462ac8158e5caec62c8ccae7d6d6672102c334b3cc8a5803e72f42da0980f3f6fbfacdedc21b10ff08ee6641752bd214aea2c87027d612ad879ad
+Y = 018d34a5ac1587992deda855b9ec4bb3f5c176f27fc94d09e2cc871294f66cc2e1bae6214d64a1d97e37b7c142e0fb0e3c28596f24804841cae7b1958193d256d46b
+
+Curve = P-521
+# N = 49
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031
+X = 00a5cb98fa3c0b8c1d57cba40a63bbaba0d39d45c347ae499839f581b8b111dea9eb6bc2a2df52f6284291d8c84e2e345fa87258dab1ac4c04ca0c8cb45049efc0ad
+Y = 0015dc95654090d77b971409b01bf72e151b749ee467877e5b7eb072c39d9da8bf835dd9775543d575c20c10f2ceebb72bb44b963fbfb0b88b1d0b9bf46a2c8884b4
+
+Curve = P-521
+# N = 50
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032
+X = 017176204957627b7a120baf0dbb1aa3f9ca5312b292e34a0cfbd8384cf1be365bc684804ab26d84fe02250a859beaced3f356b6ddab593aebf35386773de84d4766
+Y = 01ac713d5c2ffb0e111983e07ea5f688b03dd3032d3a9450b50c5def1db4859b504caf8c1321bb6c8d5fad93d2e5b9d0e75336298e21939f704f3f16ac6addc84f8f
+
+Curve = P-521
+# N = 51
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033
+X = 0168395ba51e278415a2025d93b68145f3ccfdf9f5bde34fe9ba3ba316cbf9beea26c2edfe6af07390df1321ae1b1054cbc0fba689ef1d7be2dda8d916fffbcc9504
+Y = 0071cc10f3ca041a245639d9531942d3f57952dd878f21c480ce24e5b174adeb9b3c97bc2b68badb600c849c36096e3aeba50600aabed3a89e188eb9c45edd5c087b
+
+Curve = P-521
+# N = 52
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034
+X = 001f32e875ce65d1e6d4c39a55b518749158a4dae03e0c1b4b4a58aef6be769f0693b9259354dcee33a4f00784311b6e5993955114f2afcab7a186177e368b29f6f3
+Y = 005a3c8e76fb7849180b895ea8b22ee1a6fc2cbf93bbae0f4ad4a0c126d318c397911e73a061b8098a91112577769cd77ef2bd6b45f989f292da9301992d481b07f8
+
+Curve = P-521
+# N = 53
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
+X = 0143f6e249195ee693f77b3d0ffd310306dff8688be916895fb727e82f6f948100c44e210e637209a78ed3af581fba4fe12b08153da9c98808166285684cccb69906
+Y = 01053e8fcc9618eb2a74c5611a02575109877dc73fdde42c216935437e8053d10cb14fdf64f503cf3bb30f6a7a9de3b4cf8c040a3dfbc4abee6afd6ddaddd40c7861
+
+Curve = P-521
+# N = 54
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
+X = 01938e65f2a40e6899c6414997e998e0e3e1cb09728861ea138fc6d866fdc6c98505393cf55982451c97dac4214e4dbf052bf707cab63228fdfcf5ae2048a0ce100c
+Y = 0163d556dd0941ce794e9c2868a692e89dbf943485186e2074e8222e0ed99fea4bd0c6069fa8017db8bec972b99ee0d7fcbc0f607d677492659bfa47b98bb8d1b411
+
+Curve = P-521
+# N = 55
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037
+X = 004b52fc4b6d310ce7c4b551e4155c6daca97cb03e9fd9d0a79d6472d4028e8da1a18cca93917cad27e6be17486b1e0b549a7fe9ab4bda96ac4e84ad7ccebd470f5e
+Y = 003585e54fe81461ae21385ea7907a1a7b2e619f44311a16a0b600fb114a7dbae15510aa85895c5a084cd69609e345b53586fa03a23006a096ea7a196cd230a36ef2
+
+Curve = P-521
+# N = 56
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038
+X = 003cd775038527f32baf7e9056e95b0cbef7638c12398e4b8b019cc29b3435be97f601378e253fef51d25730e56267acca241df04ea2e96c1840b3f739d5ae2df998
+Y = 00789bf7cf42095c5b1770e1a3561fc3e1232b0430e114f67a397c1e5b54987f4a28b52a737cbaa0ffa2c431863c1a8cbf15eba60fb5d8b4723dc10d9706f4083cba
+
+Curve = P-521
+# N = 57
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039
+X = 00dc53c3e7fcf3c902e04463da75efb25ffa5a8defe72e6dbf85ef3c6d77a521b9f84af300bbde9118b2f66fd5bc64b8a5208372c8fff95c84503475330a4e9a13e2
+Y = 0074e88fccd4fdbd992b68073ee6c4d4f8f7b13d0dd2caf9d989b62e7eb390dff3ea3482530d0d3bd86a4a3f82056a929f73a9493190c62a553233ad7f7fd9c4248f
+
+Curve = P-521
+# N = 58
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a
+X = 008bc46df1c09851ab92d53b6ad821b5026be61385c801f5277c9871b54c6764d3fdb96a728ad56c650d342a03e174a3106a5479d3ccb04647ad0f583210d81ab70d
+Y = 01d948ff92784e9946a27a4c58d40bfb0e0b765073f1f9a731aa52b53e4fe686134fee784bec8fca81a73b8ac3a45ca1b766cdec69190bf94d71fa08b29292c1205f
+
+Curve = P-521
+# N = 59
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b
+X = 0035aeb454ad31876773063c60342df29eadc7223c5d98901999aae5c2934c222920055693c9b1344c691a90342d64373a8acba8524ec347208863be8a26eb16686b
+Y = 00187ecbec147e7e43b4db0b0ca0aff1ba7f15a48db3660903f4e22f2ef3cb6efd45b8cf8c44bc83d0b8c9fd5fddd64ffec3fd82c38cfaffe51acf4aa03c5381fa2e
+
+Curve = P-521
+# N = 60
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c
+X = 00038a508e954435eff6ccb82b562ed32ff236f33caef52de9fdf79836e37bbe118392681661adc786c57fb6bf92d04b42fc051c3f6061c252749fd17ffbfca45bdc
+Y = 008dbd87d8b1e7bd4d2adb7a7399b931e7169b0ecf8e37baa0e9997df1a529560a5a5edb1e7a605abfd04b1ccdfa683285b4d748f73799f3e5d4f8464ad3910e9522
+
+Curve = P-521
+# N = 61
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d
+X = 0115544c4a011407425c92ca7c732a1c7d8f8b6cd17d5b660d1a4cd724b1be8b17177193c11a77dc0b39a5e7fce59a7b32d7952bad1671543c41cb53d8cfcfc376a1
+Y = 01153df9c6c0ac6485b307996d2a399c872c79af485fef422cbfec097a3aed58c6a003f78d54b7f553a8a834ff603afa032b56f22ee9c3fe5e24b8eff8cd4a17604b
+
+Curve = P-521
+# N = 62
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e
+X = 01f605dada534c5c8ae020f6ed49f27734e2378b0b7bc177aeaf943ae59581dd5885a38bf246fec516fd213f3fe32bfd4e19f0a8c971ef2a16969627fe31114dc85e
+Y = 014ff96d82698b78d305a2252c7a4be38a37f2b0afd6935f4aaa50e685ccd164a2206284a831f6b296eacb156b6e7671eb7adda2d7e072a578a319f1002224aa57b5
+
+Curve = P-521
+# N = 63
+N = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f
+X = 01c132753b64640c457fe82f799acb0a6e8e1ca21b5affa0ddc1e7f54fe4d51b08453ae99661308e125ec63996847586265e75af7d025770ac8d0f5beb6fce8888e5
+Y = 015347e184197a0538176d81ac89b205d7961a9e093286414006595e8c353aa2238005d3dbdcec1896bc13f78f82f0071283af657b5bf664a2ace9d15ad2a03dba15
+
+Curve = P-521
+# N = 64
+N = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
+X = 01ab9aa17dce1112ecd14f3d7c0392fec2a67ebdbba81860bbacb614b9ccf8872d271bebd3c5efda3773a7c02c1603e7001df5aab8029a04fd41b53dc38ce320f742
+Y = 00748d70eb848e920573aa10217c57b9586d9bb1cc8b2fb1dc1d0c63ba369e87ab8bac165ba1508d4b10f4a7b9e3af958043c1c1a5dba0653dd45af17058a5f8df5b
+
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 0ac2c5bc..d2cd5f52 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -13,6 +13,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <vector>
@@ -28,6 +29,7 @@
#include <openssl/nid.h>
#include <openssl/obj.h>
+#include "../../test/file_test.h"
#include "../../test/test_util.h"
#include "../bn/internal.h"
#include "internal.h"
@@ -368,6 +370,61 @@ TEST(ECTest, EmptyKey) {
EXPECT_FALSE(EC_KEY_get0_private_key(key.get()));
}
+static bssl::UniquePtr<BIGNUM> HexToBIGNUM(const char *hex) {
+ BIGNUM *bn = nullptr;
+ BN_hex2bn(&bn, hex);
+ return bssl::UniquePtr<BIGNUM>(bn);
+}
+
+// Test that point arithmetic works with custom curves using an arbitrary |a|,
+// rather than -3, as is common (and more efficient).
+TEST(ECTest, BrainpoolP256r1) {
+ static const char kP[] =
+ "a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377";
+ static const char kA[] =
+ "7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9";
+ static const char kB[] =
+ "26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6";
+ static const char kX[] =
+ "8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262";
+ static const char kY[] =
+ "547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997";
+ static const char kN[] =
+ "a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7";
+ static const char kD[] =
+ "0da21d76fed40dd82ac3314cce91abb585b5c4246e902b238a839609ea1e7ce1";
+ static const char kQX[] =
+ "3a55e0341cab50452fe27b8a87e4775dec7a9daca94b0d84ad1e9f85b53ea513";
+ static const char kQY[] =
+ "40088146b33bbbe81b092b41146774b35dd478cf056437cfb35ef0df2d269339";
+
+ bssl::UniquePtr<BIGNUM> p = HexToBIGNUM(kP), a = HexToBIGNUM(kA),
+ b = HexToBIGNUM(kB), x = HexToBIGNUM(kX),
+ y = HexToBIGNUM(kY), n = HexToBIGNUM(kN),
+ d = HexToBIGNUM(kD), qx = HexToBIGNUM(kQX),
+ qy = HexToBIGNUM(kQY);
+ ASSERT_TRUE(p && a && b && x && y && n && d && qx && qy);
+
+ bssl::UniquePtr<EC_GROUP> group(
+ EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), nullptr));
+ ASSERT_TRUE(group);
+ bssl::UniquePtr<EC_POINT> g(EC_POINT_new(group.get()));
+ ASSERT_TRUE(g);
+ ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group.get(), g.get(), x.get(),
+ y.get(), nullptr));
+ ASSERT_TRUE(
+ EC_GROUP_set_generator(group.get(), g.get(), n.get(), BN_value_one()));
+
+ bssl::UniquePtr<EC_POINT> q(EC_POINT_new(group.get()));
+ ASSERT_TRUE(q);
+ ASSERT_TRUE(
+ EC_POINT_mul(group.get(), q.get(), d.get(), nullptr, nullptr, nullptr));
+ ASSERT_TRUE(EC_POINT_get_affine_coordinates_GFp(group.get(), q.get(), x.get(),
+ y.get(), nullptr));
+ EXPECT_EQ(0, BN_cmp(x.get(), qx.get()));
+ EXPECT_EQ(0, BN_cmp(y.get(), qy.get()));
+}
+
class ECCurveTest : public testing::TestWithParam<EC_builtin_curve> {
public:
const EC_GROUP *group() const { return group_.get(); }
@@ -674,6 +731,29 @@ TEST_P(ECCurveTest, DoubleSpecialCase) {
EXPECT_EQ(0, EC_POINT_cmp(group(), p.get(), two_g.get(), nullptr));
}
+// This a regression test for a P-224 bug, but we may as well run it for all
+// curves.
+TEST_P(ECCurveTest, P224Bug) {
+ // P = -G
+ const EC_POINT *g = EC_GROUP_get0_generator(group());
+ bssl::UniquePtr<EC_POINT> p(EC_POINT_dup(g, group()));
+ ASSERT_TRUE(p);
+ ASSERT_TRUE(EC_POINT_invert(group(), p.get(), nullptr));
+
+ // Compute 31 * P + 32 * G = G
+ bssl::UniquePtr<EC_POINT> ret(EC_POINT_new(group()));
+ ASSERT_TRUE(ret);
+ bssl::UniquePtr<BIGNUM> bn31(BN_new()), bn32(BN_new());
+ ASSERT_TRUE(bn31);
+ ASSERT_TRUE(bn32);
+ ASSERT_TRUE(BN_set_word(bn31.get(), 31));
+ ASSERT_TRUE(BN_set_word(bn32.get(), 32));
+ ASSERT_TRUE(EC_POINT_mul(group(), ret.get(), bn32.get(), p.get(), bn31.get(),
+ nullptr));
+
+ EXPECT_EQ(0, EC_POINT_cmp(group(), ret.get(), g, nullptr));
+}
+
static std::vector<EC_builtin_curve> AllCurves() {
const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
std::vector<EC_builtin_curve> curves(num_curves);
@@ -689,3 +769,113 @@ static std::string CurveToString(
INSTANTIATE_TEST_CASE_P(, ECCurveTest, testing::ValuesIn(AllCurves()),
CurveToString);
+
+static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) {
+ std::string curve_name;
+ if (!t->GetAttribute(&curve_name, key)) {
+ return nullptr;
+ }
+
+ if (curve_name == "P-224") {
+ return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp224r1));
+ }
+ if (curve_name == "P-256") {
+ return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(
+ NID_X9_62_prime256v1));
+ }
+ if (curve_name == "P-384") {
+ return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp384r1));
+ }
+ if (curve_name == "P-521") {
+ return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp521r1));
+ }
+
+ t->PrintLine("Unknown curve '%s'", curve_name.c_str());
+ return nullptr;
+}
+
+static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) {
+ std::vector<uint8_t> bytes;
+ if (!t->GetBytes(&bytes, key)) {
+ return nullptr;
+ }
+
+ return bssl::UniquePtr<BIGNUM>(
+ BN_bin2bn(bytes.data(), bytes.size(), nullptr));
+}
+
+TEST(ECTest, ScalarBaseMultVectors) {
+ bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
+ ASSERT_TRUE(ctx);
+
+ FileTestGTest("crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt",
+ [&](FileTest *t) {
+ bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
+ ASSERT_TRUE(group);
+ bssl::UniquePtr<BIGNUM> n = GetBIGNUM(t, "N");
+ ASSERT_TRUE(n);
+ bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
+ ASSERT_TRUE(x);
+ bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
+ ASSERT_TRUE(y);
+ bool is_infinity = BN_is_zero(x.get()) && BN_is_zero(y.get());
+
+ bssl::UniquePtr<BIGNUM> px(BN_new());
+ ASSERT_TRUE(px);
+ bssl::UniquePtr<BIGNUM> py(BN_new());
+ ASSERT_TRUE(py);
+ auto check_point = [&](const EC_POINT *p) {
+ if (is_infinity) {
+ EXPECT_TRUE(EC_POINT_is_at_infinity(group.get(), p));
+ } else {
+ ASSERT_TRUE(EC_POINT_get_affine_coordinates_GFp(
+ group.get(), p, px.get(), py.get(), ctx.get()));
+ EXPECT_EQ(0, BN_cmp(x.get(), px.get()));
+ EXPECT_EQ(0, BN_cmp(y.get(), py.get()));
+ }
+ };
+
+ const EC_POINT *g = EC_GROUP_get0_generator(group.get());
+ bssl::UniquePtr<EC_POINT> p(EC_POINT_new(group.get()));
+ ASSERT_TRUE(p);
+ // Test single-point multiplication.
+ ASSERT_TRUE(EC_POINT_mul(group.get(), p.get(), n.get(), nullptr, nullptr,
+ ctx.get()));
+ check_point(p.get());
+
+ ASSERT_TRUE(
+ EC_POINT_mul(group.get(), p.get(), nullptr, g, n.get(), ctx.get()));
+ check_point(p.get());
+
+ // These tests take a very long time, but are worth running when we make
+ // non-trivial changes to the EC code.
+#if 0
+ // Test two-point multiplication.
+ bssl::UniquePtr<BIGNUM> a(BN_new()), b(BN_new());
+ for (int i = -64; i < 64; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_TRUE(BN_set_word(a.get(), abs(i)));
+ if (i < 0) {
+ ASSERT_TRUE(BN_sub(a.get(), EC_GROUP_get0_order(group.get()), a.get()));
+ }
+
+ ASSERT_TRUE(BN_copy(b.get(), n.get()));
+ ASSERT_TRUE(BN_sub(b.get(), b.get(), a.get()));
+ if (BN_is_negative(b.get())) {
+ ASSERT_TRUE(BN_add(b.get(), b.get(), EC_GROUP_get0_order(group.get())));
+ }
+
+ ASSERT_TRUE(
+ EC_POINT_mul(group.get(), p.get(), a.get(), g, b.get(), ctx.get()));
+ check_point(p.get());
+
+ EC_SCALAR a_scalar, b_scalar;
+ ASSERT_TRUE(ec_bignum_to_scalar(group.get(), &a_scalar, a.get()));
+ ASSERT_TRUE(ec_bignum_to_scalar(group.get(), &b_scalar, b.get()));
+ ASSERT_TRUE(ec_point_mul_scalar_public(group.get(), p.get(), &a_scalar, g,
+ &b_scalar, ctx.get()));
+ check_point(p.get());
+ }
+#endif
+ });
+}
diff --git a/src/crypto/fipsmodule/ec/internal.h b/src/crypto/fipsmodule/ec/internal.h
index c198cc29..742e94e1 100644
--- a/src/crypto/fipsmodule/ec/internal.h
+++ b/src/crypto/fipsmodule/ec/internal.h
@@ -249,10 +249,6 @@ int ec_GFp_simple_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b,
int ec_GFp_simple_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *);
int ec_GFp_simple_points_make_affine(const EC_GROUP *, size_t num,
EC_POINT * [], BN_CTX *);
-int ec_GFp_simple_field_mul(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
- const BIGNUM *b, BN_CTX *);
-int ec_GFp_simple_field_sqr(const EC_GROUP *, BIGNUM *r, const BIGNUM *a,
- BN_CTX *);
// method functions in montgomery.c
int ec_GFp_mont_group_init(EC_GROUP *);
diff --git a/src/crypto/fipsmodule/ec/make_ec_scalar_base_mult_tests.go b/src/crypto/fipsmodule/ec/make_ec_scalar_base_mult_tests.go
new file mode 100644
index 00000000..716da55b
--- /dev/null
+++ b/src/crypto/fipsmodule/ec/make_ec_scalar_base_mult_tests.go
@@ -0,0 +1,60 @@
+/* Copyright (c) 2018, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+package main
+
+import (
+ "crypto/elliptic"
+ "fmt"
+ "math/big"
+)
+
+const numPoints = 64
+
+func printPadded(key string, n, max *big.Int) {
+ padded := make([]byte, len(max.Bytes()))
+ b := n.Bytes()
+ copy(padded[len(padded)-len(b):], b)
+ fmt.Printf("%s = %x\n", key, padded)
+}
+
+func printMultiples(name string, curve elliptic.Curve) {
+ n := new(big.Int)
+ for i := -numPoints; i <= numPoints; i++ {
+ fmt.Printf("Curve = %s\n", name)
+ n.SetInt64(int64(i))
+ if i < 0 {
+ n = n.Add(n, curve.Params().N)
+ }
+ fmt.Printf("# N = %d\n", i)
+ printPadded("N", n, curve.Params().N)
+ x, y := curve.ScalarBaseMult(n.Bytes())
+ printPadded("X", x, curve.Params().P)
+ printPadded("Y", y, curve.Params().P)
+ fmt.Printf("\n")
+ }
+}
+
+func main() {
+ fmt.Printf(`# This file contains multiples of the base point for various curves. The point
+# at infinity is represented as X = 0, Y = 0.
+#
+# This file is generated by make_ec_scalar_base_mult_tests.go
+
+`)
+ printMultiples("P-224", elliptic.P224())
+ printMultiples("P-256", elliptic.P256())
+ printMultiples("P-384", elliptic.P384())
+ printMultiples("P-521", elliptic.P521())
+}
diff --git a/src/crypto/fipsmodule/ec/oct.c b/src/crypto/fipsmodule/ec/oct.c
index 38a3342e..19e17a7c 100644
--- a/src/crypto/fipsmodule/ec/oct.c
+++ b/src/crypto/fipsmodule/ec/oct.c
@@ -316,20 +316,20 @@ int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
// tmp1 := tmp1 + a*x
if (group->a_is_minus3) {
- if (!bn_mod_lshift1_quick_ctx(tmp2, x, &group->field, ctx) ||
- !bn_mod_add_quick_ctx(tmp2, tmp2, x, &group->field, ctx) ||
- !bn_mod_sub_quick_ctx(tmp1, tmp1, tmp2, &group->field, ctx)) {
+ if (!bn_mod_lshift1_consttime(tmp2, x, &group->field, ctx) ||
+ !bn_mod_add_consttime(tmp2, tmp2, x, &group->field, ctx) ||
+ !bn_mod_sub_consttime(tmp1, tmp1, tmp2, &group->field, ctx)) {
goto err;
}
} else {
if (!BN_mod_mul(tmp2, a, x, &group->field, ctx) ||
- !bn_mod_add_quick_ctx(tmp1, tmp1, tmp2, &group->field, ctx)) {
+ !bn_mod_add_consttime(tmp1, tmp1, tmp2, &group->field, ctx)) {
goto err;
}
}
// tmp1 := tmp1 + b
- if (!bn_mod_add_quick_ctx(tmp1, tmp1, b, &group->field, ctx)) {
+ if (!bn_mod_add_consttime(tmp1, tmp1, b, &group->field, ctx)) {
goto err;
}
diff --git a/src/crypto/fipsmodule/ec/p224-64.c b/src/crypto/fipsmodule/ec/p224-64.c
index 71a8af0a..7e2f45bb 100644
--- a/src/crypto/fipsmodule/ec/p224-64.c
+++ b/src/crypto/fipsmodule/ec/p224-64.c
@@ -257,23 +257,6 @@ static void p224_felem_sum(p224_felem out, const p224_felem in) {
out[3] += in[3];
}
-// Get negative value: out = -in
-// Assumes in[i] < 2^57
-static void p224_felem_neg(p224_felem out, const p224_felem in) {
- static const p224_limb two58p2 =
- (((p224_limb)1) << 58) + (((p224_limb)1) << 2);
- static const p224_limb two58m2 =
- (((p224_limb)1) << 58) - (((p224_limb)1) << 2);
- static const p224_limb two58m42m2 =
- (((p224_limb)1) << 58) - (((p224_limb)1) << 42) - (((p224_limb)1) << 2);
-
- // Set to 0 mod 2^224-2^96+1 to ensure out > in
- out[0] = two58p2 - in[0];
- out[1] = two58m42m2 - in[1];
- out[2] = two58m2 - in[2];
- out[3] = two58m2 - in[3];
-}
-
// Subtract field elements: out -= in
// Assumes in[i] < 2^57
static void p224_felem_diff(p224_felem out, const p224_felem in) {
@@ -513,6 +496,15 @@ static void p224_felem_contract(p224_felem out, const p224_felem in) {
out[3] = tmp[3];
}
+// Get negative value: out = -in
+// Requires in[i] < 2^63,
+// ensures out[0] < 2^56, out[1] < 2^56, out[2] < 2^56, out[3] <= 2^56 + 2^16
+static void p224_felem_neg(p224_felem out, const p224_felem in) {
+ p224_widefelem tmp = {0};
+ p224_felem_diff_128_64(tmp, in);
+ p224_felem_reduce(out, tmp);
+}
+
// Zero-check: returns 1 if input is 0, and 0 otherwise. We know that field
// elements are reduced to in < 2^225, so we only need to check three cases: 0,
// 2^224 - 2^96 + 1, and 2^225 - 2^97 + 2
@@ -1095,6 +1087,34 @@ static int ec_GFp_nistp224_points_mul(const EC_GROUP *group, EC_POINT *r,
return 1;
}
+static int ec_GFp_nistp224_field_mul(const EC_GROUP *group, BIGNUM *r,
+ const BIGNUM *a, const BIGNUM *b,
+ BN_CTX *ctx) {
+ p224_felem felem1, felem2;
+ p224_widefelem wide;
+ if (!p224_BN_to_felem(felem1, a) ||
+ !p224_BN_to_felem(felem2, b)) {
+ return 0;
+ }
+ p224_felem_mul(wide, felem1, felem2);
+ p224_felem_reduce(felem1, wide);
+ p224_felem_contract(felem1, felem1);
+ return p224_felem_to_BN(r, felem1) != NULL;
+}
+
+static int ec_GFp_nistp224_field_sqr(const EC_GROUP *group, BIGNUM *r,
+ const BIGNUM *a, BN_CTX *ctx) {
+ p224_felem felem;
+ if (!p224_BN_to_felem(felem, a)) {
+ return 0;
+ }
+ p224_widefelem wide;
+ p224_felem_square(wide, felem);
+ p224_felem_reduce(felem, wide);
+ p224_felem_contract(felem, felem);
+ return p224_felem_to_BN(r, felem) != NULL;
+}
+
DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistp224_method) {
out->group_init = ec_GFp_simple_group_init;
out->group_finish = ec_GFp_simple_group_finish;
@@ -1103,8 +1123,8 @@ DEFINE_METHOD_FUNCTION(EC_METHOD, EC_GFp_nistp224_method) {
ec_GFp_nistp224_point_get_affine_coordinates;
out->mul = ec_GFp_nistp224_points_mul;
out->mul_public = ec_GFp_nistp224_points_mul;
- out->field_mul = ec_GFp_simple_field_mul;
- out->field_sqr = ec_GFp_simple_field_sqr;
+ out->field_mul = ec_GFp_nistp224_field_mul;
+ out->field_sqr = ec_GFp_nistp224_field_sqr;
out->field_encode = NULL;
out->field_decode = NULL;
};
diff --git a/src/crypto/fipsmodule/ec/simple.c b/src/crypto/fipsmodule/ec/simple.c
index 57a9099d..e87409c6 100644
--- a/src/crypto/fipsmodule/ec/simple.c
+++ b/src/crypto/fipsmodule/ec/simple.c
@@ -395,8 +395,8 @@ int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
}
// n5, n6
- if (!bn_mod_sub_quick_ctx(n5, n1, n3, p, ctx) ||
- !bn_mod_sub_quick_ctx(n6, n2, n4, p, ctx)) {
+ if (!bn_mod_sub_consttime(n5, n1, n3, p, ctx) ||
+ !bn_mod_sub_consttime(n6, n2, n4, p, ctx)) {
goto end;
}
// n5 = n1 - n3
@@ -418,8 +418,8 @@ int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
}
// 'n7', 'n8'
- if (!bn_mod_add_quick_ctx(n1, n1, n3, p, ctx) ||
- !bn_mod_add_quick_ctx(n2, n2, n4, p, ctx)) {
+ if (!bn_mod_add_consttime(n1, n1, n3, p, ctx) ||
+ !bn_mod_add_consttime(n2, n2, n4, p, ctx)) {
goto end;
}
// 'n7' = n1 + n3
@@ -453,14 +453,14 @@ int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
if (!field_sqr(group, n0, n6, ctx) ||
!field_sqr(group, n4, n5, ctx) ||
!field_mul(group, n3, n1, n4, ctx) ||
- !bn_mod_sub_quick_ctx(&r->X, n0, n3, p, ctx)) {
+ !bn_mod_sub_consttime(&r->X, n0, n3, p, ctx)) {
goto end;
}
// X_r = n6^2 - n5^2 * 'n7'
// 'n9'
- if (!bn_mod_lshift1_quick_ctx(n0, &r->X, p, ctx) ||
- !bn_mod_sub_quick_ctx(n0, n3, n0, p, ctx)) {
+ if (!bn_mod_lshift1_consttime(n0, &r->X, p, ctx) ||
+ !bn_mod_sub_consttime(n0, n3, n0, p, ctx)) {
goto end;
}
// n9 = n5^2 * 'n7' - 2 * X_r
@@ -471,7 +471,7 @@ int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
goto end; // now n5 is n5^3
}
if (!field_mul(group, n1, n2, n5, ctx) ||
- !bn_mod_sub_quick_ctx(n0, n0, n1, p, ctx)) {
+ !bn_mod_sub_consttime(n0, n0, n1, p, ctx)) {
goto end;
}
if (BN_is_odd(n0) && !BN_add(n0, n0, p)) {
@@ -536,31 +536,31 @@ int ec_GFp_simple_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
// n1
if (BN_cmp(&a->Z, &group->one) == 0) {
if (!field_sqr(group, n0, &a->X, ctx) ||
- !bn_mod_lshift1_quick_ctx(n1, n0, p, ctx) ||
- !bn_mod_add_quick_ctx(n0, n0, n1, p, ctx) ||
- !bn_mod_add_quick_ctx(n1, n0, &group->a, p, ctx)) {
+ !bn_mod_lshift1_consttime(n1, n0, p, ctx) ||
+ !bn_mod_add_consttime(n0, n0, n1, p, ctx) ||
+ !bn_mod_add_consttime(n1, n0, &group->a, p, ctx)) {
goto err;
}
// n1 = 3 * X_a^2 + a_curve
} else if (group->a_is_minus3) {
if (!field_sqr(group, n1, &a->Z, ctx) ||
- !bn_mod_add_quick_ctx(n0, &a->X, n1, p, ctx) ||
- !bn_mod_sub_quick_ctx(n2, &a->X, n1, p, ctx) ||
+ !bn_mod_add_consttime(n0, &a->X, n1, p, ctx) ||
+ !bn_mod_sub_consttime(n2, &a->X, n1, p, ctx) ||
!field_mul(group, n1, n0, n2, ctx) ||
- !bn_mod_lshift1_quick_ctx(n0, n1, p, ctx) ||
- !bn_mod_add_quick_ctx(n1, n0, n1, p, ctx)) {
+ !bn_mod_lshift1_consttime(n0, n1, p, ctx) ||
+ !bn_mod_add_consttime(n1, n0, n1, p, ctx)) {
goto err;
}
// n1 = 3 * (X_a + Z_a^2) * (X_a - Z_a^2)
// = 3 * X_a^2 - 3 * Z_a^4
} else {
if (!field_sqr(group, n0, &a->X, ctx) ||
- !bn_mod_lshift1_quick_ctx(n1, n0, p, ctx) ||
- !bn_mod_add_quick_ctx(n0, n0, n1, p, ctx) ||
+ !bn_mod_lshift1_consttime(n1, n0, p, ctx) ||
+ !bn_mod_add_consttime(n0, n0, n1, p, ctx) ||
!field_sqr(group, n1, &a->Z, ctx) ||
!field_sqr(group, n1, n1, ctx) ||
!field_mul(group, n1, n1, &group->a, ctx) ||
- !bn_mod_add_quick_ctx(n1, n1, n0, p, ctx)) {
+ !bn_mod_add_consttime(n1, n1, n0, p, ctx)) {
goto err;
}
// n1 = 3 * X_a^2 + a_curve * Z_a^4
@@ -574,7 +574,7 @@ int ec_GFp_simple_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
} else if (!field_mul(group, n0, &a->Y, &a->Z, ctx)) {
goto err;
}
- if (!bn_mod_lshift1_quick_ctx(&r->Z, n0, p, ctx)) {
+ if (!bn_mod_lshift1_consttime(&r->Z, n0, p, ctx)) {
goto err;
}
// Z_r = 2 * Y_a * Z_a
@@ -582,30 +582,30 @@ int ec_GFp_simple_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
// n2
if (!field_sqr(group, n3, &a->Y, ctx) ||
!field_mul(group, n2, &a->X, n3, ctx) ||
- !bn_mod_lshift_quick_ctx(n2, n2, 2, p, ctx)) {
+ !bn_mod_lshift_consttime(n2, n2, 2, p, ctx)) {
goto err;
}
// n2 = 4 * X_a * Y_a^2
// X_r
- if (!bn_mod_lshift1_quick_ctx(n0, n2, p, ctx) ||
+ if (!bn_mod_lshift1_consttime(n0, n2, p, ctx) ||
!field_sqr(group, &r->X, n1, ctx) ||
- !bn_mod_sub_quick_ctx(&r->X, &r->X, n0, p, ctx)) {
+ !bn_mod_sub_consttime(&r->X, &r->X, n0, p, ctx)) {
goto err;
}
// X_r = n1^2 - 2 * n2
// n3
if (!field_sqr(group, n0, n3, ctx) ||
- !bn_mod_lshift_quick_ctx(n3, n0, 3, p, ctx)) {
+ !bn_mod_lshift_consttime(n3, n0, 3, p, ctx)) {
goto err;
}
// n3 = 8 * Y_a^4
// Y_r
- if (!bn_mod_sub_quick_ctx(n0, n2, &r->X, p, ctx) ||
+ if (!bn_mod_sub_consttime(n0, n2, &r->X, p, ctx) ||
!field_mul(group, n0, n1, n0, ctx) ||
- !bn_mod_sub_quick_ctx(&r->Y, n0, n3, p, ctx)) {
+ !bn_mod_sub_consttime(&r->Y, n0, n3, p, ctx)) {
goto err;
}
// Y_r = n1 * (n2 - X_r) - n3
@@ -688,15 +688,15 @@ int ec_GFp_simple_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
// rh := (rh + a*Z^4)*X
if (group->a_is_minus3) {
- if (!bn_mod_lshift1_quick_ctx(tmp, Z4, p, ctx) ||
- !bn_mod_add_quick_ctx(tmp, tmp, Z4, p, ctx) ||
- !bn_mod_sub_quick_ctx(rh, rh, tmp, p, ctx) ||
+ if (!bn_mod_lshift1_consttime(tmp, Z4, p, ctx) ||
+ !bn_mod_add_consttime(tmp, tmp, Z4, p, ctx) ||
+ !bn_mod_sub_consttime(rh, rh, tmp, p, ctx) ||
!field_mul(group, rh, rh, &point->X, ctx)) {
goto err;
}
} else {
if (!field_mul(group, tmp, Z4, &group->a, ctx) ||
- !bn_mod_add_quick_ctx(rh, rh, tmp, p, ctx) ||
+ !bn_mod_add_consttime(rh, rh, tmp, p, ctx) ||
!field_mul(group, rh, rh, &point->X, ctx)) {
goto err;
}
@@ -704,17 +704,17 @@ int ec_GFp_simple_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
// rh := rh + b*Z^6
if (!field_mul(group, tmp, &group->b, Z6, ctx) ||
- !bn_mod_add_quick_ctx(rh, rh, tmp, p, ctx)) {
+ !bn_mod_add_consttime(rh, rh, tmp, p, ctx)) {
goto err;
}
} else {
// rh := (rh + a)*X
- if (!bn_mod_add_quick_ctx(rh, rh, &group->a, p, ctx) ||
+ if (!bn_mod_add_consttime(rh, rh, &group->a, p, ctx) ||
!field_mul(group, rh, rh, &point->X, ctx)) {
goto err;
}
// rh := rh + b
- if (!bn_mod_add_quick_ctx(rh, rh, &group->b, p, ctx)) {
+ if (!bn_mod_add_consttime(rh, rh, &group->b, p, ctx)) {
goto err;
}
}
@@ -1034,13 +1034,3 @@ err:
return ret;
}
-
-int ec_GFp_simple_field_mul(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
- const BIGNUM *b, BN_CTX *ctx) {
- return BN_mod_mul(r, a, b, &group->field, ctx);
-}
-
-int ec_GFp_simple_field_sqr(const EC_GROUP *group, BIGNUM *r, const BIGNUM *a,
- BN_CTX *ctx) {
- return BN_mod_sqr(r, a, &group->field, ctx);
-}
diff --git a/src/crypto/fipsmodule/ecdsa/ecdsa.c b/src/crypto/fipsmodule/ecdsa/ecdsa.c
index 1d08123b..85490fad 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa.c
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa.c
@@ -66,42 +66,16 @@
#include "../../internal.h"
-// EC_LOOSE_SCALAR is like |EC_SCALAR| but is bounded by 2^|BN_num_bits(order)|
-// rather than |order|.
-typedef union {
- // bytes is the representation of the scalar in little-endian order.
- uint8_t bytes[EC_MAX_SCALAR_BYTES];
- BN_ULONG words[EC_MAX_SCALAR_WORDS];
-} EC_LOOSE_SCALAR;
-
-static void scalar_add_loose(const EC_GROUP *group, EC_LOOSE_SCALAR *r,
- const EC_LOOSE_SCALAR *a, const EC_SCALAR *b) {
- // Add and subtract one copy of |order| if necessary. We have:
- // |a| + |b| < 2^BN_num_bits(order) + order
- // so this leaves |r| < 2^BN_num_bits(order).
+static void scalar_add(const EC_GROUP *group, EC_SCALAR *r, const EC_SCALAR *a,
+ const EC_SCALAR *b) {
const BIGNUM *order = &group->order;
- BN_ULONG carry = bn_add_words(r->words, a->words, b->words, order->width);
- EC_LOOSE_SCALAR tmp;
- BN_ULONG v =
- bn_sub_words(tmp.words, r->words, order->d, order->width) - carry;
- bn_select_words(r->words, 0u - v, r->words /* tmp < 0 */,
- tmp.words /* tmp >= 0 */, order->width);
+ BN_ULONG tmp[EC_MAX_SCALAR_WORDS];
+ bn_mod_add_words(r->words, a->words, b->words, order->d, tmp, order->width);
+ OPENSSL_cleanse(tmp, sizeof(tmp));
}
-static int scalar_mod_mul_montgomery(const EC_GROUP *group, EC_SCALAR *r,
- const EC_SCALAR *a, const EC_SCALAR *b) {
- const BIGNUM *order = &group->order;
- return bn_mod_mul_montgomery_small(r->words, order->width, a->words,
- order->width, b->words, order->width,
- group->order_mont);
-}
-
-static int scalar_mod_mul_montgomery_loose(const EC_GROUP *group, EC_SCALAR *r,
- const EC_LOOSE_SCALAR *a,
- const EC_SCALAR *b) {
- // Although |a| is loose, |bn_mod_mul_montgomery_small| only requires the
- // product not exceed R * |order|. |b| is fully reduced and |a| <
- // 2^BN_num_bits(order) <= R, so this holds.
+static int scalar_mul_montgomery(const EC_GROUP *group, EC_SCALAR *r,
+ const EC_SCALAR *a, const EC_SCALAR *b) {
const BIGNUM *order = &group->order;
return bn_mod_mul_montgomery_small(r->words, order->width, a->words,
order->width, b->words, order->width,
@@ -111,28 +85,33 @@ static int scalar_mod_mul_montgomery_loose(const EC_GROUP *group, EC_SCALAR *r,
// digest_to_scalar interprets |digest_len| bytes from |digest| as a scalar for
// ECDSA. Note this value is not fully reduced modulo the order, only the
// correct number of bits.
-static void digest_to_scalar(const EC_GROUP *group, EC_LOOSE_SCALAR *out,
+static void digest_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
const uint8_t *digest, size_t digest_len) {
const BIGNUM *order = &group->order;
size_t num_bits = BN_num_bits(order);
// Need to truncate digest if it is too long: first truncate whole bytes.
- if (8 * digest_len > num_bits) {
- digest_len = (num_bits + 7) / 8;
+ size_t num_bytes = (num_bits + 7) / 8;
+ if (digest_len > num_bytes) {
+ digest_len = num_bytes;
}
OPENSSL_memset(out, 0, sizeof(EC_SCALAR));
for (size_t i = 0; i < digest_len; i++) {
out->bytes[i] = digest[digest_len - 1 - i];
}
- // If still too long truncate remaining bits with a shift
+ // If it is still too long, truncate remaining bits with a shift.
if (8 * digest_len > num_bits) {
- size_t shift = 8 - (num_bits & 0x7);
- for (int i = 0; i < order->width - 1; i++) {
- out->words[i] =
- (out->words[i] >> shift) | (out->words[i + 1] << (BN_BITS2 - shift));
- }
- out->words[order->width - 1] >>= shift;
+ bn_rshift_words(out->words, out->words, 8 - (num_bits & 0x7), order->width);
}
+
+ // |out| now has the same bit width as |order|, but this only bounds by
+ // 2*|order|. Subtract the order if out of range.
+ //
+ // Montgomery multiplication accepts the looser bounds, so this isn't strictly
+ // necessary, but it is a cleaner abstraction and has no performance impact.
+ BN_ULONG tmp[EC_MAX_SCALAR_WORDS];
+ bn_reduce_once_in_place(out->words, 0 /* no carry */, order->d, tmp,
+ order->width);
}
// field_element_to_scalar reduces |r| modulo |group->order|. |r| must
@@ -237,8 +216,7 @@ int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
goto err;
}
- EC_SCALAR r, s, u1, u2, s_inv_mont;
- EC_LOOSE_SCALAR m;
+ EC_SCALAR r, s, u1, u2, s_inv_mont, m;
const BIGNUM *order = EC_GROUP_get0_order(group);
if (BN_is_zero(sig->r) ||
!ec_bignum_to_scalar(group, &r, sig->r) ||
@@ -264,8 +242,8 @@ int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
// |s_inv_mont| is in Montgomery form while |m| and |r| are not, so |u1| and
// |u2| will be taken out of Montgomery form, as desired.
digest_to_scalar(group, &m, digest, digest_len);
- if (!scalar_mod_mul_montgomery_loose(group, &u1, &m, &s_inv_mont) ||
- !scalar_mod_mul_montgomery(group, &u2, &r, &s_inv_mont)) {
+ if (!scalar_mul_montgomery(group, &u1, &m, &s_inv_mont) ||
+ !scalar_mul_montgomery(group, &u2, &r, &s_inv_mont)) {
goto err;
}
@@ -402,8 +380,7 @@ ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
int ok = 0;
ECDSA_SIG *ret = ECDSA_SIG_new();
BN_CTX *ctx = BN_CTX_new();
- EC_SCALAR kinv_mont, r_mont, s;
- EC_LOOSE_SCALAR m, tmp;
+ EC_SCALAR kinv_mont, r_mont, s, m, tmp;
if (ret == NULL || ctx == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
return NULL;
@@ -422,16 +399,16 @@ ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
if (!ec_bignum_to_scalar(group, &r_mont, ret->r) ||
!bn_to_montgomery_small(r_mont.words, order->width, r_mont.words,
order->width, group->order_mont) ||
- !scalar_mod_mul_montgomery(group, &s, priv_key, &r_mont)) {
+ !scalar_mul_montgomery(group, &s, priv_key, &r_mont)) {
goto err;
}
// Compute tmp = m + priv_key * r.
- scalar_add_loose(group, &tmp, &m, &s);
+ scalar_add(group, &tmp, &m, &s);
// Finally, multiply s by k^-1. That was retained in Montgomery form, so the
// same technique as the previous multiplication works.
- if (!scalar_mod_mul_montgomery_loose(group, &s, &tmp, &kinv_mont) ||
+ if (!scalar_mul_montgomery(group, &s, &tmp, &kinv_mont) ||
!bn_set_words(ret->s, s.words, order->width)) {
goto err;
}
diff --git a/src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt b/src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt
index 513c5827..703b3eb6 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt
@@ -2164,3 +2164,112 @@ Digest = ea13b25b80ec89ffa649a00ce85a494892f9fb7389df56eed084d670efb020c05508ac3
K = 00ac3b6d61ebda99e23301fa198d686a13c0832af594b289c9a55669ce6d62011384769013748b68465527a597ed6858a06a99d50493562b3a7dbcee975ad34657d8
R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+# The following tests exercise the bit-shifting case of ECDSA digest
+# truncation. The digests are larger than even SHA-512, but P-521 is the only
+# common prime-field curve. (This case typically comes up with curves over
+# GF(2^m).)
+
+Curve = P-521
+Private = 01f0ec8da29295394f2f072672db014861be33bfd9f91349dad5566ff396bea055e53b1d61c8c4e5c9f6e129ed75a49f91cce1d5530ad4e78c2b793a63195eb9f0da
+X = 009ec1a3761fe3958073b9647f34202c5e8ca2428d056facc4f3fedc7077fa87f1d1eb30cc74f6e3ff3d3f82df2641cea1eb3ff1529e8a3866ae2055aacec0bf68c4
+Y = 00bed0261b91f664c3ff53e337d8321cb988c3edc03b46754680097e5a8585245d80d0b7045c75a9c5be7f599d3b5eea08d828acb6294ae515a3df57a37f903ef62e
+Digest = 007509d92dc07644ffd324d006742d24a4497cfdb9c4efab7768426b3877d810602a84561f82439421e49533f72f50170222ed6c24ae6c11e50b7aa886ac31801580
+K = 00ac3b6d61ebda99e23301fa198d686a13c0832af594b289c9a55669ce6d62011384769013748b68465527a597ed6858a06a99d50493562b3a7dbcee975ad34657d8
+R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
+S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+Curve = P-521
+Private = 01f0ec8da29295394f2f072672db014861be33bfd9f91349dad5566ff396bea055e53b1d61c8c4e5c9f6e129ed75a49f91cce1d5530ad4e78c2b793a63195eb9f0da
+X = 009ec1a3761fe3958073b9647f34202c5e8ca2428d056facc4f3fedc7077fa87f1d1eb30cc74f6e3ff3d3f82df2641cea1eb3ff1529e8a3866ae2055aacec0bf68c4
+Y = 00bed0261b91f664c3ff53e337d8321cb988c3edc03b46754680097e5a8585245d80d0b7045c75a9c5be7f599d3b5eea08d828acb6294ae515a3df57a37f903ef62e
+Digest = 007509d92dc07644ffd324d006742d24a4497cfdb9c4efab7768426b3877d810602a84561f82439421e49533f72f50170222ed6c24ae6c11e50b7aa886ac318015ff
+K = 00ac3b6d61ebda99e23301fa198d686a13c0832af594b289c9a55669ce6d62011384769013748b68465527a597ed6858a06a99d50493562b3a7dbcee975ad34657d8
+R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
+S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+Curve = P-521
+Private = 01f0ec8da29295394f2f072672db014861be33bfd9f91349dad5566ff396bea055e53b1d61c8c4e5c9f6e129ed75a49f91cce1d5530ad4e78c2b793a63195eb9f0da
+X = 009ec1a3761fe3958073b9647f34202c5e8ca2428d056facc4f3fedc7077fa87f1d1eb30cc74f6e3ff3d3f82df2641cea1eb3ff1529e8a3866ae2055aacec0bf68c4
+Y = 00bed0261b91f664c3ff53e337d8321cb988c3edc03b46754680097e5a8585245d80d0b7045c75a9c5be7f599d3b5eea08d828acb6294ae515a3df57a37f903ef62e
+Digest = 007509d92dc07644ffd324d006742d24a4497cfdb9c4efab7768426b3877d810602a84561f82439421e49533f72f50170222ed6c24ae6c11e50b7aa886ac318015ffffffffffff
+K = 00ac3b6d61ebda99e23301fa198d686a13c0832af594b289c9a55669ce6d62011384769013748b68465527a597ed6858a06a99d50493562b3a7dbcee975ad34657d8
+R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
+S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+
+# The following tests use digests equal to the order and 2^n - 1, where n is
+# the number of bits in the order. This is to test the truncated digest not
+# being fully reduced.
+
+Curve = P-224
+Private = a80489eee3b15dedbc2d8ca4134f18b7d1a541fc212718f21a04692c
+X = bd283d0c18d90b69d9ee3e0f1e8e62f53822f5593fc94343666495b5
+Y = b3177709b8dc4b62928f9dc561c2b4be42c7df52d4e90e7e885b4021
+Digest = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d
+K = 90fbb04276d112cbb6ecd2053e2a870f02350ac7e2881c89851a4640
+R = 7d0642a2cb98b56ff91837bd23e20bd90b60613b60eabfbc078cfbfa
+S = 0209a75bbd6c2310fa55fe2c0c3ddf35be53fef6e1cccf0537f3e7be
+
+Curve = P-224
+Private = 72a2e505634a669d492d28b1b43974cca3aac7b5eaffa1719a551d3e
+X = 42bafdd82b5bd766a727211e4af8bf46015705b878772b296791cca3
+Y = f5db26e760f4b2ec586222d3cecb525fed32a841fa0ae547f5c435db
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+K = d6abc49b0e60f1e2e7a5736aa8e93a5de9777f4b9e6c96692fcb662b
+R = 42232b212356d9adbb5e43e96e23c376fa5d21c9ad6a50137d2e3bd2
+S = 020596ef40a9dbea4d6779ff02c9cb853b520093113a968a32309118
+
+Curve = P-256
+Private = fb801b1a1161c143578358dc6edf8357167c12636e5b588e171d8bffcca78d7a
+X = e57231383637c82c1ac801724cf7e03e67198f467a9beb60ac13cb582d13afa8
+Y = 8f190e090155fcf63810b858bc88e259dc49afef8bdef6fd06d93dddb1991aed
+Digest = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
+K = 3d1df8b364fc045d8c6517f7a4b99c91643a2bca351b3a74fe36268c97198c3e
+R = 05cc6037bb021f4910ea2e489fab2bae6bb6a2769a97f42ba5736994102b7f10
+S = 5db54832ceabf8bccdb8be99b1a49cecff8feee045cb697dec43118e2695b1da
+
+Curve = P-256
+Private = df1ae1f7a1043d03811c61695dba0350bbe58d36a670da66d58c69e5bc9ce1fd
+X = 6e0e2897b9a554ee287cdaf43bfbe25ca8404373971575a0e4b61c61aff5a2fe
+Y = 23ea7823a411eb1b39f81bbde24c2cd6ac68be2c7eec3a0671c8676131b8905c
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+K = 6b6e0cf93ce4482a4c23821125186f39656ccc993e4f080ac8750c32927a515f
+R = 16831feeceab2fab1c575e073e944d73ce7e6f3e9b06312088f06159c530ff50
+S = 870cb824692638538b1569c6093fcb693c054e8e3b9a919e3bb26798910f66e9
+
+Curve = P-384
+Private = 2218a70d35d5a9eb16442eee8e74a8b992d9475edadd6b814ae6c8779b32df164553546bf3405bd5242b85092e2f0098
+X = f4a961c19f9cc4ebe4f43081110955f3cede085a08c1415d726e80b2eb774028c5fc96f092ba3ea7d1288dd57fe1db08
+Y = 981398eed0895e09b3b582a0616f3024e51cca7b1ecc347dbf0d24a5f6a222b0c31912f8f5e427d4dde5c6c45212bb10
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973
+K = 118f1682e0dc4602fc6f142f98d48e36adf32566f34be311ca55ccbe00fec28e52d72857e02f139578316a5dbe1ed9b4
+R = 0b77eaff05bbd922dd80525d2ab301cc119318f5a920a12c71c4b5ff5bb77d25a538983df9bdd5984b0d159daf21f1a2
+S = 73af85ad03a34b6b3993082bf719018d25d1555717b2d2f2535d0601af06a71ad020eff8232d065ab9d7fc4cd0c0ee42
+
+Curve = P-384
+Private = fae6a843fcef48d15685766d189fe1f597cd85d4e07172c8e19589e1aa2e8e8c4b75731e9afccb7b585926934583829b
+X = 54dd8d7cbf2ccdf1a42f5bbc615a372803b094f6040e3c7b651a61bc6912432c836cf2410ab7d67f543236751d81066f
+Y = 2219d6257b1c80bf327c96786f2b5d0b5a9b9bf7eee9c853bf66a3bf09520494cb1f7823e4c566d79a617b7e201ead96
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+K = b7db03d70db5cdcce3c708e55ad88eba80e90f6bb0be3713686bf298709a8b326619c1d47318f9af60039ff051f33a1e
+R = 9d923e199d98272e44b8fba382bf3c19660ecb4a9aae3513ff6802a73fef510c15c202807c3f9334b0bce7d6c6a80839
+S = 520784e6290d04d9b61993ee5ebc6fa8ff527fb0777c43cdefc7586701e60edb399005a5648ff852de80208232849fbd
+
+Curve = P-521
+Private = 015a5274c44e51b3cce4b1d657186871a851747e086934cb132559d83e07b3b2544c5d62b26385272101e20f963d2df6e029d6a6818cc4839c3f28a4c384dff4befa
+X = 00056cc489982829b728978193d047596325a91ee2e2c9110f7da605fd2d1b78424e87d85500f391fe9f54209c42e582ca3284484afc6edfe2acdc69c3591f6c47cf
+Y = 010e91be6632da7afd03caedebdb572fd41cb1a7221e9c2d984016bac4693b3d10c5b1d76ba32b89f5fadd157df122be9cd85151977b99176998cfccbd3f9a03ba3f
+Digest = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409
+K = 001441599703e14eca10a787dd421c334bdd4c91ad33d05fe2929100a5eb343df47fb28236423323e769ad1cbaffc6e9ad01a06b2e401d647511ad2e920c2145262d
+R = 00bd5e59a9bc97de61588d143990ad7fd5405ac53aa8e6332a085a301138b23beaba126b41549db1167df47362a9de77c73b1bfaa14b31114644b4db8d35179f706a
+S = 000cbb560f68b7240e309301ed4e6dc20d329f7e2098bcae26a07dd364e6177bb408eb5d0b47a3fcf36def98b951af9a55a47d24d95cd66cc11973269694e2f6f8d1
+
+Curve = P-521
+Private = 00cfac6f8a1906241d873da27b4166e0d0bd76c511177835d0978117056db44750eb0648e6899f215e6c0dd6902c114a802ed5935df8c54290fbfe184ff8ccae444e
+X = 002aca58eeac43152b292f42a6a677d327386337409ba7de17acae1978e097f21e49d47f707c6ed6045c66551c93df9ef9bcc442db804e62fcac9f0574876d6d7fea
+Y = 01862ed4f9d235afcc4e6b45e491da363104d4db7b97f12d869c40ab09a3c8c72519a9712ca733ddf046ad039842e8caed2425ecaf42d5171b3e236c11fee8699684
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+K = 00938d2f6550a46fb07b058e6287f428f0ff12aa6732a666d4a6cf2dd7cd8023ca76d0ce4e16b62830d0ff9e2fab9987261f3f3ffe0749ff70950d91b897d57007b2
+R = 00ec0b91fa4386a8acdc0e46dd9c1d1775abbe0da8ead424aa4ace58e284a5be00e2c1ef95b6f4d861615564e1e7305656567f95275ce63b534420eae77ec37492c2
+S = 01e1099fb389db498ab4cf23b4f06a74b9326878ae3c76ea13832e50702b30fe8303093a59cc9a0995f1dfc15e6f7dabca8a2acaf03ec005447d29fb429a252064ec
diff --git a/src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt b/src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt
index a1b4cb2e..aa2fbd3c 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt
@@ -2344,3 +2344,90 @@ Digest = c18be2e3f935561d1ad1cacf6ae06e733a463c7e5063cbb0cfaf162a579522786755dff
R = 0000db4c31f316912295c5b9506aabc24b0b2dc2b2358e6b023148889d9200bcf44762e88575e359b4868b2d93ba7bdb24800b09fc22eade0744b9832b71ee784e9c
S = 018c84437fac7cd82099a2a4230084ac27ec7ea9c92e1c9d9a71290df9b37dc881f9ba59ed331c22dca4b2cbb837cd916e0a78398d2b7aaf8e88f113a942beac48c0
Invalid =
+
+# The following tests exercise the bit-shifting case of ECDSA digest
+# truncation. The digests are larger than even SHA-512, but P-521 is the only
+# common prime-field curve. (This case typically comes up with curves over
+# GF(2^m).)
+
+Curve = P-521
+X = 009ec1a3761fe3958073b9647f34202c5e8ca2428d056facc4f3fedc7077fa87f1d1eb30cc74f6e3ff3d3f82df2641cea1eb3ff1529e8a3866ae2055aacec0bf68c4
+Y = 00bed0261b91f664c3ff53e337d8321cb988c3edc03b46754680097e5a8585245d80d0b7045c75a9c5be7f599d3b5eea08d828acb6294ae515a3df57a37f903ef62e
+Digest = 007509d92dc07644ffd324d006742d24a4497cfdb9c4efab7768426b3877d810602a84561f82439421e49533f72f50170222ed6c24ae6c11e50b7aa886ac31801580
+R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
+S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+Curve = P-521
+X = 009ec1a3761fe3958073b9647f34202c5e8ca2428d056facc4f3fedc7077fa87f1d1eb30cc74f6e3ff3d3f82df2641cea1eb3ff1529e8a3866ae2055aacec0bf68c4
+Y = 00bed0261b91f664c3ff53e337d8321cb988c3edc03b46754680097e5a8585245d80d0b7045c75a9c5be7f599d3b5eea08d828acb6294ae515a3df57a37f903ef62e
+Digest = 007509d92dc07644ffd324d006742d24a4497cfdb9c4efab7768426b3877d810602a84561f82439421e49533f72f50170222ed6c24ae6c11e50b7aa886ac318015ff
+R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
+S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+Curve = P-521
+X = 009ec1a3761fe3958073b9647f34202c5e8ca2428d056facc4f3fedc7077fa87f1d1eb30cc74f6e3ff3d3f82df2641cea1eb3ff1529e8a3866ae2055aacec0bf68c4
+Y = 00bed0261b91f664c3ff53e337d8321cb988c3edc03b46754680097e5a8585245d80d0b7045c75a9c5be7f599d3b5eea08d828acb6294ae515a3df57a37f903ef62e
+Digest = 007509d92dc07644ffd324d006742d24a4497cfdb9c4efab7768426b3877d810602a84561f82439421e49533f72f50170222ed6c24ae6c11e50b7aa886ac318015ffffffffffff
+R = 00cef3f4babe6f9875e5db28c27d6a197d607c3641a90f10c2cc2cb302ba658aa151dc76c507488b99f4b3c8bb404fb5c852f959273f412cbdd5e713c5e3f0e67f94
+S = 00097ed9e005416fc944e26bcc3661a09b35c128fcccdc2742739c8a301a338dd77d9d13571612a3b9524a6164b09fe73643bbc31447ee31ef44a490843e4e7db23f
+
+
+# The following tests use digests equal to the order and 2^n - 1, where n is
+# the number of bits in the order. This is to test the truncated digest not
+# being fully reduced.
+
+Curve = P-224
+X = bd283d0c18d90b69d9ee3e0f1e8e62f53822f5593fc94343666495b5
+Y = b3177709b8dc4b62928f9dc561c2b4be42c7df52d4e90e7e885b4021
+Digest = ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d
+R = 7d0642a2cb98b56ff91837bd23e20bd90b60613b60eabfbc078cfbfa
+S = 0209a75bbd6c2310fa55fe2c0c3ddf35be53fef6e1cccf0537f3e7be
+
+Curve = P-224
+X = 42bafdd82b5bd766a727211e4af8bf46015705b878772b296791cca3
+Y = f5db26e760f4b2ec586222d3cecb525fed32a841fa0ae547f5c435db
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+R = 42232b212356d9adbb5e43e96e23c376fa5d21c9ad6a50137d2e3bd2
+S = 020596ef40a9dbea4d6779ff02c9cb853b520093113a968a32309118
+
+Curve = P-256
+X = e57231383637c82c1ac801724cf7e03e67198f467a9beb60ac13cb582d13afa8
+Y = 8f190e090155fcf63810b858bc88e259dc49afef8bdef6fd06d93dddb1991aed
+Digest = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
+R = 05cc6037bb021f4910ea2e489fab2bae6bb6a2769a97f42ba5736994102b7f10
+S = 5db54832ceabf8bccdb8be99b1a49cecff8feee045cb697dec43118e2695b1da
+
+Curve = P-256
+X = 6e0e2897b9a554ee287cdaf43bfbe25ca8404373971575a0e4b61c61aff5a2fe
+Y = 23ea7823a411eb1b39f81bbde24c2cd6ac68be2c7eec3a0671c8676131b8905c
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+R = 16831feeceab2fab1c575e073e944d73ce7e6f3e9b06312088f06159c530ff50
+S = 870cb824692638538b1569c6093fcb693c054e8e3b9a919e3bb26798910f66e9
+
+Curve = P-384
+X = f4a961c19f9cc4ebe4f43081110955f3cede085a08c1415d726e80b2eb774028c5fc96f092ba3ea7d1288dd57fe1db08
+Y = 981398eed0895e09b3b582a0616f3024e51cca7b1ecc347dbf0d24a5f6a222b0c31912f8f5e427d4dde5c6c45212bb10
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973
+R = 0b77eaff05bbd922dd80525d2ab301cc119318f5a920a12c71c4b5ff5bb77d25a538983df9bdd5984b0d159daf21f1a2
+S = 73af85ad03a34b6b3993082bf719018d25d1555717b2d2f2535d0601af06a71ad020eff8232d065ab9d7fc4cd0c0ee42
+
+Curve = P-384
+X = 54dd8d7cbf2ccdf1a42f5bbc615a372803b094f6040e3c7b651a61bc6912432c836cf2410ab7d67f543236751d81066f
+Y = 2219d6257b1c80bf327c96786f2b5d0b5a9b9bf7eee9c853bf66a3bf09520494cb1f7823e4c566d79a617b7e201ead96
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+R = 9d923e199d98272e44b8fba382bf3c19660ecb4a9aae3513ff6802a73fef510c15c202807c3f9334b0bce7d6c6a80839
+S = 520784e6290d04d9b61993ee5ebc6fa8ff527fb0777c43cdefc7586701e60edb399005a5648ff852de80208232849fbd
+
+Curve = P-521
+X = 00056cc489982829b728978193d047596325a91ee2e2c9110f7da605fd2d1b78424e87d85500f391fe9f54209c42e582ca3284484afc6edfe2acdc69c3591f6c47cf
+Y = 010e91be6632da7afd03caedebdb572fd41cb1a7221e9c2d984016bac4693b3d10c5b1d76ba32b89f5fadd157df122be9cd85151977b99176998cfccbd3f9a03ba3f
+Digest = 01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409
+R = 00bd5e59a9bc97de61588d143990ad7fd5405ac53aa8e6332a085a301138b23beaba126b41549db1167df47362a9de77c73b1bfaa14b31114644b4db8d35179f706a
+S = 000cbb560f68b7240e309301ed4e6dc20d329f7e2098bcae26a07dd364e6177bb408eb5d0b47a3fcf36def98b951af9a55a47d24d95cd66cc11973269694e2f6f8d1
+
+Curve = P-521
+X = 002aca58eeac43152b292f42a6a677d327386337409ba7de17acae1978e097f21e49d47f707c6ed6045c66551c93df9ef9bcc442db804e62fcac9f0574876d6d7fea
+Y = 01862ed4f9d235afcc4e6b45e491da363104d4db7b97f12d869c40ab09a3c8c72519a9712ca733ddf046ad039842e8caed2425ecaf42d5171b3e236c11fee8699684
+Digest = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+R = 00ec0b91fa4386a8acdc0e46dd9c1d1775abbe0da8ead424aa4ace58e284a5be00e2c1ef95b6f4d861615564e1e7305656567f95275ce63b534420eae77ec37492c2
+S = 01e1099fb389db498ab4cf23b4f06a74b9326878ae3c76ea13832e50702b30fe8303093a59cc9a0995f1dfc15e6f7dabca8a2acaf03ec005447d29fb429a252064ec
diff --git a/src/crypto/fipsmodule/rsa/internal.h b/src/crypto/fipsmodule/rsa/internal.h
index 0f0c763f..f9130580 100644
--- a/src/crypto/fipsmodule/rsa/internal.h
+++ b/src/crypto/fipsmodule/rsa/internal.h
@@ -114,15 +114,10 @@ int RSA_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
size_t len);
-// The following utility functions are exported for test purposes.
-
+// This constant is exported for test purposes.
extern const BN_ULONG kBoringSSLRSASqrtTwo[];
extern const size_t kBoringSSLRSASqrtTwoLen;
-// rsa_greater_than_pow2 returns one if |b| is greater than 2^|n| and zero
-// otherwise.
-int rsa_greater_than_pow2(const BIGNUM *b, int n);
-
#if defined(__cplusplus)
} // extern C
diff --git a/src/crypto/fipsmodule/rsa/rsa.c b/src/crypto/fipsmodule/rsa/rsa.c
index 6477a26e..aed87a68 100644
--- a/src/crypto/fipsmodule/rsa/rsa.c
+++ b/src/crypto/fipsmodule/rsa/rsa.c
@@ -634,8 +634,25 @@ err:
return ret;
}
+static int check_mod_inverse(int *out_ok, const BIGNUM *a, const BIGNUM *ainv,
+ const BIGNUM *m, int check_reduced, BN_CTX *ctx) {
+ BN_CTX_start(ctx);
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ int ret = tmp != NULL &&
+ bn_mul_consttime(tmp, a, ainv, ctx) &&
+ bn_div_consttime(NULL, tmp, tmp, m, ctx);
+ if (ret) {
+ *out_ok = BN_is_one(tmp);
+ if (check_reduced && (BN_is_negative(ainv) || BN_cmp(ainv, m) >= 0)) {
+ *out_ok = 0;
+ }
+ }
+ BN_CTX_end(ctx);
+ return ret;
+}
+
int RSA_check_key(const RSA *key) {
- BIGNUM n, pm1, qm1, lcm, gcd, de, dmp1, dmq1, iqmp_times_q;
+ BIGNUM n, pm1, qm1, lcm, dmp1, dmq1, iqmp_times_q;
BN_CTX *ctx;
int ok = 0, has_crt_values;
@@ -670,26 +687,20 @@ int RSA_check_key(const RSA *key) {
BN_init(&pm1);
BN_init(&qm1);
BN_init(&lcm);
- BN_init(&gcd);
- BN_init(&de);
BN_init(&dmp1);
BN_init(&dmq1);
BN_init(&iqmp_times_q);
- if (!BN_mul(&n, key->p, key->q, ctx) ||
+ int d_ok;
+ if (!bn_mul_consttime(&n, key->p, key->q, ctx) ||
// lcm = lcm(p, q)
- !BN_sub(&pm1, key->p, BN_value_one()) ||
- !BN_sub(&qm1, key->q, BN_value_one()) ||
- !BN_mul(&lcm, &pm1, &qm1, ctx) ||
- !BN_gcd(&gcd, &pm1, &qm1, ctx)) {
- OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
- goto out;
- }
-
- if (!BN_div(&lcm, NULL, &lcm, &gcd, ctx) ||
- !BN_gcd(&gcd, &pm1, &qm1, ctx) ||
- // de = d*e mod lcm(p, q).
- !BN_mod_mul(&de, key->d, key->e, &lcm, ctx)) {
+ !bn_usub_consttime(&pm1, key->p, BN_value_one()) ||
+ !bn_usub_consttime(&qm1, key->q, BN_value_one()) ||
+ !bn_lcm_consttime(&lcm, &pm1, &qm1, ctx) ||
+ // Other implementations use the Euler totient rather than the Carmichael
+ // totient, so allow unreduced |key->d|.
+ !check_mod_inverse(&d_ok, key->e, key->d, &lcm,
+ 0 /* don't require reduced */, ctx)) {
OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
goto out;
}
@@ -699,11 +710,16 @@ int RSA_check_key(const RSA *key) {
goto out;
}
- if (!BN_is_one(&de)) {
+ if (!d_ok) {
OPENSSL_PUT_ERROR(RSA, RSA_R_D_E_NOT_CONGRUENT_TO_1);
goto out;
}
+ if (BN_is_negative(key->d) || BN_cmp(key->d, key->n) >= 0) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_D_OUT_OF_RANGE);
+ goto out;
+ }
+
has_crt_values = key->dmp1 != NULL;
if (has_crt_values != (key->dmq1 != NULL) ||
has_crt_values != (key->iqmp != NULL)) {
@@ -712,20 +728,18 @@ int RSA_check_key(const RSA *key) {
}
if (has_crt_values) {
- if (// dmp1 = d mod (p-1)
- !BN_mod(&dmp1, key->d, &pm1, ctx) ||
- // dmq1 = d mod (q-1)
- !BN_mod(&dmq1, key->d, &qm1, ctx) ||
- // iqmp = q^-1 mod p
- !BN_mod_mul(&iqmp_times_q, key->iqmp, key->q, key->p, ctx)) {
+ int dmp1_ok, dmq1_ok, iqmp_ok;
+ if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1,
+ 1 /* check reduced */, ctx) ||
+ !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1,
+ 1 /* check reduced */, ctx) ||
+ !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p,
+ 1 /* check reduced */, ctx)) {
OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
goto out;
}
- if (BN_cmp(&dmp1, key->dmp1) != 0 ||
- BN_cmp(&dmq1, key->dmq1) != 0 ||
- BN_cmp(key->iqmp, key->p) >= 0 ||
- !BN_is_one(&iqmp_times_q)) {
+ if (!dmp1_ok || !dmq1_ok || !iqmp_ok) {
OPENSSL_PUT_ERROR(RSA, RSA_R_CRT_VALUES_INCORRECT);
goto out;
}
@@ -738,8 +752,6 @@ out:
BN_free(&pm1);
BN_free(&qm1);
BN_free(&lcm);
- BN_free(&gcd);
- BN_free(&de);
BN_free(&dmp1);
BN_free(&dmq1);
BN_free(&iqmp_times_q);
diff --git a/src/crypto/fipsmodule/rsa/rsa_impl.c b/src/crypto/fipsmodule/rsa/rsa_impl.c
index 625f1010..49cbc15a 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -862,7 +862,7 @@ static int mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) {
!BN_mod_exp_mont_consttime(r0, r1, dmp1, p, ctx, mont_p) ||
// Compute r0 = r0 - m1 mod p. |p| is the larger prime, so |m1| is already
// fully reduced mod |p|.
- !bn_mod_sub_quick_ctx(r0, r0, m1, p, ctx) ||
+ !bn_mod_sub_consttime(r0, r0, m1, p, ctx) ||
// r0 = r0 * iqmp mod p. We use Montgomery multiplication to compute this
// in constant time. |inv_small_mod_large_mont| is in Montgomery form and
// r0 is not, so the result is taken out of Montgomery form.
@@ -873,8 +873,8 @@ static int mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) {
// so it is correct mod q. Finally, the result is bounded by [m1, n + m1),
// and the result is at least |m1|, so this must be the unique answer in
// [0, n).
- !bn_mul_fixed(r0, r0, q, ctx) ||
- !bn_uadd_fixed(r0, r0, m1) ||
+ !bn_mul_consttime(r0, r0, q, ctx) ||
+ !bn_uadd_consttime(r0, r0, m1) ||
// The result should be bounded by |n|, but fixed-width operations may
// bound the width slightly higher, so fix it.
!bn_resize_words(r0, n->width)) {
@@ -924,25 +924,20 @@ const BN_ULONG kBoringSSLRSASqrtTwo[] = {
};
const size_t kBoringSSLRSASqrtTwoLen = OPENSSL_ARRAY_SIZE(kBoringSSLRSASqrtTwo);
-int rsa_greater_than_pow2(const BIGNUM *b, int n) {
- if (BN_is_negative(b) || n == INT_MAX) {
- return 0;
- }
-
- int b_bits = BN_num_bits(b);
- return b_bits > n + 1 || (b_bits == n + 1 && !BN_is_pow2(b));
-}
-
// generate_prime sets |out| to a prime with length |bits| such that |out|-1 is
// relatively prime to |e|. If |p| is non-NULL, |out| will also not be close to
-// |p|.
+// |p|. |sqrt2| must be ⌊2^(bits-1)×√2⌋ (or a slightly overestimate for large
+// sizes), and |pow2_bits_100| must be 2^(bits-100).
static int generate_prime(BIGNUM *out, int bits, const BIGNUM *e,
- const BIGNUM *p, const BIGNUM *sqrt2, BN_CTX *ctx,
+ const BIGNUM *p, const BIGNUM *sqrt2,
+ const BIGNUM *pow2_bits_100, BN_CTX *ctx,
BN_GENCB *cb) {
if (bits < 128 || (bits % BN_BITS2) != 0) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
}
+ assert(BN_is_pow2(pow2_bits_100));
+ assert(BN_is_bit_set(pow2_bits_100, bits - 100));
// See FIPS 186-4 appendix B.3.3, steps 4 and 5. Note |bits| here is nlen/2.
@@ -973,11 +968,10 @@ static int generate_prime(BIGNUM *out, int bits, const BIGNUM *e,
if (p != NULL) {
// If |p| and |out| are too close, try again (step 5.4).
- if (!BN_sub(tmp, out, p)) {
+ if (!bn_abs_sub_consttime(tmp, out, p, ctx)) {
goto err;
}
- BN_set_negative(tmp, 0);
- if (!rsa_greater_than_pow2(tmp, bits - 100)) {
+ if (BN_cmp(tmp, pow2_bits_100) <= 0) {
continue;
}
}
@@ -993,21 +987,26 @@ static int generate_prime(BIGNUM *out, int bits, const BIGNUM *e,
continue;
}
- // Check gcd(out-1, e) is one (steps 4.5 and 5.6).
- if (!BN_sub(tmp, out, BN_value_one()) ||
- !BN_gcd(tmp, tmp, e, ctx)) {
- goto err;
- }
- if (BN_is_one(tmp)) {
- // Test |out| for primality (steps 4.5.1 and 5.6.1).
- int is_probable_prime;
- if (!BN_primality_test(&is_probable_prime, out, BN_prime_checks, ctx, 1,
- cb)) {
+ // RSA key generation's bottleneck is discarding composites. If it fails
+ // trial division, do not bother computing a GCD or performing Rabin-Miller.
+ if (!bn_odd_number_is_obviously_composite(out)) {
+ // Check gcd(out-1, e) is one (steps 4.5 and 5.6).
+ int relatively_prime;
+ if (!BN_sub(tmp, out, BN_value_one()) ||
+ !bn_is_relatively_prime(&relatively_prime, tmp, e, ctx)) {
goto err;
}
- if (is_probable_prime) {
- ret = 1;
- goto err;
+ if (relatively_prime) {
+ // Test |out| for primality (steps 4.5.1 and 5.6.1).
+ int is_probable_prime;
+ if (!BN_primality_test(&is_probable_prime, out, BN_prime_checks, ctx, 0,
+ cb)) {
+ goto err;
+ }
+ if (is_probable_prime) {
+ ret = 1;
+ goto err;
+ }
}
}
@@ -1043,7 +1042,19 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
return 0;
}
+ // Reject excessively large public exponents. Windows CryptoAPI and Go don't
+ // support values larger than 32 bits, so match their limits for generating
+ // keys. (|check_modulus_and_exponent_sizes| uses a slightly more conservative
+ // value, but we don't need to support generating such keys.)
+ // https://github.com/golang/go/issues/3161
+ // https://msdn.microsoft.com/en-us/library/aa387685(VS.85).aspx
+ if (BN_num_bits(e_value) > 32) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_E_VALUE);
+ return 0;
+ }
+
int ret = 0;
+ int prime_bits = bits / 2;
BN_CTX *ctx = BN_CTX_new();
if (ctx == NULL) {
goto bn_err;
@@ -1052,10 +1063,13 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
BIGNUM *totient = BN_CTX_get(ctx);
BIGNUM *pm1 = BN_CTX_get(ctx);
BIGNUM *qm1 = BN_CTX_get(ctx);
- BIGNUM *gcd = BN_CTX_get(ctx);
BIGNUM *sqrt2 = BN_CTX_get(ctx);
- if (totient == NULL || pm1 == NULL || qm1 == NULL || gcd == NULL ||
- sqrt2 == NULL) {
+ BIGNUM *pow2_prime_bits_100 = BN_CTX_get(ctx);
+ BIGNUM *pow2_prime_bits = BN_CTX_get(ctx);
+ if (totient == NULL || pm1 == NULL || qm1 == NULL || sqrt2 == NULL ||
+ pow2_prime_bits_100 == NULL || pow2_prime_bits == NULL ||
+ !BN_set_bit(pow2_prime_bits_100, prime_bits - 100) ||
+ !BN_set_bit(pow2_prime_bits, prime_bits)) {
goto bn_err;
}
@@ -1074,8 +1088,6 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
goto bn_err;
}
- int prime_bits = bits / 2;
-
// Compute sqrt2 >= ⌊2^(prime_bits-1)×√2⌋.
if (!bn_set_words(sqrt2, kBoringSSLRSASqrtTwo, kBoringSSLRSASqrtTwoLen)) {
goto bn_err;
@@ -1101,9 +1113,11 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
do {
// Generate p and q, each of size |prime_bits|, using the steps outlined in
// appendix FIPS 186-4 appendix B.3.3.
- if (!generate_prime(rsa->p, prime_bits, rsa->e, NULL, sqrt2, ctx, cb) ||
+ if (!generate_prime(rsa->p, prime_bits, rsa->e, NULL, sqrt2,
+ pow2_prime_bits_100, ctx, cb) ||
!BN_GENCB_call(cb, 3, 0) ||
- !generate_prime(rsa->q, prime_bits, rsa->e, rsa->p, sqrt2, ctx, cb) ||
+ !generate_prime(rsa->q, prime_bits, rsa->e, rsa->p, sqrt2,
+ pow2_prime_bits_100, ctx, cb) ||
!BN_GENCB_call(cb, 3, 1)) {
goto bn_err;
}
@@ -1121,27 +1135,27 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
// q-1. However, we do operations with Chinese Remainder Theorem, so we only
// use d (mod p-1) and d (mod q-1) as exponents. Using a minimal totient
// does not affect those two values.
- if (!BN_sub(pm1, rsa->p, BN_value_one()) ||
- !BN_sub(qm1, rsa->q, BN_value_one()) ||
- !BN_mul(totient, pm1, qm1, ctx) ||
- !BN_gcd(gcd, pm1, qm1, ctx) ||
- !BN_div(totient, NULL, totient, gcd, ctx) ||
- !BN_mod_inverse(rsa->d, rsa->e, totient, ctx)) {
+ int no_inverse;
+ if (!bn_usub_consttime(pm1, rsa->p, BN_value_one()) ||
+ !bn_usub_consttime(qm1, rsa->q, BN_value_one()) ||
+ !bn_lcm_consttime(totient, pm1, qm1, ctx) ||
+ !bn_mod_inverse_consttime(rsa->d, &no_inverse, rsa->e, totient, ctx)) {
goto bn_err;
}
- // Check that |rsa->d| > 2^|prime_bits| and try again if it fails. See
- // appendix B.3.1's guidance on values for d.
- } while (!rsa_greater_than_pow2(rsa->d, prime_bits));
+ // Retry if |rsa->d| <= 2^|prime_bits|. See appendix B.3.1's guidance on
+ // values for d.
+ } while (BN_cmp(rsa->d, pow2_prime_bits) <= 0);
if (// Calculate n.
- !BN_mul(rsa->n, rsa->p, rsa->q, ctx) ||
+ !bn_mul_consttime(rsa->n, rsa->p, rsa->q, ctx) ||
// Calculate d mod (p-1).
- !BN_mod(rsa->dmp1, rsa->d, pm1, ctx) ||
+ !bn_div_consttime(NULL, rsa->dmp1, rsa->d, pm1, ctx) ||
// Calculate d mod (q-1)
- !BN_mod(rsa->dmq1, rsa->d, qm1, ctx)) {
+ !bn_div_consttime(NULL, rsa->dmq1, rsa->d, qm1, ctx)) {
goto bn_err;
}
+ bn_set_minimal_width(rsa->n);
// Sanity-check that |rsa->n| has the specified size. This is implied by
// |generate_prime|'s bounds.
diff --git a/src/crypto/rsa_extra/rsa_test.cc b/src/crypto/rsa_extra/rsa_test.cc
index fdd5e497..a6bfb87b 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -826,6 +826,21 @@ TEST(RSATest, CheckKey) {
ASSERT_TRUE(BN_hex2bn(&rsa->d, kDEuler));
EXPECT_TRUE(RSA_check_key(rsa.get()));
+ // If d is completely out of range but otherwise valid, it is rejected.
+ static const char kDTooLarge[] =
+ "f2c885128cf04101c283553617c210d8ffd14cde98dc420c3c9892b55606cbedcda24298"
+ "7655b3f7b9433c2c316293a1cf1a2b034f197aeec1de8d81a67d94cc902b9fce1712d5a4"
+ "9c257ff705725cd77338d23535d3b87c8f4cecc15a6b72641ffd81aea106839d216b5fcd"
+ "7d415751d27255e540dd1638a8389721e9d0807d65d24d7b8c2f60e4b2c0bf250544ce68"
+ "b5ddbc1463d5a4638b2816b0f033dacdc0162f329af9e4d142352521fbd2fe14af824ef3"
+ "1601fe843c79cc3efbcb8eafd79262bdd25e2bdf21440f774e26d88ed7df938c5cf6982d"
+ "e9fa635b8ca36ce5c5fbd579a53cbb0348ceae752d4bc5621c5acc922ca2082494633337"
+ "42e770c1";
+ ASSERT_TRUE(BN_hex2bn(&rsa->d, kDTooLarge));
+ EXPECT_FALSE(RSA_check_key(rsa.get()));
+ ERR_clear_error();
+ ASSERT_TRUE(BN_hex2bn(&rsa->d, kD));
+
// CRT value must either all be provided or all missing.
ASSERT_TRUE(BN_hex2bn(&rsa->dmp1, kDMP1));
EXPECT_FALSE(RSA_check_key(rsa.get()));
@@ -909,54 +924,4 @@ TEST(RSATest, SqrtTwo) {
// Check the kBoringSSLRSASqrtTwo is sized for a 3072-bit RSA key.
EXPECT_EQ(3072u / 2u, bits);
}
-
-TEST(RSATest, GreaterThanPow2) {
- bssl::UniquePtr<BIGNUM> b(BN_new());
- BN_zero(b.get());
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 0));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 20));
-
- ASSERT_TRUE(BN_set_word(b.get(), 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 0));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 20));
-
- ASSERT_TRUE(BN_set_word(b.get(), 2));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), 0));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 20));
-
- ASSERT_TRUE(BN_set_word(b.get(), 3));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), 0));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 2));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 20));
-
- BN_set_negative(b.get(), 1);
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 0));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 2));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), 20));
-
- // Check all bit lengths mod 64.
- for (int n = 1024; n < 1024 + 64; n++) {
- SCOPED_TRACE(n);
- ASSERT_TRUE(BN_set_word(b.get(), 1));
- ASSERT_TRUE(BN_lshift(b.get(), b.get(), n));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), n - 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), n));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), n + 1));
-
- ASSERT_TRUE(BN_sub_word(b.get(), 1));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), n - 1));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), n));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), n + 1));
-
- ASSERT_TRUE(BN_add_word(b.get(), 2));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), n - 1));
- EXPECT_TRUE(rsa_greater_than_pow2(b.get(), n));
- EXPECT_FALSE(rsa_greater_than_pow2(b.get(), n + 1));
- }
-}
#endif // !BORINGSSL_SHARED_LIBRARY
diff --git a/src/crypto/x509/vpm_int.h b/src/crypto/x509/vpm_int.h
index 9c55defc..53b4a0d5 100644
--- a/src/crypto/x509/vpm_int.h
+++ b/src/crypto/x509/vpm_int.h
@@ -67,4 +67,5 @@ struct X509_VERIFY_PARAM_ID_st {
size_t emaillen;
unsigned char *ip; /* If not NULL IP address to match */
size_t iplen; /* Length of IP address */
+ unsigned char poison; /* Fail all verifications */
};
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index ed4978aa..0c5fc2d7 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -12,6 +12,7 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <functional>
#include <string>
#include <vector>
@@ -422,6 +423,78 @@ static const char kEd25519CertNull[] =
"recgVPpVS7B+d9g4EwtZXIh4lodTBDHBBw==\n"
"-----END CERTIFICATE-----\n";
+// kSANTypesLeaf is a leaf certificate (signed by |kSANTypesRoot|) which
+// contains SANS for example.com, test@example.com, 127.0.0.1, and
+// https://example.com/. (The latter is useless for now since crypto/x509
+// doesn't deal with URI SANs directly.)
+static const char kSANTypesLeaf[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIClzCCAgCgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCsxFzAVBgNV\n"
+ "BAoTDkJvcmluZ1NTTCBUZXN0MRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAw\n"
+ "MDAwMFoXDTI1MDEwMTAwMDAwMFowLzEXMBUGA1UEChMOQm9yaW5nU1NMIFRlc3Qx\n"
+ "FDASBgNVBAMTC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\n"
+ "gQDbRn2TLhInBki8Bighq37EtqJd/h5SRYh6NkelCA2SQlvCgcC+l3mYQPtPbRT9\n"
+ "KxOLwqUuZ9jUCZ7WIji3Sgt0cyvCNPHRk+WW2XR781ifbGE8wLBB1NkrKyQjd1sc\n"
+ "O711Xc4gVM+hY4cdHiTE8x0aUIuqthRD7ZendWL0FMhS1wIDAQABo4G+MIG7MA4G\n"
+ "A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD\n"
+ "VR0TAQH/BAIwADAZBgNVHQ4EEgQQn5EWH0NDPkmm3m22gNefYDAbBgNVHSMEFDAS\n"
+ "gBBAN9cB+0AvuBx+VAQnjFkBMEQGA1UdEQQ9MDuCC2V4YW1wbGUuY29tgRB0ZXN0\n"
+ "QGV4YW1wbGUuY29thwR/AAABhhRodHRwczovL2V4YW1wbGUuY29tLzANBgkqhkiG\n"
+ "9w0BAQsFAAOBgQBtwJvY6+Tk6D6DOtDVaNoJ5y8E25CCuE/Ga4OuIcYJas+yLckf\n"
+ "dZwUV3GUG2oBXl2MrpUFxXd4hKBO1CmlBY+hZEeIx0Yp6QWK9P/vnZeydOTP26mk\n"
+ "jusJ2PqSmtKNU1Zcaba4d29oFejmOAfeguhR8AHpsc/zHEaS5Q9cJsuJcw==\n"
+ "-----END CERTIFICATE-----\n";
+
+// -----BEGIN RSA PRIVATE KEY-----
+// MIICWwIBAAKBgQDbRn2TLhInBki8Bighq37EtqJd/h5SRYh6NkelCA2SQlvCgcC+
+// l3mYQPtPbRT9KxOLwqUuZ9jUCZ7WIji3Sgt0cyvCNPHRk+WW2XR781ifbGE8wLBB
+// 1NkrKyQjd1scO711Xc4gVM+hY4cdHiTE8x0aUIuqthRD7ZendWL0FMhS1wIDAQAB
+// AoGACwf7z0i1DxOI2zSwFimLghfyCSp8mgT3fbZ3Wj0SebYu6ZUffjceneM/AVrq
+// gGYHYLOVHcWJqfkl7X3hPo9SDhzLx0mM545/q21ZWCwjhswH7WiCEqV2/zeDO9WU
+// NIO1VU0VoLm0AQ7ZvwnyB+fpgF9kkkDtbBJW7XWrfNVtlnECQQD97YENpEJ3X1kj
+// 3rrkrHWDkKAyoWWY1i8Fm7LnganC9Bv6AVwgn5ZlE/479aWHF8vbOFEA3pFPiNZJ
+// t9FTCfpJAkEA3RCXjGI0Y6GALFLwEs+nL/XZAfJaIpJEZVLCVosYQOSaMS4SchfC
+// GGYVquT7ZgKk9uvz89Fg87OtBMWS9lrkHwJADGkGLKeBhBoJ3kHtem2fVK3F1pOi
+// xoR5SdnhNYVVyaxqjZ5xZTrHe+stOrr3uxGDqhQniVZXXb6/Ul0Egv1y2QJAVg/h
+// kAujba4wIhFf2VLyOZ+yjil1ocPj0LZ5Zgvcs1bMGJ1hHP3W2HzVrqRaowoggui1
+// HpTC891dXGA2qKYV7QJAFDmT2A7OVvh3y4AEgzVwHrDmCMwMHKjCIntS7fjxrJnF
+// YvJUG1zoHwUVrxxbR3DbpTODlktLcl/0b97D0IkH3w==
+// -----END RSA PRIVATE KEY-----
+
+static const char kSANTypesRoot[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICTTCCAbagAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwKzEXMBUGA1UE\n"
+ "ChMOQm9yaW5nU1NMIFRlc3QxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAw\n"
+ "MDAwWhcNMjUwMTAxMDAwMDAwWjArMRcwFQYDVQQKEw5Cb3JpbmdTU0wgVGVzdDEQ\n"
+ "MA4GA1UEAxMHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6Q5/\n"
+ "EQzmWuaGg3D2UQcuAngR9bIkkjjuJmICx5TxPqF3asCP1SJotl3iTNrghRE1wpJy\n"
+ "SY2BtIiXa7f8skRb2U0GcPkMxo/ps9+jaoRsQ1m+nbLQdpvD1/qZWcO45fNTA71J\n"
+ "1rPMokP+rcILuQG4VimUAySnDSghKamulFtK+Z8CAwEAAaN6MHgwDgYDVR0PAQH/\n"
+ "BAQDAgIEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8E\n"
+ "BTADAQH/MBkGA1UdDgQSBBBAN9cB+0AvuBx+VAQnjFkBMBsGA1UdIwQUMBKAEEA3\n"
+ "1wH7QC+4HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAc4N6hTE62/3gwg+kyc2f\n"
+ "c/Jj1mHrOt+0NRaBnmvbmNpsEjHS96Ef4Wt/ZlPXPkkv1C1VosJnOIMF3Q522wRH\n"
+ "bqaxARldS12VAa3gcWisDWD+SqSyDxjyojz0XDiJkTrFuCTCUiZO+1GLB7SO10Ms\n"
+ "d5YVX0c90VMnUhF/dlrqS9U=\n"
+ "-----END CERTIFICATE-----\n";
+
+// -----BEGIN RSA PRIVATE KEY-----
+// MIICXAIBAAKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1siSSOO4mYgLHlPE+oXdqwI/V
+// Imi2XeJM2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw+QzGj+mz36NqhGxDWb6dstB2
+// m8PX+plZw7jl81MDvUnWs8yiQ/6twgu5AbhWKZQDJKcNKCEpqa6UW0r5nwIDAQAB
+// AoGALEF5daZqc+aEsp8X1yky3nsoheyPL0kqSBWii33IFemZgKcSaRnAoqjPWWLS
+// 8dHj0I/4rej2MW8iuezVSpDak9tK5boHORC3w4p/wifkizQkLt1DANxTVbzcKvrt
+// aZ7LjVaKkhjRJbLddniowFHkkWVbUccjvzcUd7Y2VuLbAhECQQDq4FE88aHio8zg
+// bxSd0PwjEFwLYQTR19u812SoR8PmR6ofIL+pDwOV+fVs+OGcAAOgkhIukOrksQ4A
+// 1cKtnyhXAkEA/gRI+u3tZ7UE1twIkBfZ6IvCdRodkPqHAYIxMRLzL+MhyZt4MEGc
+// Ngb/F6U9/WOBFnoR/PI7IwE3ejutzKcL+QJBAKh+6eilk7QKPETZi1m3/dmNt+p1
+// 3EZJ65pqjwxmB3Rg/vs7vCMk4TarTdSyKu+F1xRPFfoP/mK3Xctdjj6NyhsCQAYF
+// 7/0TOzfkUPMPUJyqFB6xgbDpJ55ScnUUsznoqx+NkTWInDb4t02IqO/UmT2y6FKy
+// Hk8TJ1fTJY+ebqaVp3ECQApx9gQ+n0zIhx97FMUuiRse73xkcW4+pZ8nF+8DmeQL
+// /JKuuFGmzkG+rUbXFmo/Zg2ozVplw71NnQJ4znPsf7A=
+// -----END RSA PRIVATE KEY-----
+
+
// CertFromPEM parses the given, NUL-terminated pem block and returns an
// |X509*|.
static bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
@@ -484,12 +557,10 @@ static bssl::UniquePtr<STACK_OF(X509_CRL)> CRLsToStack(
}
static int Verify(X509 *leaf, const std::vector<X509 *> &roots,
- const std::vector<X509 *> &intermediates,
- const std::vector<X509_CRL *> &crls,
- unsigned long flags,
- bool use_additional_untrusted,
- const char *hostname,
- size_t hostname_len) {
+ const std::vector<X509 *> &intermediates,
+ const std::vector<X509_CRL *> &crls, unsigned long flags,
+ bool use_additional_untrusted,
+ std::function<void(X509_VERIFY_PARAM *)> configure_callback) {
bssl::UniquePtr<STACK_OF(X509)> roots_stack(CertsToStack(roots));
bssl::UniquePtr<STACK_OF(X509)> intermediates_stack(
CertsToStack(intermediates));
@@ -528,7 +599,9 @@ static int Verify(X509 *leaf, const std::vector<X509 *> &roots,
}
X509_VERIFY_PARAM_set_time(param, 1474934400 /* Sep 27th, 2016 */);
X509_VERIFY_PARAM_set_depth(param, 16);
- X509_VERIFY_PARAM_set1_host(param, hostname, hostname_len);
+ if (configure_callback) {
+ configure_callback(param);
+ }
if (flags) {
X509_VERIFY_PARAM_set_flags(param, flags);
}
@@ -547,9 +620,9 @@ static int Verify(X509 *leaf, const std::vector<X509 *> &roots,
const std::vector<X509_CRL *> &crls,
unsigned long flags = 0) {
const int r1 =
- Verify(leaf, roots, intermediates, crls, flags, false, nullptr, 0);
+ Verify(leaf, roots, intermediates, crls, flags, false, nullptr);
const int r2 =
- Verify(leaf, roots, intermediates, crls, flags, true, nullptr, 0);
+ Verify(leaf, roots, intermediates, crls, flags, true, nullptr);
if (r1 != r2) {
fprintf(stderr,
@@ -617,19 +690,152 @@ TEST(X509Test, TestVerify) {
Verify(forgery.get(),
{intermediate_self_signed.get(), root_cross_signed.get()},
{leaf_no_key_usage.get(), intermediate.get()}, empty_crls));
+}
- static const char kHostname[] = "example.com";
- static const char kWrongHostname[] = "example2.com";
- ASSERT_EQ(X509_V_OK,
- Verify(leaf.get(), {root.get()}, {intermediate.get()}, empty_crls,
- 0, false, kHostname, strlen(kHostname)));
- // The wrong hostname should trigger a hostname error.
- ASSERT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
- Verify(leaf.get(), {root.get()}, {intermediate.get()}, empty_crls,
- 0, false, kWrongHostname, strlen(kWrongHostname)));
- // Passing zero, for this API, is supported for compatibility with OpenSSL.
- ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()},
- empty_crls, 0, false, kHostname, 0));
+static const char kHostname[] = "example.com";
+static const char kWrongHostname[] = "example2.com";
+static const char kEmail[] = "test@example.com";
+static const char kWrongEmail[] = "test2@example.com";
+static const uint8_t kIP[4] = {127, 0, 0, 1};
+static const uint8_t kWrongIP[4] = {127, 0, 0, 2};
+static const char kIPString[] = "127.0.0.1";
+static const char kWrongIPString[] = "127.0.0.2";
+
+TEST(X509Test, ZeroLengthsWithX509PARAM) {
+ bssl::UniquePtr<X509> leaf(CertFromPEM(kSANTypesLeaf));
+ bssl::UniquePtr<X509> root(CertFromPEM(kSANTypesRoot));
+ ASSERT_TRUE(leaf);
+ ASSERT_TRUE(root);
+
+ std::vector<X509_CRL *> empty_crls;
+
+ struct Test {
+ const char *correct_value;
+ size_t correct_value_len;
+ const char *incorrect_value;
+ size_t incorrect_value_len;
+ int (*func)(X509_VERIFY_PARAM *, const char *, size_t);
+ int mismatch_error;
+ };
+ const std::vector<Test> kTests = {
+ {kHostname, strlen(kHostname), kWrongHostname, strlen(kWrongHostname),
+ X509_VERIFY_PARAM_set1_host, X509_V_ERR_HOSTNAME_MISMATCH},
+ {kEmail, strlen(kEmail), kWrongEmail, strlen(kWrongEmail),
+ X509_VERIFY_PARAM_set1_email, X509_V_ERR_EMAIL_MISMATCH},
+ };
+
+ for (size_t i = 0; i < kTests.size(); i++) {
+ SCOPED_TRACE(i);
+ const Test &test = kTests[i];
+
+ // The correct value should work.
+ ASSERT_EQ(X509_V_OK,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [&test](X509_VERIFY_PARAM *param) {
+ ASSERT_TRUE(test.func(param, test.correct_value,
+ test.correct_value_len));
+ }));
+
+ // The wrong value should trigger a verification error.
+ ASSERT_EQ(test.mismatch_error,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [&test](X509_VERIFY_PARAM *param) {
+ ASSERT_TRUE(test.func(param, test.incorrect_value,
+ test.incorrect_value_len));
+ }));
+
+ // Passing zero as the length, unlike OpenSSL, should trigger an error and
+ // should cause verification to fail.
+ ASSERT_EQ(X509_V_ERR_INVALID_CALL,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [&test](X509_VERIFY_PARAM *param) {
+ ASSERT_FALSE(test.func(param, test.correct_value, 0));
+ }));
+
+ // Passing an empty value should be an error when setting and should cause
+ // verification to fail.
+ ASSERT_EQ(X509_V_ERR_INVALID_CALL,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [&test](X509_VERIFY_PARAM *param) {
+ ASSERT_FALSE(test.func(param, nullptr, 0));
+ }));
+
+ // Passing a value with embedded NULs should also be an error and should
+ // also cause verification to fail.
+ ASSERT_EQ(X509_V_ERR_INVALID_CALL,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [&test](X509_VERIFY_PARAM *param) {
+ ASSERT_FALSE(test.func(param, "a", 2));
+ }));
+ }
+
+ // IP addresses work slightly differently:
+
+ // The correct value should still work.
+ ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0,
+ false, [](X509_VERIFY_PARAM *param) {
+ ASSERT_TRUE(X509_VERIFY_PARAM_set1_ip(
+ param, kIP, sizeof(kIP)));
+ }));
+
+ // Incorrect values should still fail.
+ ASSERT_EQ(X509_V_ERR_IP_ADDRESS_MISMATCH,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [](X509_VERIFY_PARAM *param) {
+ ASSERT_TRUE(X509_VERIFY_PARAM_set1_ip(param, kWrongIP,
+ sizeof(kWrongIP)));
+ }));
+
+ // Zero length values should trigger an error when setting and cause
+ // verification to always fail.
+ ASSERT_EQ(X509_V_ERR_INVALID_CALL,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [](X509_VERIFY_PARAM *param) {
+ ASSERT_FALSE(X509_VERIFY_PARAM_set1_ip(param, kIP, 0));
+ }));
+
+ // ... and so should NULL values.
+ ASSERT_EQ(X509_V_ERR_INVALID_CALL,
+ Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, false,
+ [](X509_VERIFY_PARAM *param) {
+ ASSERT_FALSE(X509_VERIFY_PARAM_set1_ip(param, nullptr, 0));
+ }));
+
+ // Zero bytes in an IP address are, of course, fine. This is tested above
+ // because |kIP| contains zeros.
+}
+
+TEST(X509Test, ZeroLengthsWithCheckFunctions) {
+ bssl::UniquePtr<X509> leaf(CertFromPEM(kSANTypesLeaf));
+
+ EXPECT_EQ(
+ 1, X509_check_host(leaf.get(), kHostname, strlen(kHostname), 0, nullptr));
+ EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname,
+ strlen(kWrongHostname), 0, nullptr));
+
+ EXPECT_EQ(1, X509_check_email(leaf.get(), kEmail, strlen(kEmail), 0));
+ EXPECT_NE(1,
+ X509_check_email(leaf.get(), kWrongEmail, strlen(kWrongEmail), 0));
+
+ EXPECT_EQ(1, X509_check_ip(leaf.get(), kIP, sizeof(kIP), 0));
+ EXPECT_NE(1, X509_check_ip(leaf.get(), kWrongIP, sizeof(kWrongIP), 0));
+
+ EXPECT_EQ(1, X509_check_ip_asc(leaf.get(), kIPString, 0));
+ EXPECT_NE(1, X509_check_ip_asc(leaf.get(), kWrongIPString, 0));
+
+ // OpenSSL supports passing zero as the length for host and email. We do not
+ // and it should always fail.
+ EXPECT_NE(1, X509_check_host(leaf.get(), kHostname, 0, 0, nullptr));
+ EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, 0, 0, nullptr));
+
+ EXPECT_NE(1, X509_check_email(leaf.get(), kEmail, 0, 0));
+ EXPECT_NE(1, X509_check_email(leaf.get(), kWrongEmail, 0, 0));
+
+ EXPECT_NE(1, X509_check_ip(leaf.get(), kIP, 0, 0));
+ EXPECT_NE(1, X509_check_ip(leaf.get(), kWrongIP, 0, 0));
+
+ // Unlike all the other functions, |X509_check_ip_asc| doesn't take a length,
+ // so it cannot be zero.
}
TEST(X509Test, TestCRL) {
diff --git a/src/crypto/x509/x509_vfy.c b/src/crypto/x509/x509_vfy.c
index aff2ee95..2b754f08 100644
--- a/src/crypto/x509/x509_vfy.c
+++ b/src/crypto/x509/x509_vfy.c
@@ -784,6 +784,10 @@ static int check_id(X509_STORE_CTX *ctx)
X509_VERIFY_PARAM *vpm = ctx->param;
X509_VERIFY_PARAM_ID *id = vpm->id;
X509 *x = ctx->cert;
+ if (id->poison) {
+ if (!check_id_error(ctx, X509_V_ERR_INVALID_CALL))
+ return 0;
+ }
if (id->hosts && check_hosts(x, id) <= 0) {
if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
return 0;
diff --git a/src/crypto/x509/x509_vpm.c b/src/crypto/x509/x509_vpm.c
index 0b03361e..43353c6b 100644
--- a/src/crypto/x509/x509_vpm.c
+++ b/src/crypto/x509/x509_vpm.c
@@ -89,12 +89,9 @@ static int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode,
{
char *copy;
- // This is an OpenSSL quirk that BoringSSL typically doesn't support.
- // However, we didn't make this a fatal error at the time, which was a
- // mistake. Because of that, given the risk that someone could assume the
- // OpenSSL semantics from BoringSSL, it's supported in this case.
- if (name != NULL && namelen == 0) {
- namelen = strlen(name);
+ if (name == NULL || namelen == 0) {
+ // Unlike OpenSSL, we reject trying to set or add an empty name.
+ return 0;
}
/*
@@ -108,8 +105,6 @@ static int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode,
string_stack_free(id->hosts);
id->hosts = NULL;
}
- if (name == NULL || namelen == 0)
- return 1;
copy = BUF_strndup(name, namelen);
if (copy == NULL)
@@ -170,7 +165,7 @@ static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
paramid->ip = NULL;
paramid->iplen = 0;
}
-
+ paramid->poison = 0;
}
X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void)
@@ -324,6 +319,8 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
return 0;
}
+ dest->id->poison = src->id->poison;
+
return 1;
}
@@ -342,18 +339,17 @@ static int int_x509_param_set1(char **pdest, size_t *pdestlen,
const char *src, size_t srclen)
{
void *tmp;
- if (src) {
- if (srclen == 0) {
- tmp = BUF_strdup(src);
- srclen = strlen(src);
- } else
- tmp = BUF_memdup(src, srclen);
- if (!tmp)
- return 0;
- } else {
- tmp = NULL;
- srclen = 0;
+ if (src == NULL || srclen == 0) {
+ // Unlike OpenSSL, we do not allow an empty string to disable previously
+ // configured checks.
+ return 0;
+ }
+
+ tmp = BUF_memdup(src, srclen);
+ if (!tmp) {
+ return 0;
}
+
if (*pdest)
OPENSSL_free(*pdest);
*pdest = tmp;
@@ -462,13 +458,21 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param,
int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
const char *name, size_t namelen)
{
- return int_x509_param_set_hosts(param->id, SET_HOST, name, namelen);
+ if (!int_x509_param_set_hosts(param->id, SET_HOST, name, namelen)) {
+ param->id->poison = 1;
+ return 0;
+ }
+ return 1;
}
int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
const char *name, size_t namelen)
{
- return int_x509_param_set_hosts(param->id, ADD_HOST, name, namelen);
+ if (!int_x509_param_set_hosts(param->id, ADD_HOST, name, namelen)) {
+ param->id->poison = 1;
+ return 0;
+ }
+ return 1;
}
void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
@@ -485,17 +489,27 @@ char *X509_VERIFY_PARAM_get0_peername(X509_VERIFY_PARAM *param)
int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
const char *email, size_t emaillen)
{
- return int_x509_param_set1(&param->id->email, &param->id->emaillen,
- email, emaillen);
+ if (OPENSSL_memchr(email, '\0', emaillen) != NULL ||
+ !int_x509_param_set1(&param->id->email, &param->id->emaillen,
+ email, emaillen)) {
+ param->id->poison = 1;
+ return 0;
+ }
+
+ return 1;
}
int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
const unsigned char *ip, size_t iplen)
{
- if (iplen != 0 && iplen != 4 && iplen != 16)
+ if ((iplen != 4 && iplen != 16) ||
+ !int_x509_param_set1((char **)&param->id->ip, &param->id->iplen,
+ (char *)ip, iplen)) {
+ param->id->poison = 1;
return 0;
- return int_x509_param_set1((char **)&param->id->ip, &param->id->iplen,
- (char *)ip, iplen);
+ }
+
+ return 1;
}
int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc)
@@ -520,7 +534,7 @@ const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param)
}
static const X509_VERIFY_PARAM_ID _empty_id =
- { NULL, 0U, NULL, NULL, 0, NULL, 0 };
+ { NULL, 0U, NULL, NULL, 0, NULL, 0, 0 };
#define vpm_empty_id ((X509_VERIFY_PARAM_ID *)&_empty_id)
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index c7ead039..f2e92a77 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -976,5 +976,6 @@ BORINGSSL_MAKE_DELETER(ASN1_TYPE, ASN1_TYPE_free)
#define ASN1_R_WRONG_PUBLIC_KEY_TYPE 189
#define ASN1_R_WRONG_TAG 190
#define ASN1_R_WRONG_TYPE 191
+#define ASN1_R_NESTED_TOO_DEEP 192
#endif
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 0a844ed3..16180221 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -701,12 +701,15 @@ enum bn_primality_result_t {
// Miller-Rabin tests primality for odd integers greater than 3, returning
// |bn_probably_prime| if the number is probably prime,
// |bn_non_prime_power_composite| if the number is a composite that is not the
-// power of a single prime, and |bn_composite| otherwise. If |iterations| is
-// |BN_prime_checks|, then a value that results in a false positive rate lower
-// than the number-field sieve security level of |w| is used. It returns one on
+// power of a single prime, and |bn_composite| otherwise. It returns one on
// success and zero on failure. If |cb| is not NULL, then it is called during
// each iteration of the primality test.
-int BN_enhanced_miller_rabin_primality_test(
+//
+// If |iterations| is |BN_prime_checks|, then a value that results in a false
+// positive rate lower than the number-field sieve security level of |w| is
+// used, provided |w| was generated randomly. |BN_prime_checks| is not suitable
+// for inputs potentially crafted by an adversary.
+OPENSSL_EXPORT int BN_enhanced_miller_rabin_primality_test(
enum bn_primality_result_t *out_result, const BIGNUM *w, int iterations,
BN_CTX *ctx, BN_GENCB *cb);
@@ -718,13 +721,14 @@ int BN_enhanced_miller_rabin_primality_test(
// list of small primes before Miller-Rabin tests. The probability of this
// function returning a false positive is 2^{2*checks}. If |checks| is
// |BN_prime_checks| then a value that results in a false positive rate lower
-// than the number-field sieve security level of |candidate| is used. If |cb| is
-// not NULL then it is called during the checking process. See the comment above
-// |BN_GENCB|.
+// than the number-field sieve security level of |candidate| is used, provided
+// |candidate| was generated randomly. |BN_prime_checks| is not suitable for
+// inputs potentially crafted by an adversary.
//
-// The function returns one on success and zero on error.
+// If |cb| is not NULL then it is called during the checking process. See the
+// comment above |BN_GENCB|.
//
-// (If you are unsure whether you want |do_trial_division|, don't set it.)
+// The function returns one on success and zero on error.
OPENSSL_EXPORT int BN_primality_test(int *is_probably_prime,
const BIGNUM *candidate, int checks,
BN_CTX *ctx, int do_trial_division,
@@ -737,7 +741,10 @@ OPENSSL_EXPORT int BN_primality_test(int *is_probably_prime,
// list of small primes before Miller-Rabin tests. The probability of this
// function returning one when |candidate| is composite is 2^{2*checks}. If
// |checks| is |BN_prime_checks| then a value that results in a false positive
-// rate lower than the number-field sieve security level of |candidate| is used.
+// rate lower than the number-field sieve security level of |candidate| is used,
+// provided |candidate| was generated randomly. |BN_prime_checks| is not
+// suitable for inputs potentially crafted by an adversary.
+//
// If |cb| is not NULL then it is called during the checking process. See the
// comment above |BN_GENCB|.
//
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 7059e7c2..a52fa538 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -751,5 +751,6 @@ BORINGSSL_MAKE_DELETER(RSA, RSA_free)
#define RSA_R_VALUE_MISSING 144
#define RSA_R_WRONG_SIGNATURE_LENGTH 145
#define RSA_R_PUBLIC_KEY_VALIDATION_FAILED 146
+#define RSA_R_D_OUT_OF_RANGE 147
#endif // OPENSSL_HEADER_RSA_H
diff --git a/src/include/openssl/x509v3.h b/src/include/openssl/x509v3.h
index 4a1fe7bf..56cf59c0 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -748,7 +748,7 @@ extern "C++" {
namespace bssl {
-BORINGSSL_MAKE_DELETER(AUTHORITY_INFO_ACCESS, AUTHORITY_INFO_ACCESS_free)
+BORINGSSL_MAKE_DELETER(ACCESS_DESCRIPTION, ACCESS_DESCRIPTION_free)
BORINGSSL_MAKE_DELETER(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
BORINGSSL_MAKE_DELETER(BASIC_CONSTRAINTS, BASIC_CONSTRAINTS_free)
BORINGSSL_MAKE_DELETER(DIST_POINT, DIST_POINT_free)
diff --git a/src/sources.cmake b/src/sources.cmake
index 5152b31b..ee0f9e69 100644
--- a/src/sources.cmake
+++ b/src/sources.cmake
@@ -44,6 +44,7 @@ set(
crypto/evp/scrypt_tests.txt
crypto/fipsmodule/aes/aes_tests.txt
crypto/fipsmodule/bn/bn_tests.txt
+ crypto/fipsmodule/ec/ec_scalar_base_mult_tests.txt
crypto/fipsmodule/ec/p256-x86_64_tests.txt
crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt
crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt
diff --git a/src/tool/speed.cc b/src/tool/speed.cc
index f4b452fc..9c499cb0 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -12,11 +12,13 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <algorithm>
#include <string>
#include <functional>
#include <memory>
#include <vector>
+#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -194,6 +196,59 @@ static bool SpeedRSA(const std::string &key_name, RSA *key,
return true;
}
+static bool SpeedRSAKeyGen(const std::string &selected) {
+ // Don't run this by default because it's so slow.
+ if (selected != "RSAKeyGen") {
+ return true;
+ }
+
+ bssl::UniquePtr<BIGNUM> e(BN_new());
+ if (!BN_set_word(e.get(), 65537)) {
+ return false;
+ }
+
+ const std::vector<int> kSizes = {2048, 3072, 4096};
+ for (int size : kSizes) {
+ const uint64_t start = time_now();
+ unsigned num_calls = 0;
+ unsigned us;
+ std::vector<unsigned> durations;
+
+ for (;;) {
+ bssl::UniquePtr<RSA> rsa(RSA_new());
+
+ const uint64_t iteration_start = time_now();
+ if (!RSA_generate_key_ex(rsa.get(), size, e.get(), nullptr)) {
+ fprintf(stderr, "RSA_generate_key_ex failed.\n");
+ ERR_print_errors_fp(stderr);
+ return false;
+ }
+ const uint64_t iteration_end = time_now();
+
+ num_calls++;
+ durations.push_back(iteration_end - iteration_start);
+
+ us = iteration_end - start;
+ if (us > 30 * 1000000 /* 30 secs */) {
+ break;
+ }
+ }
+
+ std::sort(durations.begin(), durations.end());
+ printf("Did %u RSA %d key-gen operations in %uus (%.1f ops/sec)\n",
+ num_calls, size, us,
+ (static_cast<double>(num_calls) / us) * 1000000);
+ const size_t n = durations.size();
+ assert(n > 0);
+ unsigned median = n & 1 ? durations[n / 2]
+ : (durations[n / 2 - 1] + durations[n / 2]) / 2;
+ printf(" min: %uus, median: %uus, max: %uus\n", durations[0], median,
+ durations[n - 1]);
+ }
+
+ return true;
+}
+
static uint8_t *align(uint8_t *in, unsigned alignment) {
return reinterpret_cast<uint8_t *>(
(reinterpret_cast<uintptr_t>(in) + alignment) &
@@ -387,8 +442,28 @@ static bool SpeedECDHCurve(const std::string &name, int nid,
return true;
}
+ bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new_by_curve_name(nid));
+ if (!peer_key ||
+ !EC_KEY_generate_key(peer_key.get())) {
+ return false;
+ }
+
+ size_t peer_value_len = EC_POINT_point2oct(
+ EC_KEY_get0_group(peer_key.get()), EC_KEY_get0_public_key(peer_key.get()),
+ POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
+ if (peer_value_len == 0) {
+ return false;
+ }
+ std::unique_ptr<uint8_t[]> peer_value(new uint8_t[peer_value_len]);
+ peer_value_len = EC_POINT_point2oct(
+ EC_KEY_get0_group(peer_key.get()), EC_KEY_get0_public_key(peer_key.get()),
+ POINT_CONVERSION_UNCOMPRESSED, peer_value.get(), peer_value_len, nullptr);
+ if (peer_value_len == 0) {
+ return false;
+ }
+
TimeResults results;
- if (!TimeFunction(&results, [nid]() -> bool {
+ if (!TimeFunction(&results, [nid, peer_value_len, &peer_value]() -> bool {
bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
if (!key ||
!EC_KEY_generate_key(key.get())) {
@@ -396,14 +471,16 @@ static bool SpeedECDHCurve(const std::string &name, int nid,
}
const EC_GROUP *const group = EC_KEY_get0_group(key.get());
bssl::UniquePtr<EC_POINT> point(EC_POINT_new(group));
+ bssl::UniquePtr<EC_POINT> peer_point(EC_POINT_new(group));
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
bssl::UniquePtr<BIGNUM> x(BN_new());
bssl::UniquePtr<BIGNUM> y(BN_new());
- if (!point || !ctx || !x || !y ||
- !EC_POINT_mul(group, point.get(), NULL,
- EC_KEY_get0_public_key(key.get()),
+ if (!point || !peer_point || !ctx || !x || !y ||
+ !EC_POINT_oct2point(group, peer_point.get(), peer_value.get(),
+ peer_value_len, ctx.get()) ||
+ !EC_POINT_mul(group, point.get(), NULL, peer_point.get(),
EC_KEY_get0_private_key(key.get()), ctx.get()) ||
!EC_POINT_get_affine_coordinates_GFp(group, point.get(), x.get(),
y.get(), ctx.get())) {
@@ -719,7 +796,8 @@ bool Speed(const std::vector<std::string> &args) {
!SpeedECDSA(selected) ||
!Speed25519(selected) ||
!SpeedSPAKE2(selected) ||
- !SpeedScrypt(selected)) {
+ !SpeedScrypt(selected) ||
+ !SpeedRSAKeyGen(selected)) {
return false;
}
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
index 9930c80d..4a514819 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -27,7 +27,7 @@ deps = {
},
'boringssl/util/bot/android_tools': {
- 'url': Var('chromium_git') + '/android_tools.git' + '@' + '9a70d48fcdd68cd0e7e968f342bd767ee6323bd1',
+ 'url': Var('chromium_git') + '/android_tools.git' + '@' + 'c22a664c39af72dd8f89200220713dcad811300a',
'condition': 'checkout_android',
},
diff --git a/src/util/bot/go/bootstrap.py b/src/util/bot/go/bootstrap.py
index 617637db..ae38ccf2 100755
--- a/src/util/bot/go/bootstrap.py
+++ b/src/util/bot/go/bootstrap.py
@@ -45,7 +45,7 @@ WORKSPACE = os.path.join(ROOT, 'go')
EXE_SFX = '.exe' if sys.platform == 'win32' else ''
# Pinned version of Go toolset to download.
-TOOLSET_VERSION = 'go1.10'
+TOOLSET_VERSION = 'go1.10.1'
# Platform dependent portion of a download URL. See http://golang.org/dl/.
TOOLSET_VARIANTS = {
diff --git a/src/util/bot/update_clang.py b/src/util/bot/update_clang.py
index 5daef343..982031f8 100644
--- a/src/util/bot/update_clang.py
+++ b/src/util/bot/update_clang.py
@@ -19,7 +19,7 @@ import urllib2
# CLANG_REVISION and CLANG_SUB_REVISION determine the build of clang
# to use. These should be synced with tools/clang/scripts/update.py in
# Chromium.
-CLANG_REVISION = '325667'
+CLANG_REVISION = '328575'
CLANG_SUB_REVISION=1
PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)