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
diff --git a/src/LICENSE b/src/LICENSE
index d133584..49c41fa 100644
--- a/src/LICENSE
+++ b/src/LICENSE
@@ -179,3 +179,73 @@
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 7c114dd..1cca36c 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 @@
}
}
}
+
+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 2f5f132..32aba0b 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_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 @@
* 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 @@
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 @@
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 @@
/*
* 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 @@
* 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 @@
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 @@
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 @@
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 @@
}
}
} 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_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 @@
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 @@
}
} 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 @@
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 3cb5866..59a7bb1 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, 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 c304b2c..56cbbe5 100644
--- a/src/crypto/err/asn1.errordata
+++ b/src/crypto/err/asn1.errordata
@@ -58,6 +58,7 @@
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 9d18e04..75d265a 100644
--- a/src/crypto/err/rsa.errordata
+++ b/src/crypto/err/rsa.errordata
@@ -18,6 +18,7 @@
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 ece7867..38a8450 100644
--- a/src/crypto/fipsmodule/bn/add.c
+++ b/src/crypto/fipsmodule/bn/add.c
@@ -100,7 +100,7 @@
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(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 @@
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;
+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;
+ }
- max = bn_minimal_width(a);
- min = bn_minimal_width(b);
- dif = max - min;
+ if (!bn_wexpand(r, a->width)) {
+ return 0;
+ }
- if (dif < 0) // hmm... should not be happening
- {
+ 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 (borrow) {
OPENSSL_PUT_ERROR(BN, BN_R_ARG2_LT_ARG3);
return 0;
}
- if (!bn_wexpand(r, max)) {
+ r->width = a->width;
+ r->neg = 0;
+ return 1;
+}
+
+int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
+ if (!bn_usub_consttime(r, a, b)) {
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;
- }
- }
- }
-
- if (dif > 0 && rp != ap) {
- OPENSSL_memcpy(rp, ap, sizeof(*rp) * dif);
- }
-
- r->width = max;
- r->neg = 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 4e5043f..c020d96 100644
--- a/src/crypto/fipsmodule/bn/bn.c
+++ b/src/crypto/fipsmodule/bn/bn.c
@@ -187,13 +187,19 @@
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 f73054d..93d6d0f 100644
--- a/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/src/crypto/fipsmodule/bn/bn_test.cc
@@ -239,8 +239,7 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
ret_word = BN_mod_word(a.get(), b_word);
EXPECT_EQ(remainder_word, ret_word);
+
+ if (b_word <= 0xffff) {
+ EXPECT_EQ(remainder_word, bn_mod_u16_consttime(a.get(), b_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()));
- }
- 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 @@
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 @@
<< "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 @@
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 @@
&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 @@
&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, CountLowZeroBits) {
- bssl::UniquePtr<BIGNUM> ten(BN_new());
- ASSERT_TRUE(ten);
- ASSERT_TRUE(BN_set_word(ten.get(), 10));
+ bssl::UniquePtr<BIGNUM> bn(BN_new());
+ ASSERT_TRUE(bn);
- bssl::UniquePtr<BIGNUM> eight(BN_new());
- ASSERT_TRUE(eight);
- ASSERT_TRUE(BN_set_word(eight.get(), 8));
+ 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);
- 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));
+ 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()));
- 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));
+ 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()));
- bssl::UniquePtr<BIGNUM> zero(BN_new());
- ASSERT_TRUE(zero);
- BN_zero(zero.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 101a449..7f85a02 100644
--- a/src/crypto/fipsmodule/bn/bn_tests.txt
+++ b/src/crypto/fipsmodule/bn/bn_tests.txt
@@ -7796,6 +7796,91 @@
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 @@
# 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 abb4ca5..bf05e75 100644
--- a/src/crypto/fipsmodule/bn/check_bn_tests.go
+++ b/src/crypto/fipsmodule/bn/check_bn_tests.go
@@ -265,7 +265,7 @@
}
}
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 @@
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 89775c0..692adb5 100644
--- a/src/crypto/fipsmodule/bn/cmp.c
+++ b/src/crypto/fipsmodule/bn/cmp.c
@@ -128,14 +128,14 @@
}
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_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 6f850d9..1950561 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -414,6 +414,35 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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(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 @@
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 850d446..7868b40 100644
--- a/src/crypto/fipsmodule/bn/gcd.c
+++ b/src/crypto/fipsmodule/bn/gcd.c
@@ -114,109 +114,132 @@
#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
- }
-
- if (shifts) {
- if (!BN_lshift(a, a, shifts)) {
- goto err;
- }
- }
-
- return a;
-
-err:
- return NULL;
+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);
}
-int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) {
- BIGNUM *a, *b, *t;
+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;
+ }
+
+ // This is a constant-time implementation of Stein's algorithm (binary GCD).
int ret = 0;
-
BN_CTX_start(ctx);
- a = BN_CTX_get(ctx);
- b = BN_CTX_get(ctx);
-
- if (a == NULL || b == NULL) {
- goto err;
- }
- if (BN_copy(a, in_a) == NULL) {
- goto err;
- }
- if (BN_copy(b, in_b) == NULL) {
+ 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;
}
- a->neg = 0;
- b->neg = 0;
-
- if (BN_cmp(a, b) < 0) {
- t = a;
- a = b;
- b = t;
- }
- 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 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;
}
- if (BN_copy(r, t) == NULL) {
+ 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:
+ BN_CTX_end(ctx);
+ return ret;
+}
+
+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:
@@ -224,10 +247,176 @@
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_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
+ BN_CTX_start(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;
+}
+
+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_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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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 = BN_copy(r, A) != NULL;
+
+err:
+ BN_CTX_end(ctx);
+ return ret;
+}
int BN_mod_inverse_odd(BIGNUM *out, int *out_no_inverse, const BIGNUM *a,
const BIGNUM *n, BN_CTX *ctx) {
@@ -423,7 +612,7 @@
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 @@
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 20945a9..668d8dd 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -285,10 +285,8 @@
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 @@
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 @@
#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 @@
// 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);
-// Fixed-width arithmetic.
+// 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);
+
+// 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 @@
// 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_mul_fixed behaves like |BN_mul|, but it rejects negative inputs and
+// 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_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_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_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_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_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_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_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 c21a030..7ce8c4c 100644
--- a/src/crypto/fipsmodule/bn/montgomery.c
+++ b/src/crypto/fipsmodule/bn/montgomery.c
@@ -289,18 +289,7 @@
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 @@
}
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 15e62e4..a920ca4 100644
--- a/src/crypto/fipsmodule/bn/montgomery_inv.c
+++ b/src/crypto/fipsmodule/bn/montgomery_inv.c
@@ -177,7 +177,7 @@
// 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 352b7e5..4a0711d 100644
--- a/src/crypto/fipsmodule/bn/mul.c
+++ b/src/crypto/fipsmodule/bn/mul.c
@@ -306,6 +306,24 @@
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 @@
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 @@
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 @@
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 @@
}
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 a291f7a..d2dfc2c 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 @@
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,20 +524,285 @@
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;
- return 1;
- default:
- *is_probably_prime = 0;
- return 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;
+ }
+ }
+ return 0;
+}
+
+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_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;
+ }
+
+ if (!BN_is_odd(w)) {
+ // The only even prime is two.
+ *is_probably_prime = BN_is_word(w, 2);
+ return 1;
+ }
+
+ // Miller-Rabin does not work for three.
+ if (BN_is_word(w, 3)) {
+ *is_probably_prime = 1;
+ return 1;
+ }
+
+ if (do_trial_division) {
+ // 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 0;
+ }
+ }
+
+ if (iterations == BN_prime_checks) {
+ iterations = BN_prime_checks_for_size(BN_num_bits(w));
+ }
+
+ BN_CTX *new_ctx = NULL;
+ if (ctx == NULL) {
+ new_ctx = BN_CTX_new();
+ if (new_ctx == NULL) {
+ return 0;
+ }
+ ctx = new_ctx;
+ }
+
+ // 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;
+ }
+
+ // 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_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) {
@@ -483,57 +811,12 @@
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 is_probably_prime;
+ if (!BN_primality_test(&is_probably_prime, a, checks, ctx, do_trial_division,
+ cb)) {
+ 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);
- }
-
- // Enhanced Miller-Rabin does not work for three.
- if (BN_is_word(a, 3)) {
- 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]);
- }
- }
-
- if (!BN_GENCB_call(cb, 1, -1)) {
- return -1;
- }
- }
-
- int ret = -1;
- BN_CTX *ctx_allocated = NULL;
- if (ctx == NULL) {
- ctx_allocated = BN_CTX_new();
- if (ctx_allocated == NULL) {
- return -1;
- }
- ctx = ctx_allocated;
- }
-
- enum bn_primality_result_t result;
- if (!BN_enhanced_miller_rabin_primality_test(&result, a, checks, ctx, cb)) {
- goto err;
- }
-
- ret = (result == bn_probably_prime);
-
-err:
- BN_CTX_free(ctx_allocated);
- return ret;
+ return is_probably_prime;
}
int BN_enhanced_miller_rabin_primality_test(
@@ -585,7 +868,7 @@
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 @@
// 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 @@
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 @@
// 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 c6f9f08..e41a0ef 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 @@
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 (bottom == BN_RAND_BOTTOM_ODD) {
+ rnd->d[0] |= 1;
}
- if (!BN_bin2bn(buf, bytes, rnd)) {
- goto err;
- }
-
- 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 @@
// |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 @@
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 @@
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 d8dfe5f..ccf7141 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 @@
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;
+ if (!bn_wexpand(r, a->width)) {
+ return 0;
}
- 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
- }
- }
-
- 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;
- }
- }
-
- if (r->width == 0) {
- r->neg = 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;
}
+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;
+ }
+
+ // 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);
+ }
+
+ ret = 1;
+
+err:
+ BN_CTX_end(ctx);
+ return ret;
+}
+
+void bn_rshift1_words(BN_ULONG *r, const BN_ULONG *a, size_t num) {
+ if (num == 0) {
+ return;
+ }
+ for (size_t i = 0; i < num - 1; i++) {
+ r[i] = (a[i] >> 1) | (a[i + 1] << (BN_BITS2 - 1));
+ }
+ r[num - 1] = a[num - 1] >> 1;
+}
+
int BN_rshift1(BIGNUM *r, const BIGNUM *a) {
- BN_ULONG *ap, *rp, t, c;
- int i, j;
-
- if (BN_is_zero(a)) {
- BN_zero(r);
- return 1;
+ if (!bn_wexpand(r, a->width)) {
+ return 0;
}
- 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;
- }
- 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);
- }
- r->width = j;
-
- if (r->width == 0) {
- r->neg = 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 @@
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 852512c..23417d1 100644
--- a/src/crypto/fipsmodule/bn/sqrt.c
+++ b/src/crypto/fipsmodule/bn/sqrt.c
@@ -184,7 +184,7 @@
// 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 0000000..d48a021
--- /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 0ac2c5b..d2cd5f5 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 @@
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 @@
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 @@
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 c198cc2..742e94e 100644
--- a/src/crypto/fipsmodule/ec/internal.h
+++ b/src/crypto/fipsmodule/ec/internal.h
@@ -249,10 +249,6 @@
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 0000000..716da55
--- /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 38a3342..19e17a7 100644
--- a/src/crypto/fipsmodule/ec/oct.c
+++ b/src/crypto/fipsmodule/ec/oct.c
@@ -316,20 +316,20 @@
// 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 71a8af0..7e2f45b 100644
--- a/src/crypto/fipsmodule/ec/p224-64.c
+++ b/src/crypto/fipsmodule/ec/p224-64.c
@@ -257,23 +257,6 @@
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 @@
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 @@
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 @@
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 57a9099..e87409c 100644
--- a/src/crypto/fipsmodule/ec/simple.c
+++ b/src/crypto/fipsmodule/ec/simple.c
@@ -395,8 +395,8 @@
}
// 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 @@
}
// '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 @@
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 @@
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 @@
// 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 @@
} 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 @@
// 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 @@
// 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 @@
// 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 @@
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 1d08123..85490fa 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 @@
// 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 @@
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 @@
// |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 @@
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 @@
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 513c582..703b3eb 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt
@@ -2164,3 +2164,112 @@
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 a1b4cb2..aa2fbd3 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt
@@ -2344,3 +2344,90 @@
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 0f0c763..f913058 100644
--- a/src/crypto/fipsmodule/rsa/internal.h
+++ b/src/crypto/fipsmodule/rsa/internal.h
@@ -114,15 +114,10 @@
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 6477a26..aed87a6 100644
--- a/src/crypto/fipsmodule/rsa/rsa.c
+++ b/src/crypto/fipsmodule/rsa/rsa.c
@@ -634,8 +634,25 @@
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 @@
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 @@
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 @@
}
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 @@
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 625f101..49cbc15 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -862,7 +862,7 @@
!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 @@
// 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 fdd5e49..a6bfb87 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -826,6 +826,21 @@
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 @@
// 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 9c55def..53b4a0d 100644
--- a/src/crypto/x509/vpm_int.h
+++ b/src/crypto/x509/vpm_int.h
@@ -67,4 +67,5 @@
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 ed4978a..0c5fc2d 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 @@
"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 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 @@
}
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 @@
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 @@
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 aff2ee9..2b754f0 100644
--- a/src/crypto/x509/x509_vfy.c
+++ b/src/crypto/x509/x509_vfy.c
@@ -784,6 +784,10 @@
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 0b03361..43353c6 100644
--- a/src/crypto/x509/x509_vpm.c
+++ b/src/crypto/x509/x509_vpm.c
@@ -89,12 +89,9 @@
{
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 @@
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 @@
paramid->ip = NULL;
paramid->iplen = 0;
}
-
+ paramid->poison = 0;
}
X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void)
@@ -324,6 +319,8 @@
return 0;
}
+ dest->id->poison = src->id->poison;
+
return 1;
}
@@ -342,18 +339,17 @@
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_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 @@
int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
const char *email, size_t emaillen)
{
- return int_x509_param_set1(¶m->id->email, ¶m->id->emaillen,
- email, emaillen);
+ if (OPENSSL_memchr(email, '\0', emaillen) != NULL ||
+ !int_x509_param_set1(¶m->id->email, ¶m->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 **)¶m->id->ip, ¶m->id->iplen,
+ (char *)ip, iplen)) {
+ param->id->poison = 1;
return 0;
- return int_x509_param_set1((char **)¶m->id->ip, ¶m->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 @@
}
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 c7ead03..f2e92a7 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -976,5 +976,6 @@
#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 0a844ed..1618022 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -701,12 +701,15 @@
// 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 @@
// 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.
+//
+// If |cb| is not NULL then it is called during the checking process. See the
+// comment above |BN_GENCB|.
//
// The function returns one on success and zero on error.
-//
-// (If you are unsure whether you want |do_trial_division|, don't set it.)
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 @@
// 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 7059e7c..a52fa53 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -751,5 +751,6 @@
#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 4a1fe7b..56cf59c 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -748,7 +748,7 @@
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 5152b31..ee0f9e6 100644
--- a/src/sources.cmake
+++ b/src/sources.cmake
@@ -44,6 +44,7 @@
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 f4b452f..9c499cb 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 @@
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 @@
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 @@
}
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 @@
!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 9930c80..4a51481 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -27,7 +27,7 @@
},
'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 617637d..ae38ccf 100755
--- a/src/util/bot/go/bootstrap.py
+++ b/src/util/bot/go/bootstrap.py
@@ -45,7 +45,7 @@
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 5daef34..982031f 100644
--- a/src/util/bot/update_clang.py
+++ b/src/util/bot/update_clang.py
@@ -19,7 +19,7 @@
# 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)