From 862830309868c8ae669f9e014bc95c8954762f7b Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 23 Jul 2024 21:03:48 -0400 Subject: [PATCH 1/2] Add NUMS curve numsp512d1 The NUMS curves are a set of curves generated using a simple and straighforward set of criteria, while also admitting very efficient implementation. Further details can be found in the IETF draft draft-black-numscurves-02 --- src/build-data/oids.txt | 4 + src/lib/asn1/oid_maps.cpp | 6 + src/lib/math/pcurves/pcurves.cpp | 14 + src/lib/math/pcurves/pcurves_id.h | 1 + src/lib/math/pcurves/pcurves_instance.h | 2 + .../math/pcurves/pcurves_numsp512d1/info.txt | 12 + .../pcurves_numsp512d1/pcurves_numsp512d1.cpp | 105 +++++++ src/lib/pubkey/ec_group/ec_named.cpp | 17 ++ src/tests/data/pubkey/ecc_base_point_mul.vec | 285 +++++++++++++++++- src/tests/test_ecdh.cpp | 40 +++ src/tests/test_ecdsa.cpp | 46 ++- src/tests/test_pcurves.cpp | 17 +- 12 files changed, 543 insertions(+), 6 deletions(-) create mode 100644 src/lib/math/pcurves/pcurves_numsp512d1/info.txt create mode 100644 src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt index daffa7e289d..37513b91410 100644 --- a/src/build-data/oids.txt +++ b/src/build-data/oids.txt @@ -390,3 +390,7 @@ 1.2.643.7.1.2.1.2.2 = gost_512B 1.2.643.2.2.35.1 = gost_256A 1.2.643.2.2.36.0 = gost_256A + +1.3.6.1.4.1.25258.4.1 = numsp256d1 +1.3.6.1.4.1.25258.4.2 = numsp384d1 +1.3.6.1.4.1.25258.4.3 = numsp512d1 diff --git a/src/lib/asn1/oid_maps.cpp b/src/lib/asn1/oid_maps.cpp index 5f2049ba8f8..95fa332ce9c 100644 --- a/src/lib/asn1/oid_maps.cpp +++ b/src/lib/asn1/oid_maps.cpp @@ -211,6 +211,9 @@ std::unordered_map OID_Map::load_oid2str_map() { {"1.3.6.1.4.1.25258.3.4.7", "Camellia-192/SIV"}, {"1.3.6.1.4.1.25258.3.4.8", "Camellia-256/SIV"}, {"1.3.6.1.4.1.25258.3.4.9", "SM4/SIV"}, + {"1.3.6.1.4.1.25258.4.1", "numsp256d1"}, + {"1.3.6.1.4.1.25258.4.2", "numsp384d1"}, + {"1.3.6.1.4.1.25258.4.3", "numsp512d1"}, {"1.3.6.1.4.1.3029.1.2.1", "ElGamal"}, {"1.3.6.1.4.1.3029.1.5.1", "OpenPGP.Curve25519"}, {"1.3.6.1.4.1.311.20.2.2", "Microsoft SmartcardLogon"}, @@ -582,6 +585,9 @@ std::unordered_map OID_Map::load_str2oid_map() { {"gost_256B", OID({1, 2, 643, 7, 1, 2, 1, 1, 2})}, {"gost_512A", OID({1, 2, 643, 7, 1, 2, 1, 2, 1})}, {"gost_512B", OID({1, 2, 643, 7, 1, 2, 1, 2, 2})}, + {"numsp256d1", OID({1, 3, 6, 1, 4, 1, 25258, 4, 1})}, + {"numsp384d1", OID({1, 3, 6, 1, 4, 1, 25258, 4, 2})}, + {"numsp512d1", OID({1, 3, 6, 1, 4, 1, 25258, 4, 3})}, {"secp160k1", OID({1, 3, 132, 0, 9})}, {"secp160r1", OID({1, 3, 132, 0, 8})}, {"secp160r2", OID({1, 3, 132, 0, 30})}, diff --git a/src/lib/math/pcurves/pcurves.cpp b/src/lib/math/pcurves/pcurves.cpp index 9db4e189177..3e64e1f5e4d 100644 --- a/src/lib/math/pcurves/pcurves.cpp +++ b/src/lib/math/pcurves/pcurves.cpp @@ -84,6 +84,13 @@ std::shared_ptr PCurveInstance::sm2p256v1() { } #endif +#if !defined(BOTAN_HAS_PCURVES_NUMSP512D1) +//static +std::shared_ptr PCurveInstance::numsp512d1() { + return nullptr; +} +#endif + std::shared_ptr PrimeOrderCurve::from_id(PrimeOrderCurveId id) { switch(id.code()) { case PrimeOrderCurveId::secp192r1: @@ -106,6 +113,8 @@ std::shared_ptr PrimeOrderCurve::from_id(PrimeOrderCurveI return PCurveInstance::frp256v1(); case PrimeOrderCurveId::sm2p256v1: return PCurveInstance::sm2p256v1(); + case PrimeOrderCurveId::numsp512d1: + return PCurveInstance::numsp512d1(); } return {}; } @@ -122,6 +131,7 @@ std::vector PrimeOrderCurveId::all() { PrimeOrderCurveId::brainpool512r1, PrimeOrderCurveId::frp256v1, PrimeOrderCurveId::sm2p256v1, + PrimeOrderCurveId::numsp512d1, }; } @@ -147,6 +157,8 @@ std::string PrimeOrderCurveId::to_string() const { return "frp256v1"; case PrimeOrderCurveId::sm2p256v1: return "sm2p256v1"; + case PrimeOrderCurveId::numsp512d1: + return "numsp512d1"; } return "unknown"; @@ -174,6 +186,8 @@ std::optional PrimeOrderCurveId::from_string(std::string_view return PCurve::PrimeOrderCurveId::frp256v1; } else if(name == "sm2p256v1") { return PCurve::PrimeOrderCurveId::sm2p256v1; + } else if(name == "numsp512d1") { + return PCurve::PrimeOrderCurveId::numsp512d1; } else { return {}; } diff --git a/src/lib/math/pcurves/pcurves_id.h b/src/lib/math/pcurves/pcurves_id.h index 7192b4f7614..c49a82f35e8 100644 --- a/src/lib/math/pcurves/pcurves_id.h +++ b/src/lib/math/pcurves/pcurves_id.h @@ -44,6 +44,7 @@ class BOTAN_TEST_API PrimeOrderCurveId final { brainpool512r1, frp256v1, sm2p256v1, + numsp512d1, }; using enum Code; diff --git a/src/lib/math/pcurves/pcurves_instance.h b/src/lib/math/pcurves/pcurves_instance.h index 1ac05841a13..a7a37580c3b 100644 --- a/src/lib/math/pcurves/pcurves_instance.h +++ b/src/lib/math/pcurves/pcurves_instance.h @@ -40,6 +40,8 @@ class PCurveInstance final { static std::shared_ptr frp256v1(); static std::shared_ptr sm2p256v1(); + + static std::shared_ptr numsp512d1(); }; } // namespace Botan::PCurve diff --git a/src/lib/math/pcurves/pcurves_numsp512d1/info.txt b/src/lib/math/pcurves/pcurves_numsp512d1/info.txt new file mode 100644 index 00000000000..16a9b4a4d49 --- /dev/null +++ b/src/lib/math/pcurves/pcurves_numsp512d1/info.txt @@ -0,0 +1,12 @@ + +PCURVES_NUMSP512D1 -> 20240723 + + + +name -> "PCurve numsp512d1" +brief -> "numsp512d1" + + + +pcurves_impl + diff --git a/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp b/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp new file mode 100644 index 00000000000..a5b23f00436 --- /dev/null +++ b/src/lib/math/pcurves/pcurves_numsp512d1/pcurves_numsp512d1.cpp @@ -0,0 +1,105 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::PCurve { + +namespace { + +namespace numsp512d1 { + +template +class Numsp512d1Rep final { + public: + static constexpr auto P = Params::P; + static constexpr size_t N = Params::N; + typedef typename Params::W W; + + static constexpr W C = 569; + + constexpr static std::array one() { return std::array{1}; } + + constexpr static std::array redc(const std::array& z) { + return redc_crandall(std::span{z}); + } + + constexpr static std::array to_rep(const std::array& x) { return x; } + + constexpr static std::array wide_to_rep(const std::array& x) { return redc(x); } + + constexpr static std::array from_rep(const std::array& z) { return z; } +}; + +// clang-format off +class Params final : public EllipticCurveParameters< + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", + "1D99B", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555D", + "2", + "1C282EB23327F9711952C250EA61AD53FCC13031CF6DD336E0B9328433AFBDD8CC5A1C1F0C716FDC724DDE537C2B0ADB00BB3D08DC83755B205CC30D7F83CF28"> { +}; + +// clang-format on + +class Curve final : public EllipticCurve { + public: + static FieldElement fe_invert2(const FieldElement& x) { + // Generated by https://github.com/mmcloughlin/addchain + auto z = x.square(); + z *= x; + z = z.square(); + z *= x; + auto t0 = z; + t0.square_n(3); + t0 *= z; + t0.square_n(3); + auto t1 = t0 * z; + t0 = t1; + t0.square_n(9); + t0 *= t1; + t0.square_n(3); + t0 *= z; + auto t2 = t0; + t2.square_n(9); + t1 *= t2; + t2 = t1; + t2.square_n(30); + t1 *= t2; + t2 = t1; + t2.square_n(60); + t1 *= t2; + t2 = t1; + t2.square_n(120); + t1 *= t2; + t2 = t1; + t2.square_n(240); + t1 *= t2; + t1.square_n(21); + t0 *= t1; + t0 = t0.square(); + t0 *= x; + t0.square_n(4); + z *= t0; + z.square_n(4); + z *= x; + z.square_n(2); + return z; + } +}; + +} // namespace numsp512d1 + +} // namespace + +std::shared_ptr PCurveInstance::numsp512d1() { + return PrimeOrderCurveImpl::instance(); +} + +} // namespace Botan::PCurve diff --git a/src/lib/pubkey/ec_group/ec_named.cpp b/src/lib/pubkey/ec_group/ec_named.cpp index 6cda5a28124..d255f1e7f98 100644 --- a/src/lib/pubkey/ec_group/ec_named.cpp +++ b/src/lib/pubkey/ec_group/ec_named.cpp @@ -337,6 +337,18 @@ std::shared_ptr EC_Group::EC_group_info(const OID& oid) { oid); } + // numsp512d1 + if(oid == OID{1, 3, 6, 1, 4, 1, 25258, 4, 3}) { + return load_EC_group_info( + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", + "0x1D99B", + "0x2", + "0x1C282EB23327F9711952C250EA61AD53FCC13031CF6DD336E0B9328433AFBDD8CC5A1C1F0C716FDC724DDE537C2B0ADB00BB3D08DC83755B205CC30D7F83CF28", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555D", + oid); + } + return std::shared_ptr(); } @@ -453,6 +465,10 @@ OID EC_Group::EC_group_identity_from_order(const BigInt& order) return OID{1, 2, 840, 10045, 3, 1, 6}; } + if(low_bits == 0x0433555D && order == BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555D")) { + return OID{1, 3, 6, 1, 4, 1, 25258, 4, 3}; + } + return OID(); } @@ -469,6 +485,7 @@ const std::set& EC_Group::known_named_groups() { "frp256v1", "gost_256A", "gost_512A", + "numsp512d1", "secp160k1", "secp160r1", "secp160r2", diff --git a/src/tests/data/pubkey/ecc_base_point_mul.vec b/src/tests/data/pubkey/ecc_base_point_mul.vec index 7bbe2b3462c..81cd5527392 100644 --- a/src/tests/data/pubkey/ecc_base_point_mul.vec +++ b/src/tests/data/pubkey/ecc_base_point_mul.vec @@ -187,7 +187,7 @@ P = 0414890E61FCD4B0BD92E5B36C81372CA6FED471EF3AA60A3E415EE4FE987DABA1297B858D9F k = 1B22644A7BE026548810C378D0B2994EEFA6D2B9881803CB02CEFF865287D1B9 P = 04F73C65EAD01C5126F28F442D087689BFA08E12763E0CEC1D35B01751FD735ED3F449A8376906482A84ED01479BD18882B919C140D638307F0C0934BA12590BDE -# Following tests are from http://point-at-infinity.org/ecc/nisttv +# NIST P curves tests are from http://point-at-infinity.org/ecc/nisttv [secp192r1] @@ -1330,3 +1330,286 @@ P = 0408DAAE840B2A43EBD6B48259B69E853027D053C0F150FE10AC995C5066BDAAA57F33F566FB k = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54103 P = 0425D3DEBD0950D180A6D5C2B5817F2329791734CD03E5565CA32641E56024666C6D26658E98629E1046C73BF922A34F1EFBA776ADDF1DF74C2C61EFFCFC05EF5D + +[numsp512d1] + +# Generated using OpenSSL + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +P = 04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021C282EB23327F9711952C250EA61AD53FCC13031CF6DD336E0B9328433AFBDD8CC5A1C1F0C716FDC724DDE537C2B0ADB00BB3D08DC83755B205CC30D7F83CF28 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 +P = 045CE4024147F6BA0334847CE2FECD85A8E0D77788C3735A85F1D9661D6DD7C08152193F9A3F003244D40BE213AC9D925407D444A3662DA513E55EE4D50F55D528E2C3A45404E4165D3C25C5CF4C0595B861EDC6E4645E4B9DB1D834DEC5094B1ACFF86094D051378296E376FCA97FF2B8AAA474C02D14602FB9E2FEF47D14E47C + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003 +P = 0411067A32D4B933BAF041F9138C1C71BE1B0DBE5C1C6A9310D4C02225AC629DD5F6FB22578F877F9EEA9E9A2061FC626559E2FCFDDDFC0AE0A51C9E09607DBFC56CA721E1033ACF3AD66EFD5127BCF77F82B9A77150A5D5EF6BE3AEE61F246B292494E8B30F91BB6FC3F54C2CB08BAEDD7E5F05B411E41531FB1515A4A84E977B + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +P = 0435639D2D4177A7FF7B4C527BBC4F2F47E2270B5488CFF05C7197CB41C8A195346AF767D3F1322996AD76C128F18D2E2D6AECC36396F6A5571867E19C962C150F705AF9FE48FF7BD237E4DB1F25ABED937333F8E0A807684E8C8A1C113B33E275D7B974ED77AE79F45E16B02A3FC047E83EDEEB114E7E857330BE665BF4D2F304 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005 +P = 041598B3CD5CD0E245A8BA7F70E63F9D3691B904D213B1E7172C7CDA7D2C7AAB913500F3CDAC78AA9F9BA081168056887016BA87EE1317E4D842542F346AE829F023BEAE3B57A18DF1E105BCA9C6EB3BC960EAE9D000333FA9C9BE1D8DF880B6BC7734C3EDD5DA00AEABB70C3AC436CBE476B86BF8349BF5A7773B9E615E80ED8E + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 +P = 040AE6B086ECFADA271913F0E20F39D8631D9F945900CAEC5494CBAFAB1F49002D4E3EC81868B9C9315591E966E230D2E10DD1494A641B23FCC473F67A35308FF957895C1980E47392B759B8B8FCD8D22CACDEF52931CABA0A9FFC0D6C3749C7E0E5675ED3F1F0F567B74306C38527014DC6F6AABC6BCC89098D50B66846C43D02 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007 +P = 0434EEE0304574716DA0DBF629D7299CDDEFC215006F988095750D98B04509260C231E8B7BD6B8E760ABE8AA314148595D1A2CDC7D4A23133361DC65CDEB4510F817998DEAD15B901824B832D32C2A3E80C6914A6F7C4A941D1AF1728C796D1351EA24B6660063D040929FCD9BAD049BF561C31646F33918D7A1227DDFC4B95B69 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008 +P = 043AC03447141D0A93DA2B7002A03D3B5298CAD83BB501F6854506E0C25306D9F95021A151076B359E93794286255615831D5D60137D6F5DE2DC8287958CABAE57943A54CA29AD56B3CE0EEEDC63EBB1004B97DBDEABBCBB8C8F4B260C7BD14F14A28415DA8B0EEDE9C121A840B25A5602CF2B5C1E4CFD0FE923A08760383527A6 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009 +P = 04F6FB2B8E0E009089DFFF172BB2A336FC7BC2B21D5054673D9DFEE8A7A1B231FBB7E63786183923B2E2869C195603636DBA418214292F1F379F60FE70F449BC7388A172886DDB03B398B86114E119D4099B910F3806093F96053703558B9FC4C04D6C969B9D78FD7B59038AF5F1082E1C2809B182C57A836B09B03D8FDDC70C7C + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A +P = 04D671493BADCAD3EF190A2FE6DB714C09C8FE16995838803F4D30302B929DAAA246BB9D96FC124B771B7E1B9E5346C40A02CED54CA21FC569704C1EB59DF23977761EC2DBFACCE78BD40C61FE01C271514B97A674AAFA9FDB74269EBA70E0BC2E1A2A398811D4AC908E6A819D7FDC2F5FA226123538FDA17EE606D75F61FAE49E + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B +P = 048496FE2D96926A64227B5EACA01652A8DFEFD1971B16D42CDA4953B901951E911094A28B3CDA63AB7100A13986710D2D9A0C9C277406D2F18CCB3B9CF99F13E4CA52F9993F2FD97972449B58FD1088D4686FBE054F72CC3D7D1D356DA6334BF1EDC77E7BB918037235E210B874EDAE009D4E6962C2F127CED95F81ABCF1E7B3B + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C +P = 04D63C9ED8C6C27A7DDF753BE807BFD4B6CA4CA740E77C98F8347296F3BB4064A071A9EEE78A6E9487ED160B45E1B4F8FECA36E4B3532094BDA7E15706F1C8E032B7D2697B4CB1517654F27EA465378B4F1B5F58B22BAA2F75CEADE52CE1354D81998FA42C8FE0C9E246AC0E68E278A444A927E286E5A74DC96179DDDD4A34C83C + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000D +P = 04FE4F433167E3CBE103BA2BBDB965414D123AA9C44BD25F43D10E9730E1F710A82EB49B684C12AFD8BAA89A4E49B4E506FB79D4FC492A2E0F6BAB62576056DD7302BFD76FD561C935563965AB6B97AA6DA24EE08E2C5AE1369682BE43D24052DF5844C7877086AB3B716E1B39AC04DE213945546E44E3C81DCA80B12978B40E96 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E +P = 04583A7CFD201FE24CE603BB163583403BF694B5ED4223F7975FC65058B40665A31DADDCB2D21549C2AB1130307455F7F34503910C760FF8E622468C6F894F90D25023C475DDDA36FE680865A52B7F6AAF5BE72579BC4AA2682AD3CCE4F1A7B7669E809BD98E3DF9C416B879461FD28BEDD86F1491707E1055437E7A2155318515 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F +P = 04EE3331033B0AFD682D893A188F134EDE7C0C66D7BB9BE61F56177F5A2D719E88B75CCCF796FD92C94FECDD07C0EB16973364B979914F2FC44F7D6699A89ABEF411B8597C76A900D1623CB48306393278AADFCB763C77337596A2411B57E3EC61399C0F47ACAEB8C130C007C2775C5516483E7FA8A92A28CAA9CE727AD7BFFA9D + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010 +P = 04FE02D8717B66E11D6AF798993613DE5806FEC277496635817694DE5D4515519CEBDD8DC83A3AACEFD78AD2C53306962DFA2EFC881C0527E53386A46207BF80695B6C6768636057AFD00FA6C5075DEC713D5B3E1E13720CEE59D405F6D33F0ADAB62C5ED7A8D1F5A4EE0585B06A44BE9A771CF8475ECB3C91B384185ED086AB42 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011 +P = 049E5F9B2D04688421E1A105B66585AF9FA2440378CD5A1E10525FE6EF54255269B19407293BBFC9D788E77A9E9215D263567EDFA84C88134FE2293F20A185762D4BDBE07D907E9AA2379B86F841E3752B34583FF0BFD86561012CE8C90FF2C1FA68AA0ADF12ABC68C5B06658EE3D2FB7D2FB30D4023CE776FF3A8048C9684B3F2 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012 +P = 04AF7D38C362D1756DCA11DE8BC74F1669AE55A5B4AB1A651C0E78868767D2065D0F15780AD8DCC41DE8E56B2EEB4AC97260E0485CA1697A67CC1A33C4CC7E08D16D6B30897C3B519B25249060363DF3ACFEC479D445B5281B142989CA89236DCD4163B85BC707734E300709E2071FAB147E7F6F6604BE389410726E386154BC66 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013 +P = 04F4A7733DAFD2A3FE2CF2333964D18ED2C7456C8A249F01CB77F030AFF87F0523F9D6C512820DEDE259194283CB9BD394535E80506AF185AA89D97EE14A8DC0B922FD745697DDE09AE13832CA04BBD53E93434A815237A12BD286D79C38A0470352B48D73BF5A280E2CC8953CC85F5375879BA75EEAD872A802EFA5930009B39F + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014 +P = 049FA258102F70D0DDB88C82AA4BDAB463A2175B0EDEA5F671FD1AF4FD7A653260AE74FA5BD59FA7ECF9A486807B88EB088162E8A1E659CFD89577A19DC37CFBAD49A8796B7EFBC209B3217A4771A92BE777AD1C80365A7F300B029279ABBE4BC8548620BCB191FB436C26D009B9545CD017D86598DC335510FCB3674E4375B29C + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015 +P = 04FECFE5CA436A0EB12F15B89E2E2AE0879E08B6836968AA39156F471DAD9A39EABC413A4BD54E56A36AB703368E3CD40A8BBC43A720FB64FC5048E9B1E70C362251CC776BD85BA6FB813073BD51D9AD120AB4F34AC18731F9F3DF449B33BB54FE736B7875DC832FD04EF637851B6FE2D0930E3FFA1D3ED6D0923380D2C4821E6D + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016 +P = 04D7846555C9C148ACCA5B614298523B4D54EE83ED7C6DEC2322A6E113B777A1815E429DC4C06FF7DEBC5134284F15E3E53A9893F9141855AF536A5668F744D2CF4345AFCA3103C5DE9138927040A19C00994BFAC32AE73BDE146F21125EDF61EAADFC15B5E75ACBA0FFE6B5B8CB011814A9E8DA9E7F8B6B15AEC9B3DFF31A5F1B + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017 +P = 04EDC1A96AC42607A89B4A62F44D3631A2BF7D2DCDBEE67E59FE8E82546F70F902DD611AB370CFD52BA78207835345524B84534157F25B6A77E09B029EE287800824494C9CD4F8D5186AF446F1D752590CA5DE9A8B82A7270485136BB7B488480F2312FBB8452A91C42EDB483F8D7798974C06E4D04CC1CC3D7E6F2F3CE8E9CCFB + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018 +P = 04625BBA7A8584E30A5EAEDD9D80263E0115EBFCB615E213F1275156F89C3291BA185B8228CCB7FC6812D4910DDD83DE6E8D605C44E8DC4FE0E465A99F5B211431DB615EF242ED9530CB98A7AE6021F178F2679B5EA52523AE57F34DAA097F26AC4CDA42F15A04B6EE6D16076EA8426E403646E0A52A3B62E1C973BEBDD018F9D8 + +k = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019 +P = 04814AEF2D04E9B3724619D94096A5C00177E6D7DBA81BBB68FFF4FC459F8622E5E52A864B23ECB0FA6BFFBAC2FDAC40E8F174EF9DB2360C4DC27611512E9CCB2988DC4AF6537CAA21DF06AD44FDD058FD17B9F3D514B29FA5D39745CA808333D9A1567B954FC06B7F3B1F5EF10528CFB5AE8F2D17B0524762DC3B052C9358DA08 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001A +P = 04014E735D319B3FD5C49158082C00AED737AA329E48935EB2A3BCE6F8EE8E6A4599AD9E95C782799FE5544B9DFB5E99763A1DBE04FC45A2C4445B5D5BD9E83A469BB6464BB17AC42B87CC8159348BD9D8EC9093BAF3B7F3C7A5E5C09696C646BDA089E2A1C0653EBADA7C9AD0DD42BB4E4CA5E3836BA4966AEBAEB89720C3DAA9 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B +P = 04193E9CF5595BB66860258925CA86C15DBEB9FF4B3C215BA524FEEA43C1C7FA01AC841AB602DBB54D6D1DCCD0CD19ED44CDE9896F4BF5965FE79F6AEDFF185484FBA4478C58F6C643321A2649FB5C6FB44C8CADD09916C8733DDD2E9857116F66861DDBCD3CDF2B8B3525EB09A567A0B97851AC12F3EAF9B556CEC0CEE1773240 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001C +P = 042194D93F960E171EA3F3BFF2FA05ECD46654463D8F75E035BE3733FF1585FCAD0116B50C8DE460B9770D59C9EA78A78FBEE0ECEF292791EE3E2E9AB92B2A43642A37D4F7916FAD03BE9288F02D0BFD303AE2E8F0E40E25C71ABA94C1F1FA4C4810192E58A993651345B6833A0FF792A930BB995190E04050574BA1C76144E626 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001D +P = 04ECF05DDCCC91DB01C4648BF10AB4EC19A7F10E9136B459FB24A46ADDBE65114EE9204CA5A6FF4382DD6FC74208CCD74FCEDF95FB0D4F64FC92BD1BB8FE1B266D31593311CEC51563F0A5DD33744AAC9075DC8AFF9E7EAC2E2FA5A0F9009093FE1834A4F85B066055F2BEA7F2BD83E235B97C3CF7AAFC4EF6ADD125767CF19D4C + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001E +P = 04FC48AEE2A55B4C51F9660883496364686CBCD8DFAEBAB2989C1EF9375BC860C4845F5E5233C3DD0AAD848F2DCFCC38B6F1EC5154E01BB85D89B16B0FA13894BAB852A770A632E051AAE52364DFF1D5FB6F4C084EAF28435EE9BDA8E68C3679C219D27ACD022ACC53F9805B6A03E619F5B222450588D3C11C9FA87F3409937751 + +k = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001F +P = 0408228917E1240D10B48BFE47066270ADCA228B846CC2525D11688FF731825C72080377E8DB22CBD808F8173BA7DD5EAF3F42C1C2C6B11F245731163458FFE4B92C3F681D0A273BBF62363B77BEA5604A8C4B31FE27CDCE5A003139FA32845CD155F69DE4D672F248F56138621903CD1FBF442144DCF9A5AA7A342401CE75F2A1 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555C +P = 0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E3D7D14DCCD8068EE6AD3DAF159E52AC033ECFCE30922CC91F46CD7BCC50422733A5E3E0F38E90238DB221AC83D4F524FF44C2F7237C8AA4DFA33CF2807C2E9F + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555B +P = 045CE4024147F6BA0334847CE2FECD85A8E0D77788C3735A85F1D9661D6DD7C08152193F9A3F003244D40BE213AC9D925407D444A3662DA513E55EE4D50F55D5281D3C5BABFB1BE9A2C3DA3A30B3FA6A479E12391B9BA1B4624E27CB213AF6B4E530079F6B2FAEC87D691C890356800D47555B8B3FD2EB9FD0461D010B82EB194B + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433555A +P = 0411067A32D4B933BAF041F9138C1C71BE1B0DBE5C1C6A9310D4C02225AC629DD5F6FB22578F877F9EEA9E9A2061FC626559E2FCFDDDFC0AE0A51C9E09607DBFC59358DE1EFCC530C5299102AED84308807D46588EAF5A2A10941C5119E0DB94D6DB6B174CF06E44903C0AB3D34F74512281A0FA4BEE1BEACE04EAEA5B57B1664C + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335559 +P = 0435639D2D4177A7FF7B4C527BBC4F2F47E2270B5488CFF05C7197CB41C8A195346AF767D3F1322996AD76C128F18D2E2D6AECC36396F6A5571867E19C962C150F8FA50601B700842DC81B24E0DA54126C8CCC071F57F897B17375E3EEC4CC1D8A28468B128851860BA1E94FD5C03FB817C12114EEB1817A8CCF4199A40B2D0AC3 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335558 +P = 041598B3CD5CD0E245A8BA7F70E63F9D3691B904D213B1E7172C7CDA7D2C7AAB913500F3CDAC78AA9F9BA081168056887016BA87EE1317E4D842542F346AE829F0DC4151C4A85E720E1EFA43563914C4369F15162FFFCCC0563641E272077F494388CB3C122A25FF515448F3C53BC9341B89479407CB640A5888C4619EA17F1039 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335557 +P = 040AE6B086ECFADA271913F0E20F39D8631D9F945900CAEC5494CBAFAB1F49002D4E3EC81868B9C9315591E966E230D2E10DD1494A641B23FCC473F67A35308FF9A876A3E67F1B8C6D48A6474703272DD353210AD6CE3545F56003F293C8B6381F1A98A12C0E0F0A9848BCF93C7AD8FEB239095543943376F672AF4997B93BC0C5 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335556 +P = 0434EEE0304574716DA0DBF629D7299CDDEFC215006F988095750D98B04509260C231E8B7BD6B8E760ABE8AA314148595D1A2CDC7D4A23133361DC65CDEB4510F8E86672152EA46FE7DB47CD2CD3D5C17F396EB59083B56BE2E50E8D738692ECAE15DB4999FF9C2FBF6D60326452FB640A9E3CE9B90CC6E7285EDD82203B46A25E + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335555 +P = 043AC03447141D0A93DA2B7002A03D3B5298CAD83BB501F6854506E0C25306D9F95021A151076B359E93794286255615831D5D60137D6F5DE2DC8287958CABAE576BC5AB35D652A94C31F111239C144EFFB46824215443447370B4D9F3842EB0EB5D7BEA2574F112163EDE57BF4DA5A9FD30D4A3E1B302F016DC5F789FC7CAD621 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335554 +P = 04F6FB2B8E0E009089DFFF172BB2A336FC7BC2B21D5054673D9DFEE8A7A1B231FBB7E63786183923B2E2869C195603636DBA418214292F1F379F60FE70F449BC73775E8D779224FC4C67479EEB1EE62BF6646EF0C7F9F6C069FAC8FCAA74603B3FB293696462870284A6FC750A0EF7D1E3D7F64E7D3A857C94F64FC2702238F14B + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335553 +P = 04D671493BADCAD3EF190A2FE6DB714C09C8FE16995838803F4D30302B929DAAA246BB9D96FC124B771B7E1B9E5346C40A02CED54CA21FC569704C1EB59DF2397789E13D24053318742BF39E01FE3D8EAEB468598B550560248BD961458F1F43D1E5D5C677EE2B536F71957E628023D0A05DD9EDCAC7025E8119F928A09E051929 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335552 +P = 048496FE2D96926A64227B5EACA01652A8DFEFD1971B16D42CDA4953B901951E911094A28B3CDA63AB7100A13986710D2D9A0C9C277406D2F18CCB3B9CF99F13E435AD0666C0D026868DBB64A702EF772B979041FAB08D33C282E2CA9259CCB40E1238818446E7FC8DCA1DEF478B1251FF62B1969D3D0ED83126A07E5430E1828C + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335551 +P = 04D63C9ED8C6C27A7DDF753BE807BFD4B6CA4CA740E77C98F8347296F3BB4064A071A9EEE78A6E9487ED160B45E1B4F8FECA36E4B3532094BDA7E15706F1C8E032482D9684B34EAE89AB0D815B9AC874B0E4A0A74DD455D08A31521AD31ECAB27E66705BD3701F361DB953F1971D875BBB56D81D791A58B2369E862222B5CB358B + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335550 +P = 04FE4F433167E3CBE103BA2BBDB965414D123AA9C44BD25F43D10E9730E1F710A82EB49B684C12AFD8BAA89A4E49B4E506FB79D4FC492A2E0F6BAB62576056DD73FD4028902A9E36CAA9C69A54946855925DB11F71D3A51EC9697D41BC2DBFAD20A7BB38788F7954C48E91E4C653FB21DEC6BAAB91BB1C37E2357F4ED6874BEF31 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433554F +P = 04583A7CFD201FE24CE603BB163583403BF694B5ED4223F7975FC65058B40665A31DADDCB2D21549C2AB1130307455F7F34503910C760FF8E622468C6F894F90D2AFDC3B8A2225C90197F79A5AD4809550A418DA8643B55D97D52C331B0E584899617F642671C2063BE94786B9E02D74122790EB6E8F81EFAABC8185DEAACE78B2 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433554E +P = 04EE3331033B0AFD682D893A188F134EDE7C0C66D7BB9BE61F56177F5A2D719E88B75CCCF796FD92C94FECDD07C0EB16973364B979914F2FC44F7D6699A89ABEF4EE47A6838956FF2E9DC34B7CF9C6CD8755203489C388CC8A695DBEE4A81C139EC663F0B85351473ECF3FF83D88A3AAE9B7C1805756D5D73556318D852840032A + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433554D +P = 04FE02D8717B66E11D6AF798993613DE5806FEC277496635817694DE5D4515519CEBDD8DC83A3AACEFD78AD2C53306962DFA2EFC881C0527E53386A46207BF8069A49398979C9FA8502FF0593AF8A2138EC2A4C1E1EC8DF311A62BFA092CC0F52549D3A128572E0A5B11FA7A4F95BB416588E307B8A134C36E4C7BE7A12F795285 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433554C +P = 049E5F9B2D04688421E1A105B66585AF9FA2440378CD5A1E10525FE6EF54255269B19407293BBFC9D788E77A9E9215D263567EDFA84C88134FE2293F20A185762DB4241F826F81655DC8647907BE1C8AD4CBA7C00F40279A9EFED31736F00D3E059755F520ED543973A4F99A711C2D0482D04CF2BFDC3188900C57FB73697B49D5 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433554B +P = 04AF7D38C362D1756DCA11DE8BC74F1669AE55A5B4AB1A651C0E78868767D2065D0F15780AD8DCC41DE8E56B2EEB4AC97260E0485CA1697A67CC1A33C4CC7E08D19294CF7683C4AE64DADB6F9FC9C20C53013B862BBA4AD7E4EBD6763576DC9232BE9C47A438F88CB1CFF8F61DF8E054EB81809099FB41C76BEF8D91C79EAB4161 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433554A +P = 04F4A7733DAFD2A3FE2CF2333964D18ED2C7456C8A249F01CB77F030AFF87F0523F9D6C512820DEDE259194283CB9BD394535E80506AF185AA89D97EE14A8DC0B9DD028BA968221F651EC7CD35FB442AC16CBCB57EADC85ED42D792863C75FB8FCAD4B728C40A5D7F1D3376AC337A0AC8A786458A115278D57FD105A6CFFF64A28 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335549 +P = 049FA258102F70D0DDB88C82AA4BDAB463A2175B0EDEA5F671FD1AF4FD7A653260AE74FA5BD59FA7ECF9A486807B88EB088162E8A1E659CFD89577A19DC37CFBADB657869481043DF64CDE85B88E56D4188852E37FC9A580CFF4FD6D865441B437AB79DF434E6E04BC93D92FF646ABA32FE8279A6723CCAAEF034C98B1BC8A4B2B + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335548 +P = 04FECFE5CA436A0EB12F15B89E2E2AE0879E08B6836968AA39156F471DAD9A39EABC413A4BD54E56A36AB703368E3CD40A8BBC43A720FB64FC5048E9B1E70C3622AE33889427A459047ECF8C42AE2652EDF54B0CB53E78CE060C20BB64CC44AB018C94878A237CD02FB109C87AE4901D2F6CF1C005E2C1292F6DCC7F2D3B7DDF5A + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335547 +P = 04D7846555C9C148ACCA5B614298523B4D54EE83ED7C6DEC2322A6E113B777A1815E429DC4C06FF7DEBC5134284F15E3E53A9893F9141855AF536A5668F744D2CFBCBA5035CEFC3A216EC76D8FBF5E63FF66B4053CD518C421EB90DEEDA1209E155203EA4A18A5345F00194A4734FEE7EB56172561807494EA51364C200CE59EAC + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335546 +P = 04EDC1A96AC42607A89B4A62F44D3631A2BF7D2DCDBEE67E59FE8E82546F70F902DD611AB370CFD52BA78207835345524B84534157F25B6A77E09B029EE2878008DBB6B3632B072AE7950BB90E28ADA6F35A2165747D58D8FB7AEC94484B77B7F0DCED0447BAD56E3BD124B7C072886768B3F91B2FB33E33C28190D0C3171630CC + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335545 +P = 04625BBA7A8584E30A5EAEDD9D80263E0115EBFCB615E213F1275156F89C3291BA185B8228CCB7FC6812D4910DDD83DE6E8D605C44E8DC4FE0E465A99F5B211431249EA10DBD126ACF346758519FDE0E870D9864A15ADADC51A80CB255F680D953B325BD0EA5FB491192E9F89157BD91BFC9B91F5AD5C49D1E368C41422FE703EF + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335544 +P = 04814AEF2D04E9B3724619D94096A5C00177E6D7DBA81BBB68FFF4FC459F8622E5E52A864B23ECB0FA6BFFBAC2FDAC40E8F174EF9DB2360C4DC27611512E9CCB297723B509AC8355DE20F952BB022FA702E8460C2AEB4D605A2C68BA357F7CCC265EA9846AB03F9480C4E0A10EFAD7304A5170D2E84FADB89D23C4FAD36CA723BF + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335543 +P = 04014E735D319B3FD5C49158082C00AED737AA329E48935EB2A3BCE6F8EE8E6A4599AD9E95C782799FE5544B9DFB5E99763A1DBE04FC45A2C4445B5D5BD9E83A466449B9B44E853BD478337EA6CB742627136F6C450C480C385A1A3F696939B9425F761D5E3F9AC1452583652F22BD44B1B35A1C7C945B699514514768DF3C231E + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335542 +P = 04193E9CF5595BB66860258925CA86C15DBEB9FF4B3C215BA524FEEA43C1C7FA01AC841AB602DBB54D6D1DCCD0CD19ED44CDE9896F4BF5965FE79F6AEDFF185484045BB873A70939BCCDE5D9B604A3904BB373522F66E9378CC222D167A8EE909979E22432C320D474CADA14F65A985F4687AE53ED0C15064AA9313F311E88CB87 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335541 +P = 042194D93F960E171EA3F3BFF2FA05ECD46654463D8F75E035BE3733FF1585FCAD0116B50C8DE460B9770D59C9EA78A78FBEE0ECEF292791EE3E2E9AB92B2A4364D5C82B086E9052FC416D770FD2F402CFC51D170F1BF1DA38E5456B3E0E05B3B7EFE6D1A7566C9AECBA497CC5F0086D56CF4466AE6F1FBFAFA8B45E389EBB17A1 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F3904335540 +P = 04ECF05DDCCC91DB01C4648BF10AB4EC19A7F10E9136B459FB24A46ADDBE65114EE9204CA5A6FF4382DD6FC74208CCD74FCEDF95FB0D4F64FC92BD1BB8FE1B266DCEA6CCEE313AEA9C0F5A22CC8BB5536F8A237500618153D1D05A5F06FF6F6C01E7CB5B07A4F99FAA0D41580D427C1DCA4683C3085503B109522EDA89830E607B + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433553F +P = 04FC48AEE2A55B4C51F9660883496364686CBCD8DFAEBAB2989C1EF9375BC860C4845F5E5233C3DD0AAD848F2DCFCC38B6F1EC5154E01BB85D89B16B0FA13894BA47AD588F59CD1FAE551ADC9B200E2A0490B3F7B150D7BCA11642571973C9863DE62D8532FDD533AC067FA495FC19E60A4DDDBAFA772C3EE3605780CBF66C8676 + +k = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B3CA4FB94E7831B4FC258ED97D0BDC63B568B36607CD243CE153F390433553E +P = 0408228917E1240D10B48BFE47066270ADCA228B846CC2525D11688FF731825C72080377E8DB22CBD808F8173BA7DD5EAF3F42C1C2C6B11F245731163458FFE4B9D3C097E2F5D8C4409DC9C488415A9FB573B4CE01D83231A5FFCEC605CD7BA32EAA09621B298D0DB70A9EC79DE6FC32E040BBDEBB23065A5585CBDBFE318A0B26 + +k = F900A26CF56CB7B0FF237C20618DC333876D9685BD235F1CA0D5189DFC058AAF99E499DA6FABFE1A832853D5B599CAD46E17F4A5ADFCFE28242FD7B113577E35 +P = 04F21FBF6DADAE9F45BD65C24457052A058768EB74CAF05DD259C9B88737E02D41D40CC142E8E09BF7D02CD3A4540611906E41CBBC386ACCCA700981591A76DF06A45966A11B23E70F5EE2FCA2AB541C17C1C109885C9C3BE615D9EAD9CB114F0401BF504E4990701EE63B7B88C3114D68260F8746B7FD9423898B7D58AEB75E10 + +k = BA42D992872A1FB65C6753ACE2148DCC46A4F6A42CE3B945BDA250CCE56D82FC4A005C4B6CFA55F49AF6B12589FD5CC1B5770EDF80910D783896D246685CC72C +P = 04F2692F8846CD4020FD21CA34A96662C70F85C7031035D6BFBA6451980740FD6E8A4071F4F4C70277587BDEF162C7763C4597B9847E1E6467B1991CF1C41E0F31DFA98E9AA559924D6922E74668EB3ACDA01400D6A3FC5AAD347F545E7202F9A11116E06391994075F7F1C74607819B861EB77F3B6E02039EAE0FD4DDFCDF2B2C + +k = ABC86E059AFDFFA63612BBC3626B7656A24E0A4C06D31FE5D47F8989EB6A7256B5A199728882E690319D2EE4971DB5B104F042EC6D1DE6954D6AF4C8E0528063 +P = 045128E0AAD326378454C11ACFA3F422CF92AD2B15E6FF25E3502DA1A34A818552007B111838573C4771B134B316EA43FF804169C01D1FF847047372BBADFFF8FA37333A911D4DA38B78B47270F1056C79B618B10D768416C21A2519EF6CD1D79B08CEB6B0954AA7773373A570FCF7A360AB83FBD98F7567FABD6F6C7F4007EC87 + +k = 7E32302C279303812DBF743F41057BB1EA212E74A97E7136024B4E0FB41931DAD8276515F0F2457F31552BB91F2FAD0E1E9B4748C4E4512CEC7F0F8C69E962AC +P = 045481E7FE0546B8D5096AEED27451A7704641DACEBEF3A5573D5F77AD543B04E79BC8587A3B082197DB531E19C23B0CD3E46EE62479691F5E9F303AEF2A0D31E26B7328C56EE96644DBBE426158F2A6BCA593E476CEB5C4C268B0C11F166E1AFDAC135B1974C1072598AC44E21B0601A4C2BB9F00EDA5445C760B9CDB6D7E0A7C + +k = FF4D80590C4E5A0D083787943D4545E680A42A02FE329B63BF840D5BF584BD9CB697219C7BAF5F9689679BBCD7401C841EF1910680960847D5CF23F1673A80D1 +P = 0407F270F5F132C7671B5F2B9C3796D861987B2262B7C93F05167E118ECFB57F092B3E5FF48158401F06FF359FD906987502AAFC98DA9B9740CB8791837B3F1F2B3E1BC4F355C37697876957C2DBAEE29A4B20BEDF3C1E8AD2253EA4779A112CBE0441CEA31949A9E38A3B7E658A8BBE53A91EDCBCA03080D04431B7C1260AFEEC + +k = FF48ECE56931E7A9072C1B00B6956B137E26E9A092FBA247EC74042A4BBB9AD85302C4A458D2BE0AC862821FAE67A47EC683CE220AE740F18F80329C7340DAE0 +P = 04BBD837A084B2CF61CF702494464C3B4E980F8EB82D90CAAAC51ABBD971225B8D54AF6068D73B026949573F506121AEBFC27B235BC0EB8A46D2A3131C7B669ED9D32343BFF2D055424B8CCDB10237AD5DEA04B1F9A73B2E7B13B53FAB9F83B4CDEE7E44E73A6FAA2ACB135234788D9994644A288A382B809BF8EC1A614864CA56 + +k = 2E499B8F1134D967A0131F72A00447F17F14C4CD12D92C1B6564931445AC3342E992BB31EFB77626797DB6B1049E6E79D26DE8AD2E313EC0B5CE74377915898F +P = 04507900F097B39414D131C4328838F2ECCA638EDFC842D55886A605BC28981537B953BD12534EAB618804FBD018C0026B046ED19847952E31C3F83A9BD5A15675A6E681CF7A22DA859A0557979D7887DABADA3D321539270D55251CBBBDC8443E8E50BEFB325BF468C16EDB163117BFCE4603BBC809E95D4273EFFA4BD59A9B01 + +k = F1CA0FD91214500E30CBE41727B6547EB8F450390ACFEAFD50F50EAF316A8342C27D85897D8F4EBB23B2BC311C7DFCF825B3DAA86F5D50689BA2E450FA6BC489 +P = 04BE54A4DC5CE7EFC4722E18CC763658297D2CFA7A302EC986E3B8699965DD7ED9973E68A5BD53AC09956C597AAA254A8EF34DCCC65D06D97BBC68873E9A95EA7BFEC4F9FFFA242960B8159B3E05C97602ABC4B810EDD20CA17999B27426FD54DC6A2A31A35D7ADC9ABEA258656A5B1AA10BF637DA6D38AD5D48C74631B2BDFD9A + +k = 8B06B65E5F2FC1342108D34029109C9464FE15436134177AECFF9B5F5667E4116FEBA49297F1C70285883FCCF7E341FEFF77714833B8ABD02BC4339323637756 +P = 0485BFE839FBD1947023764F754618E96E6DE0275DF0FD5185629C1BBFD346E7A7719A22D5C8D5399E1AC51E088F2E28B45811D544452927758C9C8F6B016EC353459A8B30ECB9D177159E08BBE4D20546A38C615C470F83400D594734EB3BD0BDC3AC2E9C9A9A9972BB72A6CC543A4D8D13F7797D1E8EF7A4812F70F6261C0C1D + +k = 8FED62DB9CCAA6AD3D8D0B95BE50313CAE81CFE6D1EFAD88651A3449428821A15C29E605F513B465F257E644AE1963FA9B6B089E7F73F448F3E1CE814C55160E +P = 046EFED5730E6FB93643CB99C1B2D5C0FFE9615320382C286493F786495FDC91C521F03C2ADB2167C33D40A3F3531BA60E5A474A7E7EE13FEC22160527DB00446167CA37BDC0FB49BB7A693AC2AD00AECC3B6ECD4B86A7D9D7C7C3172C3702D3C111FC170F6F15FC69BBBC0C9A35AF959C69C8D893B10874DBBDDF860C8D4B99FF + +k = 7E7F0411E036C876153E7513F4D5FC08331BFD98A4572C99303F19934D71879424BD1E200D8F26F4F24D61DDFE7B3BA0FDDF9974933FAA6B16FCAD1EBE122BC2 +P = 043E325169751473BC9B3CC4BECBB2553D41BD61666DC6C3873334AFA9CE94F9E869A4F528342E8D8E1310CB2A00C8FF34028DCA0A9923BD7869FFC5A1CF1F94EA7A24B0214A1FF1DE4AEDFAA3DA84138D4EC6FB4D12AACF30265DAD456D6CFC2785F49C443A3FCAB02DA58F447C227C3F5B4B42A11DAC373E3E2437F478B72D71 + +k = 3567011BD463EF01FB1466E81EA6DDC4F7D520A7590EF9AA8758C8411810DAEFB772ADA98E8C0338B720CDE743DDD9FAC5F4F3DDD0FD5035AB8091C5CC843ABF +P = 04AFE7647157C4417967DAEF14DD6743AC2C287772EF8AD414CAB2C7895EDA1868821A6961D60292F8CAD0337FAB57DF24337BD2350EAE52868651B20EFE3AE27AF569FB53DD8D20278E3D3309F2AF384C415708B90B5A71721BBD5C1FC3C7EDAD6936D591AFEF034F83B5D14D9146A3D6DFD3931A9BD6301648CDC2587B43E4B8 + +k = 85DC8B2D3933986C9CD908DC852B4EC4732F772D5C9393C9F6E78493F5B4A96CDF3552120181ED67BFB362FB564169763E44450790EEA6108206745A3A77A869 +P = 0421D6A8EA3BE0501535FC30C0C7A9DC620C5A0E6AA781EF5EBE43BD4BA9168AB71E9958F771E7A72CF83DA9C86CFCE52C538CCDE79C77C5F99173ACFF0805BFBD2DC68060F22AD06317D75213B10393C89F62EA17382B3DE1871EFAA47B685C13BEEC34E3B0EE8DB9E9BF3634D91AB18B9EB2CF3DCE70FF8EE5A69A7433F2553B + +k = 1390597C870F8A70976627AA40971225F7E60E200DFBEE0098FE584609E89AD16D12CE5F6DBD92C61DAC8F86CB7C39B511C11B5EF4159192FE875E2F5EFAA24A +P = 043DF876898FF7D0D7D2EA159F1762E4DF768E125AF30F4EA1E0E8171BF5347A25136DD2B7A04B6EC5189B587812DEA3996A1F194E9EFEE9E872DC6BA2A45D2612E86B38428B17B79E3FB8A7B719D44B90DEF6FF12A3A30878C19096700608DDEBEB3087356BE586059C92852E4A8D6C24AC15438B8454C7A21EF8420AD0DD8E76 + +k = CBE1C575CFF73D1CA1AD9849D079CD5CECC1F05C3A38DC3E7CFE5E926BA79F99669F9AB6779E13C79C1CAE4FDF2F1FE92E12567237782DF0E9392B53C01A0BCF +P = 048713E57D0D8CCDAE917CD52DF11A3844E97D2168A146852FCE646B85F8166532B5DEDD593A505F48F3CA72A02D70C6F96F9D1AD8594AD3E1F1B6F49C732D1CF175BB33FEFC282EE68AD58801986262B2110342361DCCCEDD4308C2D98AF017CC30C6BF0D512C67382552B4CB25B2C5BEFD1D1B5BEE914B6CDAF1F035C945961B + +k = 2D8E623020A342367FD75622E321B48339CBC20C657701BE77C90F415A99EA143AE3B58794B599B9BA42963FC20E4AFF45484E6559A880B574BFFF1111CAB5E1 +P = 049C8E75E252AB7B545C141673F489777D7274119C7906BFA3ED63E743BF5EB6BEB52D5FC101D654B0BB5A9C23464CD698A1B1A81FE5F2878DB466B9423505C8DBA0669F02A80C1E527385CA6E56B1D3FC4B2372701AEF35DBEA7930577E2714D3288497A358C17C76040586AC18AEECB570AB3F79E986D6343A9502A3DE97E3D3 + +k = 7D6B7933D1A3665DFBC2E68B3F50CE73E3F56C8E0BC4EEB6F2282FC6ECEE0098B97825D5DEBC838358F3111D5FF52BBB0B6007CF3D5C1532209DCDA41FEF996D +P = 04B65BE612190332F03F889458256EA1B76634D905B4D789817B459422444872FB24F10A1DCC7849FBD9306F5FCE9819BD6FEE25C92338B8B16AC46F26469AAE0FD138111D299EC6AE0C82D26AFBD60B3C5FA92671BF7251193E59701B425E902DA6AB3AD1E0891910200B3C64773C24086A6466D5558A084B02C5095644095C1F + +k = A947D65AE9B22DCBEA7824038C5FF37B0EDBCE9C9A902D3604C8EE747F6512D2269286D697747CF1FBFFDE77F2A005E97420E7C75105B5EB9D91E743EEC1611C +P = 04359D9DD9418A096185F2867C124C638064B07FFFEB198B70DA2578ACDA1C792DF0658222D22D78A3E931AB95BA0C8F0B1CD4181F8432C2B9BC9D582B115B9F5F4656F3102F20FD65D19AE462E940407410FBECFD2B07C89DECCB6746C27D53135E29A1B481B62F313C4AA0CBFBFEB92F335546E0681735B88BFEA7171FC7D59E + +k = 5D248DB260DAF3F59EAB3CCFBA121D00D72173628EA2F84E20D0EE7AF7BADC978165829F469E04E5288B7FB8D13BBE61B3350A460F0C635E48BEE7AB3F8C1EDC +P = 04986E22D0DD3FBDF1242F26057E46D911959CE6C9EDE7B25589ADEA26E41F9FE73E0C8D85DCC9413EBBC3EC3A0EE6738681D6AC77586A98EE86DE5785923A27EA7E7A3A7771A05A12887A9815B313A15D6A3E31FF24F2008FFBAF45B551214B700D2120DD768EB27CDC3D637091F7F7FF6E67FD299CFB3FAC1157E96E6B57823A + +k = 7A037F66EB4A38B12CED0DF8CD0BD0EF8183BEECE3FF45BA41C7212EA7514B0DA51CBA125D230E20D8D8BCD58FDA65B35EB42F0AA2B771A0F54216C627EE12E4 +P = 04690730C4C447121BA3F21ED223AA1B2D28779F904047698D369C2274CDD576A057D78B4B4AF17F0C67D664BF364990BDE4F7790B219A1B08F79D102029F411DAF1DD91C4B99A2DA40E1DADA511A0D0902D5D2ED7B817D20319D266B2E1E577C543D905F18CBF193F0F059AAD72FBA13F3CE66D5302597B2FA134BE61DC3F1FF7 + +k = E5270A3DE1AA3919D8CA207E19326020F204655BB02A785DDFC79CDAD4A8719212A10FF05C8E00F47043971FBDCFCD99F4C200754728F624C7B15306902DE5CE +P = 04AAD4FA2E1A0BC2BC3C674EB8559A316E6C62853A72D3BBB1EB948D7099D5B40935C330A6D9F9F97D1173D23A5D2268AAC8FBD82344CBCEC3173A39E603710FE23A3E3AC7266BE1CEC831421DC202612DEC0D3D1109439C14E678AD8911FCD9DDA21585C0920C3D3A423F3F24A8DC05F3FE6F4812679043E9B7F01F64983517A2 + +k = A850D6B6EC16819C1E7613A555B4D4528951050C9C0E797DCE962D02467965515608C3C4141B816341EC57E70B647CFCECE4A233F6B08EF1BB9ADB35F5E0D2B7 +P = 04E7EC431574785A44DD4717C63155ACD267408B159BEAFC68792C2B52D25C9AF492F6336A1D9B357D1906C9577C1D8A2F5D242D3431DB45FE1C351BF5D31123B75A3A91217F4CD5D961AD35BAD5E739ABCC20B7D9E01F17F6737D323AFC41665618333E12B12A9D68DD2F338718C5660A606D98FE1C548FEF7FD75565E50DF219 + +k = 3BE197826450A38E14D7B82FF43704F4C5540D5D2030847A176A289EB40CB01BD11163D25760AB5B9325BF66ECC6EE2259B701CBC9FC8049F6CEC0A912F6D2EB +P = 0419337345ADB2CE07DDF6D18E44C846D46D8DC69B363C1F1146B12B46F5DF1A6CE20163FEC2685221ADEED852E46A8428D319A2196DAE404DD6B83E0FBDDE1FAF4D746D28BCF3FEEA48C4F7DCE8258FF10FC7D7F2E5CFC3CB6B6267D2AA72CF3BB9FCDC21F78B1288B71510E7BB52C6592EBBD601B0A3E0FC8109B3135EE56037 + +k = D9C0B3ACA75C7B0C8AD9F8E5E3EB54AF5DD74E645871016F1C40B0F1FAB86363123972D464B02CDF79B374EEE0FBFD854EA7BCC2CBEF24B42BE3E2D833915DEF +P = 04AB611FB3B7126B40D924AB603849369E6F34BE6D69F3FB4249A73CC19DAA769E9A2C4B2443C05125E7AC6516DC68DC67E495021CAA71619506D7F461B1EAFC60A1AA74AC67FE74B60278D4A7F0732C277BEB7E65A5125E0EB95EE9CA6401A170D0BEB72C2C88632FE753F8AEFD2AB5D22C2B3C35380523804F892F5C5EEE9F7D + +k = 532055998B64E6EE992759C045F6FD7DF45C08FF86B51887F2767D8F1AA757F95A2DCE93B5962253E3B2E98E0F560EA6C09B895270146AB5799D18B9446A946C +P = 04E9C6C0927FEFBF75868F3E08BA3DA97236DF2E541AC81019707ECE9B236478787DCBF5943ED0F5AC68D67A9D3871D979966A64DF8BBC363E5617B0C516D440E4B95B98D1F7AD4885D38B3CCF3FA6A7471747BCB44D09F52E6B0A3267784A148DC0C84D0E5AA545F39CEC7525712F56EC4F0685975779652599028B125CEECF0B + +k = E45E639CEA3EC42ED97462D634C331C73E78F36F9AC151F3B1873EF48E2CD4A2447AB93151B234196CE951F709698C82812052C97AC324C011648374B10C7541 +P = 0470BDF3266E9312944B1A7F98BA6142D1CEC6AAC1F08606D749927A5DD9C061C7DF2D6A34EAD0B1E698CB52105BE4B10A4A8ABEE817523DA70791BF032092FB3FCD5CA5070D44005D8094613AF107BB83BD78F12F358FF86EE4F0151EEF76CF1AE406BFE0940AFC0E3D0B48893CD0CE7516BB92C117722D591DDFA98C2C7038A6 + +k = A6F9D36D2871122894DDA5244B3C7647634872B04CB9003F3E36D38C9F77F6AE05937133A75E434AC81DCBEF0E8C10630BAAF22BE2FE7806537722FF15D9CCF0 +P = 045ADB606F1FD2BCFFB5CBA7E999B376FA5153BFA9C15883A9F3ADA6D116959BF2B1D1B04F3493A99ADA82E64D820A2B4ED53657DAFDEEB80ED186495889E73FB06DC38CF12B0650F3960C8BB91E2F007EA5D269FD2D1D54535783500279E919C544147592C48CE6F98CDC3BCA478C8C7625CE354AB7B4C9D0277DFA3DCFEF88AB + +k = CAAEAD3EDB1D30BA148B35549EB0CB02E15364B47D390148A5A8C077D003F3485108E8153021FB346DC0C30CF9B2164A3967E7E63A0920E3F052EC54A4C9333C +P = 0493D4E419D4313D0DE37B140FAAC2D3B1BF94B8D62915E629F23EFE306EF715D3EFF6F31F36912E6FC39BDB25CABAEF5D69F5BD2D4915F1A02D27E0D18EE4624504C4C57238AAA4FA60BC52CA4CD4D97724454262A1B7733F7410AA4477AC3C390CF5E6B0291A9EC31D014068D4DEF528641BBB50ADAA67F00CE91F28BE707884 + +k = 3767D04B7773BCD85AD6E89A968DB3B348B29FCE2409B2AFA7A0312D719A4BC6C0D6C0943BEA116A618FD0BE07C56BCD726D2B6FBAD5E5ADEBF5A6BCBB9CBD84 +P = 04C629642E601D4D0728125A41858A617499BF11C8F11BE27AB1BEB5252E12B9D15750108ABE787023005BBFB9A4E60E4346398AC49940DA968A509E72DE7C5497271B3149E344E103CC8AA323DAA5D2FB9E829DF010D83ABA30501D4EA3A8B476BC3054FCF8DE6626C2B008AE8E608C6A4FC3723BE89A80EBC3A0149A646140AF + +k = 05A523B764408022BA1E38D8C37ED5F669191D787CCB9B05D084E7710A39D2152038F28A0B0E06A640D86DA56C1B70B60EE376710AECE07118C49AADF63E9359 +P = 04E6C3247F809DD0B48A19DB52C7D5224BAD5D137DDA24ACF5E2912016EE604BBF1CC7B1EC29EBFE0D79E7DD5373BC00F0009A97BAF0479CF5D2E775CAE66D181158DE0355F3DC69E38347450858358A5A5DFFFC10BDCA32336D8BC18EA503592F88D2FA95DB7147C6244BF6C08E0624B2024CC824E04A9D987B29C6757F9339A2 + +k = E3C230A387FEA7FCFEC4EA37E84E0E989610DD5C52D2A847BE5D9F0AB06A3CF763FC3AAC6FF17F54BF9AF52D7537C3056A19F06A8CC756F3BF6DD6187790068A +P = 04B7554BC4B1A019E994CE6012575DE7000458AE8A4CD6D6243913FDF1C01C994974CF0300207AB6AF757D1D16E21BCD1E564C0AD70DEBE3CFE8319E0E7D953CBC72FA42224947E7CFD3EAAB4A6AD13EA9DEF6274F06F33A917E94B3A7946EFFF33DA59B4EE22849901BD80E562C1CE926A2660876955B4F95107D94783EF2FF14 diff --git a/src/tests/test_ecdh.cpp b/src/tests/test_ecdh.cpp index 0458a160416..141d5dbb49b 100644 --- a/src/tests/test_ecdh.cpp +++ b/src/tests/test_ecdh.cpp @@ -52,8 +52,48 @@ class ECDH_Keygen_Tests final : public PK_Key_Generation_Test { } }; +class ECDH_AllGroups_Tests : public Test { + public: + std::vector run() override { + std::vector results; + + for(const std::string& group_name : Botan::EC_Group::known_named_groups()) { + Test::Result result("ECDH " + group_name); + + const std::string kdf = "Raw"; + + try { + const auto group = Botan::EC_Group::from_name(group_name); + + for(size_t i = 0; i != 4; ++i) { + const Botan::ECDH_PrivateKey a_priv(rng(), group); + const auto a_pub = a_priv.public_value(); + + const Botan::ECDH_PrivateKey b_priv(rng(), group); + const auto b_pub = b_priv.public_value(); + + Botan::PK_Key_Agreement a_ka(a_priv, rng(), kdf); + const auto a_ss = a_ka.derive_key(0, b_pub); + + Botan::PK_Key_Agreement b_ka(b_priv, rng(), kdf); + const auto b_ss = b_ka.derive_key(0, a_pub); + + result.test_eq("Same shared secret", a_ss.bits_of(), b_ss.bits_of()); + } + } catch(std::exception& e) { + result.test_failure("Exception", e.what()); + } + + results.push_back(result); + } + + return results; + } +}; + BOTAN_REGISTER_TEST("pubkey", "ecdh_kat", ECDH_KAT_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdh_keygen", ECDH_Keygen_Tests); +BOTAN_REGISTER_TEST("pubkey", "ecdh_all_groups", ECDH_AllGroups_Tests); #endif diff --git a/src/tests/test_ecdsa.cpp b/src/tests/test_ecdsa.cpp index 5aaee63c2f3..ff52e5fc57d 100644 --- a/src/tests/test_ecdsa.cpp +++ b/src/tests/test_ecdsa.cpp @@ -12,6 +12,7 @@ #if defined(BOTAN_HAS_ECDSA) #include "test_pubkey.h" #include + #include #include #endif @@ -144,7 +145,8 @@ class ECDSA_Sign_Verify_DER_Test final : public PK_Sign_Verify_DER_Test { class ECDSA_Keygen_Tests final : public PK_Key_Generation_Test { public: std::vector keygen_params() const override { - return {"secp256r1", "secp384r1", "secp521r1", "frp256v1"}; + auto grp = Botan::EC_Group::known_named_groups(); + return std::vector(grp.begin(), grp.end()); } std::string algo_name() const override { return "ECDSA"; } @@ -244,6 +246,47 @@ class ECDSA_Invalid_Key_Tests final : public Text_Based_Test { } }; +class ECDSA_AllGroups_Tests : public Test { + public: + std::vector run() override { + std::vector results; + + const std::vector hash_fn = {"SHA-256", "SHA-384", "SHA-512", "SHAKE-128(208)", "SHAKE-128(520)"}; + + for(const std::string& group_name : Botan::EC_Group::known_named_groups()) { + Test::Result result("ECDSA " + group_name); + + for(const auto& hash : hash_fn) { + if(!Botan::HashFunction::create(hash)) { + continue; + } + + try { + const auto group = Botan::EC_Group::from_name(group_name); + + const Botan::ECDSA_PrivateKey priv(rng(), group); + const auto pub = priv.public_key(); + + Botan::PK_Signer signer(priv, rng(), hash); + + const auto message = rng().random_vec(rng().next_byte()); + const auto sig = signer.sign_message(message, rng()); + result.test_eq("Expected signature size", sig.size(), 2 * group.get_order_bytes()); + + Botan::PK_Verifier verifier(*pub, hash); + result.confirm("Signature accepted", verifier.verify_message(message, sig)); + } catch(std::exception& e) { + result.test_failure("Exception", e.what()); + } + } + + results.push_back(result); + } + + return results; + } +}; + BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify", ECDSA_Verification_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_verify_wycheproof", ECDSA_Wycheproof_Verification_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_sign", ECDSA_Signature_KAT_Tests); @@ -252,6 +295,7 @@ BOTAN_REGISTER_TEST("pubkey", "ecdsa_sign_verify_der", ECDSA_Sign_Verify_DER_Tes BOTAN_REGISTER_TEST("pubkey", "ecdsa_keygen", ECDSA_Keygen_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_keygen_stability", ECDSA_Keygen_Stability_Tests); BOTAN_REGISTER_TEST("pubkey", "ecdsa_invalid", ECDSA_Invalid_Key_Tests); +BOTAN_REGISTER_TEST("pubkey", "ecdsa_all_groups", ECDSA_AllGroups_Tests); #endif diff --git a/src/tests/test_pcurves.cpp b/src/tests/test_pcurves.cpp index 0028404f0c0..fb5cc4721cd 100644 --- a/src/tests/test_pcurves.cpp +++ b/src/tests/test_pcurves.cpp @@ -33,18 +33,27 @@ class Pcurve_Basemul_Tests final : public Text_Based_Test { if(auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_id)) { if(auto scalar = curve->deserialize_scalar(k_bytes)) { - auto pt2 = curve->mul_by_g(scalar.value(), rng).to_affine().serialize(); + const auto k = scalar.value(); + auto pt2 = curve->mul_by_g(k, rng).to_affine().serialize(); result.test_eq("mul_by_g correct", pt2, P_bytes); - auto pt3 = curve->mul_by_g(scalar.value(), null_rng).to_affine().serialize(); + auto pt3 = curve->mul_by_g(k, null_rng).to_affine().serialize(); result.test_eq("mul_by_g (Null_RNG) correct", pt3, P_bytes); auto g = curve->generator(); - auto pt4 = curve->mul(g, scalar.value(), rng).to_affine().serialize(); + auto pt4 = curve->mul(g, k, rng).to_affine().serialize(); result.test_eq("mul correct", pt4, P_bytes); - auto pt5 = curve->mul(g, scalar.value(), null_rng).to_affine().serialize(); + auto pt5 = curve->mul(g, k, null_rng).to_affine().serialize(); result.test_eq("mul correct (Null_RNG)", pt5, P_bytes); + + // Now test the var point mul with a blinded point ((g*b)*k)/b = pt + auto b = curve->random_scalar(rng); + auto binv = b.invert(); + auto gx = curve->mul_by_g(b, rng).to_affine(); + auto gx_k = curve->mul(gx, k, rng).to_affine(); + auto g_k = curve->mul(gx_k, binv, rng).to_affine(); + result.test_eq("blinded mul correct", g_k.serialize(), P_bytes); } else { result.test_failure("Curve rejected scalar input"); } From d7d565bd078ef60bfce841cb1090b67bf3f0f5d5 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 24 Jul 2024 08:02:38 -0400 Subject: [PATCH 2/2] Fix bug in point multiplication when two points share a y coordinate The pcurves addition formula implicitly assumed that if the y coordinates were identical, then the points were as well. This is not necessarily the case. --- src/lib/math/pcurves/pcurves_impl/pcurves_impl.h | 12 +++++++++--- src/tests/data/pubkey/ecc_base_point_mul.vec | 10 ++++++++++ src/tests/test_pcurves.cpp | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h b/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h index da203ef5751..2d53756c82b 100644 --- a/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h +++ b/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h @@ -657,8 +657,10 @@ class ProjectiveCurvePoint { const auto H = U2 - a.x(); const auto r = S2 - a.y(); - // If r is zero then we are in the doubling case - if(r.is_zero().as_bool()) { + // If r == H == 0 then we are in the doubling case + // For a == -b we compute the correct result because + // H will be zero, leading to Z3 being zero also + if((r.is_zero() && H.is_zero()).as_bool()) { return a.dbl(); } @@ -687,6 +689,7 @@ class ProjectiveCurvePoint { constexpr static Self add(const Self& a, const Self& b) { const auto a_is_identity = a.is_identity(); const auto b_is_identity = b.is_identity(); + if((a_is_identity && b_is_identity).as_bool()) { return Self::identity(); } @@ -706,7 +709,10 @@ class ProjectiveCurvePoint { const auto H = U2 - U1; const auto r = S2 - S1; - if(r.is_zero().as_bool()) { + // If a == -b then H == 0 && r != 0, in which case + // at the end we'll set z = a.z * b.z * H = 0, resulting + // in the correct output (point at infinity) + if((r.is_zero() && H.is_zero()).as_bool()) { return a.dbl(); } diff --git a/src/tests/data/pubkey/ecc_base_point_mul.vec b/src/tests/data/pubkey/ecc_base_point_mul.vec index 81cd5527392..9615ba3aae6 100644 --- a/src/tests/data/pubkey/ecc_base_point_mul.vec +++ b/src/tests/data/pubkey/ecc_base_point_mul.vec @@ -1613,3 +1613,13 @@ P = 04E6C3247F809DD0B48A19DB52C7D5224BAD5D137DDA24ACF5E2912016EE604BBF1CC7B1EC29 k = E3C230A387FEA7FCFEC4EA37E84E0E989610DD5C52D2A847BE5D9F0AB06A3CF763FC3AAC6FF17F54BF9AF52D7537C3056A19F06A8CC756F3BF6DD6187790068A P = 04B7554BC4B1A019E994CE6012575DE7000458AE8A4CD6D6243913FDF1C01C994974CF0300207AB6AF757D1D16E21BCD1E564C0AD70DEBE3CFE8319E0E7D953CBC72FA42224947E7CFD3EAAB4A6AD13EA9DEF6274F06F33A917E94B3A7946EFFF33DA59B4EE22849901BD80E562C1CE926A2660876955B4F95107D94783EF2FF14 + +# Some weird cases (confirmed with OpenSSL) + +# k = (N+1)/2 -> (-1, -y) where y is the y coordinate of G +k = 7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffad9e527dca73c18da7e12c76cbe85ee31dab459b303e6921e70a9f9c8219aaaf +P = 04FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC6E3D7D14DCCD8068EE6AD3DAF159E52AC033ECFCE30922CC91F46CD7BCC50422733A5E3E0F38E90238DB221AC83D4F524FF44C2F7237C8AA4DFA33CF2807C2E9F + +# k = (N-1)/2 -> (-1, y) where y is the y coordinate of G +k = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffad9e527dca73c18da7e12c76cbe85ee31dab459b303e6921e70a9f9c8219aaae +P = 04FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC61C282EB23327F9711952C250EA61AD53FCC13031CF6DD336E0B9328433AFBDD8CC5A1C1F0C716FDC724DDE537C2B0ADB00BB3D08DC83755B205CC30D7F83CF28 diff --git a/src/tests/test_pcurves.cpp b/src/tests/test_pcurves.cpp index fb5cc4721cd..c491d0fbce9 100644 --- a/src/tests/test_pcurves.cpp +++ b/src/tests/test_pcurves.cpp @@ -258,7 +258,7 @@ class Pcurve_Point_Tests final : public Test { const auto inf_plus_ga = inf + g_one.to_affine(); result.test_eq("inf + g (affine) == g", inf_plus_ga.to_affine().serialize(), g_bytes); - const auto g_neg_one = curve->mul_by_g(one.negate(), rng); + const auto g_neg_one = curve->mul_by_g(one.negate(), rng).to_affine(); const auto inf_from_g = g_one + g_neg_one; result.test_eq("g - g is infinity", inf_from_g.to_affine().serialize(), inf_bytes);