diff --git a/doc/api_ref/bls12_381.rst b/doc/api_ref/bls12_381.rst new file mode 100644 index 00000000000..ff02e22c3d9 --- /dev/null +++ b/doc/api_ref/bls12_381.rst @@ -0,0 +1,231 @@ +BLS12-381 +============================ + +.. versionadded:: 3.13.0 + +BLS12-381 is a pairing-friendly elliptic curve widely used for BLS signatures, +verifiable random functions, threshold cryptography, and zero knowledge proof systems. + +.. warning:: + + This is a low level mathematical primitive intended for implementing higher level + protocols. It does not by itself implement any signature or encryption scheme. + +The interface consists of value types for the scalar field, the two source groups G1 and +G2, and the pairing target group Gt, all declared in ``bls12_381.h`` within namespace +``Botan::BLS12_381``. The base field and extension tower arithmetic is internal to the +library. + +All operations are constant time unless otherwise documented; in particular scalar +multiplication via ``mul`` is safe for secret scalars. Operations documented as variable +time (such as ``msm_vartime``), the pairing, and hash-to-curve assume their inputs are +public values, which is the case in the standard usages (signature verification, hashing +a message to the curve). + +Serialization Formats +---------------------------- + +Group elements use the ZCash compressed point encoding: 48 bytes for G1, 96 bytes for G2. +The three high bits of the first byte are flag bits indicating (in order) compressed +encoding, the point at infinity, and the lexicographically larger of the two possible y +coordinates. The x coordinate follows as a big-endian integer; for G2 the ``c1`` +coefficient of the extension field element is encoded first. Deserialization rejects +any encoding that is non-canonical, not on the curve, or outside the prime order +subgroup. + +Scalars are encoded as 32 byte big-endian integers, field elements as 48 byte big-endian +integers; deserialization of values not fully reduced modulo the respective prime is +rejected. + +.. note:: + + Some implementations of BLS12-381 default to using little-endian encoding of scalars. + +.. note:: + + There is no standardized serialization for elements of Gt. ``Gt::serialize`` + encodes the twelve field coefficients of the underlying extension field + element in a fixed order. This encoding is stable across versions of the + library but is not guaranteed to match the encoding of any other + implementation. Note also that while all correct implementations will agree + on whether a pairing product equals the identity, the exact value of that + pairing output may vary depending on implementation choices (eg using + an alternative field tower) + +Scalars +---------------------------- + +.. cpp:class:: Botan::BLS12_381::Scalar + + An integer modulo the group order r. Default constructed as zero. Since scalars are + commonly secret keys, the value is zeroized on destruction, and moving from a + ``Scalar`` zeroizes the moved-from value. + + .. cpp:function:: static Scalar zero() + .. cpp:function:: static Scalar one() + .. cpp:function:: static Scalar from_u32(uint32_t v) + + .. cpp:function:: static std::optional deserialize(std::span bytes) + + Accepts exactly 32 bytes encoding a big-endian integer smaller than r. + + .. cpp:function:: static Scalar from_bytes_wide(std::span bytes) + + Reduces a 64 byte big-endian integer modulo r, suitable for deriving + uniformly distributed scalars from an RNG or hash. + + .. cpp:function:: static Scalar hash(std::span input, std::span dst) + + Hash an input to a uniformly distributed scalar, using the hash_to_field + construction of RFC 9380 (``expand_message_xmd`` with SHA-256 followed by wide + reduction). Useful for example for deriving Fiat-Shamir challenges. The domain + separation tag ``dst`` distinguishes different uses of the hash; see RFC 9380 + section 3.1 for the requirements. An empty tag is rejected with + ``Invalid_Argument``; tags longer than 255 bytes are replaced by an intermediate + hash, following RFC 9380 section 5.3.3. + + .. cpp:function:: std::array serialize() const + + Arithmetic is available via ``add``, ``sub``, ``mul``, ``square``, ``negate`` and + ``invert`` (the inverse of zero is zero), as well as the usual overloaded operators. + +Group Elements +---------------------------- + +.. cpp:class:: Botan::BLS12_381::G1Affine + + A point of the prime order group G1 in affine form, used for serialization and as + pairing input. + + .. cpp:function:: static G1Affine generator() + .. cpp:function:: static G1Affine identity() + + .. cpp:function:: static std::optional deserialize(std::span bytes) + + Only the 48 byte compressed encoding is accepted. Points outside the prime + order subgroup are rejected. + + .. cpp:function:: std::array serialize() const + .. cpp:function:: bool is_identity() const + +.. cpp:class:: Botan::BLS12_381::G1Projective + + A point of G1 in projective form, used for group arithmetic. Default + constructed as the identity element. + + .. cpp:function:: static G1Projective generator() + .. cpp:function:: static G1Projective identity() + .. cpp:function:: static G1Projective from_affine(const G1Affine& affine) + .. cpp:function:: G1Affine to_affine() const + + .. cpp:function:: static std::vector to_affine_batch(std::span points) + + Equivalent to calling ``to_affine`` on each point, but much faster, since a + single field inversion is shared across the batch; always prefer this when + converting more than one point. + + .. cpp:function:: G1Projective add(const G1Projective& other) const + .. cpp:function:: G1Projective add_mixed(const G1Affine& other) const + .. cpp:function:: G1Projective negate() const + + .. cpp:function:: G1Projective mul(const Scalar& scalar) const + + Constant time scalar multiplication, safe for secret scalars. + + .. cpp:function:: static G1Projective mul2(const G1Projective& p, const Scalar& a, \ + const G1Projective& q, const Scalar& b) + + Compute ``a*p + b*q`` in constant time, sharing one doubling chain; notably faster + than composing ``mul`` and ``add``. Being constant time it is safe for secret + scalars, for example when computing a Pedersen commitment. + + .. cpp:function:: static G1Projective mul2_vartime(const G1Projective& p, const Scalar& a, \ + const G1Projective& q, const Scalar& b) + + Compute ``a*p + b*q``, faster than ``mul2``. + + .. warning:: + + Runs in variable time and must only be used with public inputs, such as + verification equations. + + .. cpp:function:: static G1Projective msm_vartime(std::span points, \ + std::span scalars) + + Multiscalar multiplication, returning the sum of ``scalars[i] * points[i]``. + Above a certain number of terms this will use Pippenger's algorithm. Below that + size, instead a chain of 2-ary multiplications will be used. The algorithm choice + will in any event always be superior to chaining individual multiplications and + additions. + + .. warning:: + + Runs in variable time and must only be used with public inputs. + + .. cpp:function:: static G1Projective hash_to_curve_ro(std::span input, \ + std::span dst) + + Hash to curve following RFC 9380, using the suite ``BLS12381G1_XMD:SHA-256_SSWU_RO_``. + The domain separation tag ``dst`` distinguishes different uses of the hash; see + RFC 9380 section 3.1 for the requirements. An empty tag is rejected with + ``Invalid_Argument``; tags longer than 255 bytes are replaced by an intermediate + hash, following RFC 9380 section 5.3.3. + + .. cpp:function:: static G1Projective hash_to_curve_nu(std::span input, \ + std::span dst) + + The nonuniform (``encode_to_curve``) variant, using the suite ``BLS12381G1_XMD:SHA-256_SSWU_NU_``. + Faster, but the output is distinguishable from a uniformly random group element; + use ``hash_to_curve_ro`` unless the protocol explicitly calls for the nonuniform + encoding. + +.. cpp:class:: Botan::BLS12_381::G2Affine + +.. cpp:class:: Botan::BLS12_381::G2Projective + + The group G2, defined over the quadratic extension field, with the same interface as + the G1 types. Compressed points are 96 bytes, and the hash to curve functions implement + the suites ``BLS12381G2_XMD:SHA-256_SSWU_RO_`` and ``BLS12381G2_XMD:SHA-256_SSWU_NU_``. + +The Pairing +---------------------------- + +.. cpp:class:: Botan::BLS12_381::Gt + + An element of the pairing target group, a prime order subgroup of the multiplicative + group of Fp12. Elements are created only by the pairing functions; there is no + deserialization. + + .. cpp:function:: static Gt identity() + .. cpp:function:: bool is_identity() const + .. cpp:function:: bool operator==(const Gt& other) const + .. cpp:function:: std::array serialize() const + + Note that the serialization format of Gt is not standardized for BLS12-381, and + the encoding may differ between implementations. + + .. cpp:function:: Gt pairing(const G1Affine& p, const G2Affine& q) + + Compute the optimal ate pairing ``e(p, q)``. If either input is the identity + element the result is the identity. + + .. cpp:function:: Gt multi_pairing(std::span p, std::span q) + + Compute the product of pairings ``e(p[0], q[0]) * e(p[1], q[1]) * ...``, sharing + one final exponentiation; always prefer this over multiplying individual pairing + results. The empty product is the identity. Throws ``Invalid_Argument`` if the + spans have different lengths. + + Pairing equations of the form ``e(a, b) == e(c, d)`` should be checked by testing + ``multi_pairing({a, -c}, {b, d}).is_identity()``, which is both faster and the + standard formulation. + +Example +---------------------------- + +The following example implements the operations of a BLS signature (the +minimal-pubkey-size variant of draft-irtf-cfrg-bls-signature, with public keys in G1 and +signatures in G2): key generation, signing, verification, and signature aggregation. + +.. literalinclude:: /../src/examples/bls_signature.cpp + :language: cpp diff --git a/doc/api_ref/contents.rst b/doc/api_ref/contents.rst index a75bd8f46c3..387601f5312 100644 --- a/doc/api_ref/contents.rst +++ b/doc/api_ref/contents.rst @@ -31,6 +31,7 @@ API Reference tss ec_group ecc + bls12_381 compression providers pkcs11 diff --git a/src/cli/perf_bls12_381.cpp b/src/cli/perf_bls12_381.cpp new file mode 100644 index 00000000000..53a600fd1dd --- /dev/null +++ b/src/cli/perf_bls12_381.cpp @@ -0,0 +1,149 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_BLS12_381) + #include + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_BLS12_381) + +namespace { + +Botan::BLS12_381::Scalar random_bls_scalar(Botan::RandomNumberGenerator& rng) { + std::array buf{}; + rng.randomize(buf); + return Botan::BLS12_381::Scalar::from_bytes_wide(buf); +} + +class PerfTest_Bls12_381 final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto run = config.runtime(); + auto& rng = config.rng(); + + using namespace Botan::BLS12_381; + + const auto g1 = G1Projective::generator(); + const auto g2 = G2Projective::generator(); + + auto g1_mul_timer = config.make_timer("BLS12-381 G1 mul"); + while(g1_mul_timer->under(run)) { + const auto k = random_bls_scalar(rng); + g1_mul_timer->run([&]() { return g1.mul(k); }); + } + config.record_result(*g1_mul_timer); + + auto g2_mul_timer = config.make_timer("BLS12-381 G2 mul"); + while(g2_mul_timer->under(run)) { + const auto k = random_bls_scalar(rng); + g2_mul_timer->run([&]() { return g2.mul(k); }); + } + config.record_result(*g2_mul_timer); + + auto g1_mul2_timer = config.make_timer("BLS12-381 G1 mul2"); + auto g1_mul2_vt_timer = config.make_timer("BLS12-381 G1 mul2_vartime"); + while(g1_mul2_timer->under(run)) { + const auto p = g1.mul(random_bls_scalar(rng)); + const auto q = g1.mul(random_bls_scalar(rng)); + const auto a = random_bls_scalar(rng); + const auto b = random_bls_scalar(rng); + g1_mul2_timer->run([&]() { return G1Projective::mul2(p, a, q, b); }); + g1_mul2_vt_timer->run([&]() { return G1Projective::mul2_vartime(p, a, q, b); }); + } + config.record_result(*g1_mul2_timer); + config.record_result(*g1_mul2_vt_timer); + + auto g1_deser_timer = config.make_timer("BLS12-381 G1 deserialize"); + while(g1_deser_timer->under(run)) { + const auto bytes = g1.mul(random_bls_scalar(rng)).to_affine().serialize(); + g1_deser_timer->run([&]() { return G1Affine::deserialize(bytes); }); + } + config.record_result(*g1_deser_timer); + + auto g2_deser_timer = config.make_timer("BLS12-381 G2 deserialize"); + while(g2_deser_timer->under(run)) { + const auto bytes = g2.mul(random_bls_scalar(rng)).to_affine().serialize(); + g2_deser_timer->run([&]() { return G2Affine::deserialize(bytes); }); + } + config.record_result(*g2_deser_timer); + + auto g1_h2c_timer = config.make_timer("BLS12-381 G1 hash to curve"); + while(g1_h2c_timer->under(run)) { + std::array input{}; + rng.randomize(input); + const auto dst = std::span{input}.first(16); + g1_h2c_timer->run([&]() { return G1Projective::hash_to_curve_ro(input, dst); }); + } + config.record_result(*g1_h2c_timer); + + auto g2_h2c_timer = config.make_timer("BLS12-381 G2 hash to curve"); + while(g2_h2c_timer->under(run)) { + std::array input{}; + rng.randomize(input); + const auto dst = std::span{input}.first(16); + g2_h2c_timer->run([&]() { return G2Projective::hash_to_curve_ro(input, dst); }); + } + config.record_result(*g2_h2c_timer); + + auto pairing_timer = config.make_timer("BLS12-381 pairing"); + while(pairing_timer->under(run)) { + const auto a = g1.mul(random_bls_scalar(rng)).to_affine(); + const auto b = g2.mul(random_bls_scalar(rng)).to_affine(); + pairing_timer->run([&]() { return Gt::pairing(a, b); }); + } + config.record_result(*pairing_timer); + + for(const size_t n : {2, 8}) { + std::vector ps_proj; + std::vector qs_proj; + for(size_t i = 0; i != n; ++i) { + ps_proj.push_back(g1.mul(random_bls_scalar(rng))); + qs_proj.push_back(g2.mul(random_bls_scalar(rng))); + } + const auto ps = G1Projective::to_affine_batch(ps_proj); + const auto qs = G2Projective::to_affine_batch(qs_proj); + + auto mp_timer = config.make_timer("BLS12-381 multi-pairing (" + std::to_string(n) + ")"); + while(mp_timer->under(run)) { + mp_timer->run([&]() { return Gt::multi_pairing(ps, qs); }); + } + config.record_result(*mp_timer); + } + + for(const size_t n : {4, 32, 256}) { + std::vector proj; + proj.reserve(n); + for(size_t i = 0; i != n; ++i) { + proj.push_back(g1.mul(random_bls_scalar(rng))); + } + const auto points = G1Projective::to_affine_batch(proj); + + auto msm_timer = config.make_timer("BLS12-381 G1 MSM " + std::to_string(n) + " (per point)", n); + while(msm_timer->under(run)) { + std::vector scalars; + scalars.reserve(n); + for(size_t i = 0; i != n; ++i) { + scalars.push_back(random_bls_scalar(rng)); + } + msm_timer->run([&]() { return G1Projective::msm_vartime(points, scalars); }); + } + config.record_result(*msm_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("bls12_381", PerfTest_Bls12_381); + +} // namespace + +#endif + +} // namespace Botan_CLI diff --git a/src/examples/bls_signature.cpp b/src/examples/bls_signature.cpp new file mode 100644 index 00000000000..616223c0a4f --- /dev/null +++ b/src/examples/bls_signature.cpp @@ -0,0 +1,200 @@ +/* +* BLS signatures built from the low level BLS12-381 interface. +* +* This implements the scheme of draft-irtf-cfrg-bls-signature in the +* minimal-pubkey-size setting (public keys in G1, signatures in G2), +* specifically the "basic" ciphersuite +* BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_. +* +* The basic scheme is only safe for aggregation if all messages in an +* aggregate are distinct; see section 3.1 of the draft. The +* message-augmentation and proof-of-possession variants build on the +* same operations shown here. +*/ + +#include +#include + +#include +#include +#include +#include +#include + +namespace { + +using namespace Botan::BLS12_381; + +const std::string_view BLS_SIG_DST = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"; + +std::span as_bytes(std::string_view s) { + return {reinterpret_cast(s.data()), s.size()}; +} + +G2Projective hash_message(std::string_view msg) { + return G2Projective::hash_to_curve_ro(as_bytes(msg), as_bytes(BLS_SIG_DST)); +} + +class BlsPrivateKey { + public: + /* + * Generate a signing key. + * + * Note that section 2.3 of the draft specifies a deterministic + * HKDF based derivation from a seed; if interoperable key + * derivation matters for your application, implement KeyGen with + * Botan's HKDF. Here fresh randomness is used directly. + */ + static BlsPrivateKey generate(Botan::RandomNumberGenerator& rng) { + for(;;) { + std::array seed{}; + rng.randomize(seed); + const auto sk = Scalar::from_bytes_wide(seed); + + // The secret key must be nonzero (probability 2^-255) + if(sk.serialize() != Scalar::zero().serialize()) { + return BlsPrivateKey(sk); + } + } + } + + // SkToPk: the public key is sk*g1, serialized to 48 bytes + std::array public_key() const { + return G1Projective::generator().mul(m_sk).to_affine().serialize(); + } + + // CoreSign: the signature is sk*H(msg), serialized to 96 bytes + std::array sign(std::string_view msg) const { + return hash_message(msg).mul(m_sk).to_affine().serialize(); + } + + private: + explicit BlsPrivateKey(const Scalar& sk) : m_sk(sk) {} + + Scalar m_sk; +}; + +/* +* CoreVerify: check that e(pk, H(msg)) == e(g1, sig). +* +* Deserialization rejects any point outside the prime order subgroup, +* which the scheme requires. The pairing equation is rearranged as +* e(pk, H(msg)) * e(-g1, sig) == 1 so that a single product of pairings +* (with its one shared final exponentiation) suffices. +*/ +bool bls_verify(std::span pk_bytes, std::string_view msg, std::span sig_bytes) { + const auto pk = G1Affine::deserialize(pk_bytes); + const auto sig = G2Affine::deserialize(sig_bytes); + if(!pk || !sig || pk->is_identity()) { + return false; + } + + const auto neg_g1 = G1Projective::generator().negate().to_affine(); + + const std::vector ps{*pk, neg_g1}; + const std::vector qs{hash_message(msg).to_affine(), *sig}; + return Gt::multi_pairing(ps, qs).is_identity(); +} + +// Aggregate: signatures combine by group addition in G2 +std::optional> bls_aggregate( + std::span> sigs) { + auto agg = G2Projective::identity(); + for(const auto& sig_bytes : sigs) { + const auto sig = G2Affine::deserialize(sig_bytes); + if(!sig) { + return std::nullopt; + } + agg = agg.add_mixed(*sig); + } + return agg.to_affine().serialize(); +} + +/* +* CoreAggregateVerify: check e(pk_1, H(msg_1)) * ... * e(pk_n, H(msg_n)) == e(g1, sig) +* via one product of n+1 pairings. +* +* Warning: in the basic scheme the messages MUST be pairwise distinct, +* otherwise the aggregate is forgeable by a rogue key attack. This +* example does not check distinctness. +*/ +bool bls_aggregate_verify(std::span> pks, + std::span msgs, + std::span agg_sig_bytes) { + if(pks.size() != msgs.size() || pks.empty()) { + return false; + } + + const auto agg_sig = G2Affine::deserialize(agg_sig_bytes); + if(!agg_sig) { + return false; + } + + std::vector ps; + std::vector hashed; + for(size_t i = 0; i != pks.size(); ++i) { + const auto pk = G1Affine::deserialize(pks[i]); + if(!pk || pk->is_identity()) { + return false; + } + ps.push_back(*pk); + hashed.push_back(hash_message(msgs[i])); + } + + ps.push_back(G1Projective::generator().negate().to_affine()); + + // A single shared inversion converts all the hashed points at once + auto qs = G2Projective::to_affine_batch(hashed); + qs.push_back(*agg_sig); + + return Gt::multi_pairing(ps, qs).is_identity(); +} + +} // namespace + +int main() { + Botan::AutoSeeded_RNG rng; + + bool all_ok = true; + auto check = [&](const char* what, bool ok, bool expected) { + std::cout << what << ": " << (ok ? "valid" : "invalid") << "\n"; + all_ok = all_ok && (ok == expected); + }; + + // Single signer + const auto key = BlsPrivateKey::generate(rng); + const auto pk = key.public_key(); + + const std::string_view message = "This is a tasty burger!"; + const auto sig = key.sign(message); + + check("Signature", bls_verify(pk, message, sig), true); + check("Signature on modified message", bls_verify(pk, "This is a nasty burger!", sig), false); + + // Aggregation: three signers, three (distinct!) messages, one signature + const std::vector messages{ + "message to the first signer", "message to the second signer", "message to the third signer"}; + + std::vector> pks; + std::vector> sigs; + for(const auto msg : messages) { + const auto k = BlsPrivateKey::generate(rng); + const auto k_pk = k.public_key(); + pks.emplace_back(k_pk.begin(), k_pk.end()); + sigs.push_back(k.sign(msg)); + } + + const auto agg_sig = bls_aggregate(sigs); + if(!agg_sig) { + std::cout << "Aggregation failed\n"; + return 1; + } + + check("Aggregate signature", bls_aggregate_verify(pks, messages, *agg_sig), true); + + auto swapped = messages; + std::swap(swapped[0], swapped[1]); + check("Aggregate with swapped messages", bls_aggregate_verify(pks, swapped, *agg_sig), false); + + return all_ok ? 0 : 1; +} diff --git a/src/lib/kdf/xmd/xmd.cpp b/src/lib/kdf/xmd/xmd.cpp index 4593ee801fe..e31ec754d90 100644 --- a/src/lib/kdf/xmd/xmd.cpp +++ b/src/lib/kdf/xmd/xmd.cpp @@ -18,9 +18,23 @@ void expand_message_xmd(std::string_view hash_fn, std::span output, std::span input, std::span domain_sep) { + auto hash = HashFunction::create_or_throw(hash_fn); + + std::vector hashed_domain_sep; if(domain_sep.size() > 0xFF) { - // RFC 9380 has a specification for handling this - throw Not_Implemented("XMD does not currently implement oversize DST handling"); + /* RFC 9380 section 5.3.3: "If applications require a domain + * separation tag longer than 255 bytes, e.g., because of + * requirements imposed by an invoking protocol, implementors MUST + * compute a short domain separation tag by hashing, as follows: + * + * For expand_message_xmd using hash function H, DST is computed as + * + * DST = H("H2C-OVERSIZE-DST-" || a_very_long_DST)" + */ + hash->update("H2C-OVERSIZE-DST-"); + hash->update(domain_sep); + hashed_domain_sep = hash->final_stdvec(); + domain_sep = hashed_domain_sep; } if(domain_sep.empty()) { @@ -29,8 +43,6 @@ void expand_message_xmd(std::string_view hash_fn, } const uint8_t domain_sep_len = static_cast(domain_sep.size()); - - auto hash = HashFunction::create_or_throw(hash_fn); const size_t block_size = hash->hash_block_size(); if(block_size == 0) { throw Invalid_Argument(fmt("expand_message_xmd cannot be used with {}", hash_fn)); diff --git a/src/lib/math/bls12_381/bls12_381.h b/src/lib/math/bls12_381/bls12_381.h new file mode 100644 index 00000000000..e42e50397a7 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381.h @@ -0,0 +1,536 @@ +/* +* (C) 2024,2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BLS12_381_H_ +#define BOTAN_BLS12_381_H_ + +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +// The field element and extension tower types are internal; functions +// naming these types are reserved for internal use and not covered by SemVer +class FieldElement; +class FieldElement2; +class Fp12; + +/** +* A BLS12-381 scalar +* +* Integer modulo 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +*/ +class BOTAN_PUBLIC_API(3, 13) Scalar final { + public: + static constexpr size_t BITS = 255; + static constexpr size_t BYTES = (BITS + 7) / 8; + static constexpr size_t N = (BYTES + sizeof(word) - 1) / sizeof(word); + + static_assert(BYTES == N * sizeof(word)); + + // Default zero initialized + constexpr Scalar() : m_val({}) {} + + Scalar(const Scalar& other) = default; + Scalar& operator=(const Scalar& other) = default; + + /** + * Scalars are commonly secrets, so they are zeroized on + * destruction, and moving from a Scalar zeroizes the source + */ + Scalar(Scalar&& other) noexcept : m_val(other.m_val) { other.wipe(); } + + Scalar& operator=(Scalar&& other) noexcept { + if(this != &other) { + m_val = other.m_val; + other.wipe(); + } + return *this; + } + + ~Scalar() { this->wipe(); } + + static Scalar from_u32(uint32_t v); + + static Scalar zero() { return Scalar(); } + + static Scalar one(); + + /** + * Deserialize a scalar from a 32-byte long big-endian encoding + * + * Returns nullopt unless the encoding is a canonical integer in the + * range [1, r). In particular the zero scalar is rejected, since a zero + * scalar is almost always a mistake; this matches EC_Scalar::deserialize. + * + * @note that some implementations of BLS12-381 use little-endian scalar encodings + */ + static std::optional deserialize(std::span bytes); + + static Scalar from_bytes_wide(std::span bytes); + + /** + * Hash an input to a scalar + * + * Uses the hash_to_field construction of RFC 9380, namely expand_message_xmd with + * SHA-256 followed by wide reduction. The distribution of the result is uniform + * (negligibly biased). The domain separation tag dst distinguishes different uses + * of the hash; see RFC 9380 section 3.1 for the requirements. + */ + static Scalar hash(std::span input, std::span dst); + + void serialize_to(std::span bytes) const; + + std::array serialize() const { + std::array buf{}; + this->serialize_to(buf); + return buf; + } + + Scalar add(const Scalar& x) const; + + Scalar sub(const Scalar& x) const; + + Scalar mul(const Scalar& x) const; + + Scalar& operator+=(const Scalar& x) { + (*this) = this->add(x); + return (*this); + } + + Scalar& operator-=(const Scalar& x) { + (*this) = this->sub(x); + return (*this); + } + + Scalar& operator*=(const Scalar& x) { + (*this) = this->mul(x); + return (*this); + } + + Scalar negate() const; + + Scalar invert() const; + + Scalar square() const; + + private: + // Squaring in place + void square_n(size_t n); + + void wipe(); + + constexpr const std::array& value() const { return m_val; } + + constexpr const word* data() const { return m_val.data(); } + + static Scalar from_words(std::array v); + + explicit constexpr Scalar(std::array v) : m_val(v) {} + + std::array m_val; +}; + +inline Scalar operator+(const Scalar& a, const Scalar& b) { + return a.add(b); +} + +inline Scalar operator-(const Scalar& a, const Scalar& b) { + return a.sub(b); +} + +inline Scalar operator*(const Scalar& a, const Scalar& b) { + return a.mul(b); +} + +class BOTAN_PUBLIC_API(3, 13) G1Affine final { + public: + static constexpr size_t BYTES = 48; + + /** + * Return the G1 identity element + */ + static G1Affine identity(); + + /** + * Return the G1 standard group generator + */ + static G1Affine generator(); + + /** + * Point deserialization + * + * Only compressed point encoding is supported. + * + * This rejects points not in the prime order subgroup. + */ + static std::optional deserialize(std::span bytes); + + /** + * Point serialization + * + * Only compressed point encoding is supported. + */ + std::array serialize() const; + + /** + * Check if this point is the identity element + */ + bool is_identity() const; + + /** + * Access the coordinates directly; the values are meaningless if + * the point is the identity element. + * + * Reserved for internal use, not covered by SemVer. + */ + FieldElement _x() const; + FieldElement _y() const; + + private: + friend class G1Projective; + + G1Affine(const FieldElement& x, const FieldElement& y, uint32_t infinity); + + static constexpr size_t FE_WORDS = 48 / sizeof(word); + + std::array m_x; + std::array m_y; + uint32_t m_infinity; +}; + +class BOTAN_PUBLIC_API(3, 13) G1Projective final { + public: + /** + * Default constructed as the identity element + */ + G1Projective(); + + static G1Projective from_affine(const G1Affine& affine); + + static G1Projective identity() { return G1Projective(); } + + static G1Projective generator(); + + G1Affine to_affine() const; + + G1Projective negate() const; + + G1Projective add(const G1Projective& other) const; + + G1Projective add_mixed(const G1Affine& other) const; + + G1Projective mul(const Scalar& scalar) const; + + /** + * Compute a*p + b*q in constant time + * + * Faster than composing mul() and add(), and safe for secret + * scalars, as arise for example in Pedersen commitments. + */ + static G1Projective mul2(const G1Projective& p, const Scalar& a, const G1Projective& q, const Scalar& b); + + /** + * Compute a*p + b*q + * + * Warning: this function runs in variable time and must be used + * only with public inputs, such as signature verification. Use + * mul2() with secret scalars. + */ + static G1Projective mul2_vartime(const G1Projective& p, const Scalar& a, const G1Projective& q, const Scalar& b); + + /** + * Check if this point is the identity element + */ + bool is_identity() const; + + /** + * Hash to curve (RFC 9380), suite BLS12381G1_XMD:SHA-256_SSWU_RO_ + */ + static G1Projective hash_to_curve_ro(std::span input, std::span dst); + + /** + * Nonuniform encoding to curve (RFC 9380 encode_to_curve), + * suite BLS12381G1_XMD:SHA-256_SSWU_NU_ + */ + static G1Projective hash_to_curve_nu(std::span input, std::span dst); + + /** + * Multiscalar multiplication, the sum of scalars[i]*points[i] + * + * The empty sum is the identity element. Throws Invalid_Argument + * if the spans are of unequal length. + * + * Warning: this function runs in variable time and must be used + * only with public inputs, such as signature verification. Use + * mul() with secret scalars. + */ + static G1Projective msm_vartime(std::span points, std::span scalars); + + /** + * Convert a batch of points to affine + * + * Equivalent to calling to_affine on each point, but much faster, + * since a single field inversion is shared across the batch. + */ + static std::vector to_affine_batch(std::span points); + + /** + * The input is assumed to be a point on the curve, though possibly + * outside the prime order subgroup. + * + * Reserved for internal use, not covered by SemVer. + */ + static G1Projective _unchecked_from_affine_coords(const FieldElement& x, const FieldElement& y); + + private: + G1Projective(const FieldElement& x, const FieldElement& y, const FieldElement& z); + + G1Projective dbl() const; + + friend class G1Affine; + + template + friend class PointMul; + + static constexpr size_t FE_WORDS = 48 / sizeof(word); + + std::array m_x; + std::array m_y; + std::array m_z; +}; + +class BOTAN_PUBLIC_API(3, 13) G2Affine final { + public: + static constexpr size_t BYTES = 96; + + /** + * Return the G2 identity element + */ + static G2Affine identity(); + + /** + * Return the G2 standard group generator + */ + static G2Affine generator(); + + /** + * Point deserialization + * + * Only compressed point encoding is supported. + * + * This rejects points not in the prime order subgroup. + */ + static std::optional deserialize(std::span bytes); + + /** + * Point serialization + * + * Only compressed point encoding is supported. + */ + std::array serialize() const; + + /** + * Check if this point is the identity element + */ + bool is_identity() const; + + /** + * Access the coordinates directly; the values are meaningless if + * the point is the identity element. + * + * Reserved for internal use, not covered by SemVer. + */ + FieldElement2 _x() const; + FieldElement2 _y() const; + + private: + friend class G2Projective; + + G2Affine(const FieldElement2& x, const FieldElement2& y, uint32_t infinity); + + static constexpr size_t FE2_WORDS = 96 / sizeof(word); + + std::array m_x; + std::array m_y; + uint32_t m_infinity; +}; + +class BOTAN_PUBLIC_API(3, 13) G2Projective final { + public: + /** + * Default constructed as the identity element + */ + G2Projective(); + + static G2Projective from_affine(const G2Affine& affine); + + static G2Projective identity() { return G2Projective(); } + + static G2Projective generator(); + + G2Affine to_affine() const; + + G2Projective negate() const; + + G2Projective add(const G2Projective& other) const; + + G2Projective add_mixed(const G2Affine& other) const; + + G2Projective mul(const Scalar& scalar) const; + + /** + * Compute a*p + b*q in constant time + * + * Faster than composing mul() and add(), and safe for secret + * scalars, as arise for example in Pedersen commitments. + */ + static G2Projective mul2(const G2Projective& p, const Scalar& a, const G2Projective& q, const Scalar& b); + + /** + * Compute a*p + b*q + * + * Warning: this function runs in variable time and must be used + * only with public inputs, such as signature verification. Use + * mul2() with secret scalars. + */ + static G2Projective mul2_vartime(const G2Projective& p, const Scalar& a, const G2Projective& q, const Scalar& b); + + /** + * Check if this point is the identity element + */ + bool is_identity() const; + + /** + * Hash to curve (RFC 9380), suite BLS12381G2_XMD:SHA-256_SSWU_RO_ + */ + static G2Projective hash_to_curve_ro(std::span input, std::span dst); + + /** + * Nonuniform encoding to curve (RFC 9380 encode_to_curve), + * suite BLS12381G2_XMD:SHA-256_SSWU_NU_ + */ + static G2Projective hash_to_curve_nu(std::span input, std::span dst); + + /** + * Multiscalar multiplication, the sum of scalars[i]*points[i] + * + * The empty sum is the identity element. Throws Invalid_Argument + * if the spans are of unequal length. + * + * Warning: this function runs in variable time and must be used + * only with public inputs, such as signature verification. Use + * mul() with secret scalars. + */ + static G2Projective msm_vartime(std::span points, std::span scalars); + + /** + * Convert a batch of points to affine + * + * Equivalent to calling to_affine on each point, but much faster, + * since a single field inversion is shared across the batch. + */ + static std::vector to_affine_batch(std::span points); + + /** + * The input is assumed to be a point on the curve, though possibly + * outside the prime order subgroup. + * + * Reserved for internal use, not covered by SemVer. + */ + static G2Projective _unchecked_from_affine_coords(const FieldElement2& x, const FieldElement2& y); + + private: + G2Projective(const FieldElement2& x, const FieldElement2& y, const FieldElement2& z); + + G2Projective dbl() const; + + /** + * The untwist-Frobenius-twist endomorphism, and its square + */ + G2Projective psi() const; + G2Projective psi2() const; + + /** + * Cofactor clearing for hash to curve + */ + G2Projective clear_cofactor() const; + + friend class G2Affine; + + template + friend class PointMul; + + static constexpr size_t FE2_WORDS = 96 / sizeof(word); + + std::array m_x; + std::array m_y; + std::array m_z; +}; + +/** +* An element of the pairing target group, a subgroup of Fp12* +* +* Elements are members of the prime order subgroup by construction; +* there is no deserialization. +*/ +class BOTAN_PUBLIC_API(3, 13) Gt final { + public: + static constexpr size_t BYTES = 576; + + /** + * Compute the optimal ate pairing e(p, q) + */ + static Gt pairing(const G1Affine& p, const G2Affine& q); + + /** + * Compute the product of pairings prod_i e(p[i], q[i]) + * + * More efficient than multiplying individual pairings, since the final + * exponentiation is shared. The empty product yields the identity. + * + * Throws Invalid_Argument if the spans are of unequal length. + */ + static Gt multi_pairing(std::span p, std::span q); + + /** + * Return the Gt identity element + */ + static Gt identity(); + + /** + * Check if this element is the identity + */ + bool is_identity() const; + + /** + * Constant time equality + */ + bool operator==(const Gt& other) const; + + /** + * Serialization of the underlying Fp12 element; the coefficients + * are encoded in the order c0.c0.c0, c0.c0.c1, ..., c1.c2.c1 with + * each Fp coefficient as 48 big-endian bytes + */ + std::array serialize() const; + + private: + explicit Gt(const Fp12& v); + + Fp12 _to_fp12() const; + + static constexpr size_t WORDS = 12 * (48 / sizeof(word)); + + std::array m_coeffs; +}; + +} // namespace Botan::BLS12_381 + +#endif diff --git a/src/lib/math/bls12_381/bls12_381_fe.cpp b/src/lib/math/bls12_381/bls12_381_fe.cpp new file mode 100644 index 00000000000..c6c23e96a2b --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_fe.cpp @@ -0,0 +1,329 @@ +/* +* (C) 2024,2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include + +namespace Botan::BLS12_381 { + +namespace { + +constexpr auto FE_P = hex_to_words( + "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"); + +// R1 = 2**384 % p +constexpr auto FE_R1 = hex_to_words( + "15f65ec3fa80e4935c071a97a256ec6d77ce5853705257455f48985753c758baebf4000bc40c0002760900000002fffd"); + +// R2 = (R1**2) % p +constexpr auto FE_R2 = hex_to_words( + "11988fe592cae3aa9a793e85b519952d67eb88a9939d83c08de5476c4c95b6d50a76e6a609d104f1f4df1f341c341746"); + +// R3 = (R1*R2) % p +constexpr auto FE_R3 = hex_to_words( + "aa6346091755d4d2512d4356572472834c04e5e921e17619a53352a615e29dd315f831e03a7adf8ed48ac6bd94ca1e0"); + +constexpr word FE_P_DASH = monty_inverse(FE_P[0]); + +// (p-1)/2, for the lexicographic comparison +constexpr auto FE_P_HALF = [] { + auto x = FE_P; + shift_right<1>(x); + return x; +}(); + +// (p+1)/4; since p == 3 (mod 4) this is (p >> 2) + 1, and the increment +// cannot overflow +constexpr auto FE_SQRT_EXP = [] { + auto x = FE_P; + shift_right<2>(x); + x[0] += 1; + return x; +}(); + +std::array fe_redc(const std::array& v) { + std::array r{}; + std::array ws{}; + bigint_monty_redc(r.data(), v.data(), FE_P.data(), FE_P.size(), FE_P_DASH, ws.data(), ws.size()); + return r; +} + +std::array fe_to_rep(std::array v) { + std::array z{}; + comba_mul(z.data(), v.data(), FE_R2.data()); + return fe_redc(z); +} + +std::array fe_wide_to_rep(const std::array& v) { + auto redc_v = fe_redc(v); + std::array z{}; + comba_mul(z.data(), redc_v.data(), FE_R3.data()); + return fe_redc(z); +} + +std::array fe_from_rep(const std::array& v) { + std::array z{}; + copy_mem(z.data(), v.data(), v.size()); + return fe_redc(z); +} + +} // namespace + +//static +FieldElement FieldElement::from_words(std::array v) { + return FieldElement(fe_to_rep(v)); +} + +//static +FieldElement FieldElement::from_u32(uint32_t v) { + std::array w{}; + w[0] = v; + return FieldElement::from_words(w); +} + +//static +FieldElement FieldElement::one() { + return FieldElement(FE_R1); +} + +std::optional FieldElement::deserialize(std::span bytes) { + if(bytes.size() != FieldElement::BYTES) { + return {}; + } + + const auto words = bytes_to_words(bytes.first()); + + if(!bigint_ct_is_lt(words.data(), N, FE_P.data(), N).as_bool()) { + return {}; + } + + return FieldElement::from_words(words); +} + +FieldElement FieldElement::from_bytes_wide(std::span bytes) { + return FieldElement(fe_wide_to_rep(bytes_to_words(bytes))); +} + +void FieldElement::serialize_to(std::span bytes) const { + auto v = fe_from_rep(m_val); + std::reverse(v.begin(), v.end()); + store_be(bytes, v); +} + +FieldElement FieldElement::add(const FieldElement& other) const { + std::array t{}; + word carry = 0; + for(size_t i = 0; i != FieldElement::N; ++i) { + t[i] = word_add(m_val[i], other.m_val[i], &carry); + } + + std::array r{}; + bigint_monty_maybe_sub(r.data(), carry, t.data(), FE_P.data()); + return FieldElement(r); +} + +FieldElement FieldElement::sub(const FieldElement& other) const { + return this->add(other.negate()); +} + +FieldElement FieldElement::mul(const FieldElement& other) const { + std::array z{}; + comba_mul(z.data(), data(), other.data()); + return FieldElement(fe_redc(z)); +} + +FieldElement FieldElement::square() const { + std::array z{}; + comba_sqr(z.data(), data()); + return FieldElement(fe_redc(z)); +} + +void FieldElement::square_n(size_t n) { + std::array z{}; + for(size_t i = 0; i != n; ++i) { + comba_sqr(z.data(), this->data()); + m_val = fe_redc(z); + } +} + +FieldElement FieldElement::negate() const { + auto v_is_zero = CT::all_zeros(this->data(), N); + + std::array r{}; + bigint_sub3(r.data(), FE_P.data(), N, this->data(), N); + v_is_zero.if_set_zero_out(r.data(), N); + return FieldElement(r); +} + +CT::Choice FieldElement::is_zero() const { + return CT::all_zeros(this->data(), N).as_choice(); +} + +CT::Choice FieldElement::_is_lexicographically_largest() const { + const auto v = fe_from_rep(m_val); + return !bigint_ct_is_lt(v.data(), N, FE_P_HALF.data(), N, true).as_choice(); +} + +CT::Choice FieldElement::operator==(const FieldElement& other) const { + // The Montgomery representation of a reduced element is unique + return CT::is_equal(this->data(), other.data(), N).as_choice(); +} + +void FieldElement::_conditional_assign(CT::Choice cnd, const FieldElement& other) { + CT::conditional_assign_mem(cnd, m_val.data(), other.m_val.data(), N); +} + +std::optional FieldElement::sqrt() const { + // Since p == 3 (mod 4) any square root must be x^((p+1)/4) + auto r = FieldElement::one(); + for(size_t i = 0; i != N; ++i) { + const word w = FE_SQRT_EXP[N - 1 - i]; + for(size_t b = 0; b != WordInfo::bits; ++b) { + r = r.square(); + // The exponent is a public constant so this branch leaks nothing + if(((w >> (WordInfo::bits - 1 - b)) & 1) == 1) { + r = r * (*this); + } + } + } + + if((r.square() == (*this)).as_bool()) { + return r; + } else { + return {}; + } +} + +FieldElement FieldElement::invert() const { + // Addition chain for exponentiation to p - 2 + // Found using https://github.com/mmcloughlin/addchain + auto z = (*this).square(); + auto t3 = z.square(); + auto t10 = t3.square(); + auto t6 = t10 * (*this); + auto t8 = t6 * z; + auto t5 = t8 * z; + auto t24 = t3 * t5; + auto t1 = t6 * t8; + auto t17 = t10 * t24; + auto t9 = t17 * (*this); + auto t12 = t9.square(); + auto t4 = t12 * z; + auto t2 = t4 * (*this); + auto t22 = t12 * t17; + auto t14 = t22 * z; + auto t0 = t10 * t22; + auto t20 = t0 * t10; + auto t11 = t22 * t9; + auto t19 = t11 * z; + auto t16 = t20 * t9; + auto t7 = t16 * t3; + t12 *= t0; + t9 *= t7; + auto t15 = t9 * z; + z = t1 * t9; + auto t21 = t10 * z; + t4 *= t12; + t10 = t3 * t4; + t5 *= t10; + auto t18 = t1 * t10; + auto t13 = t24 * t5; + auto t23 = t13 * t3; + t3 = t1 * t18; + t5 = t1 * t13; + t1 *= t3; + auto t25 = t13 * t4; + t25.square_n(8); + t24 *= t25; + t24.square_n(11); + t24 *= t5; + t24.square_n(11); + t23 *= t24; + t23.square_n(8); + t23 *= t1; + t23.square_n(7); + t22 *= t23; + t22.square_n(9); + t22 *= t19; + t22.square_n(10); + t21 *= t22; + t21.square_n(7); + t20 *= t21; + t20.square_n(9); + t20 *= t7; + t20.square_n(6); + t20 *= t17; + t20.square_n(11); + t19 *= t20; + t19.square_n(9); + t19 *= t3; + t19.square_n(10); + t18 *= t19; + t18.square_n(6); + t17 *= t18; + t17.square_n(10); + t16 *= t17; + t16.square_n(9); + t15 *= t16; + t15.square_n(11); + t14 *= t15; + t14.square_n(10); + t13 *= t14; + t13.square_n(9); + t12 *= t13; + t12.square_n(9); + t12 *= t4; + t12.square_n(8); + t11 *= t12; + t11.square_n(10); + t10 *= t11; + t10.square_n(9); + t9 *= t10; + t9.square_n(12); + t9 *= t7; + t9.square_n(5); + t8 *= t9; + t8.square_n(11); + t7 *= t8; + t7.square_n(7); + t6 *= t7; + t6.square_n(13); + t5 *= t6; + t5.square_n(9); + t4 *= t5; + t4.square_n(8); + t4 *= t1; + t4.square_n(8); + t3 *= t4; + t3.square_n(11); + t3 *= z; + t3.square_n(8); + t3 *= t1; + t3.square_n(8); + t3 *= t1; + t3.square_n(6); + t2 *= t3; + t2.square_n(10); + t2 *= t1; + t2.square_n(9); + t2 *= t1; + t2.square_n(8); + t2 *= t1; + t2.square_n(8); + t2 *= t1; + t2.square_n(8); + t1 *= t2; + t1.square_n(7); + t0 *= t1; + t0.square_n(9); + z *= t0; + return z; +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_fields.h b/src/lib/math/bls12_381/bls12_381_fields.h new file mode 100644 index 00000000000..7d553967167 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_fields.h @@ -0,0 +1,278 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BLS12_381_FIELDS_H_ +#define BOTAN_BLS12_381_FIELDS_H_ + +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +/** +* A BLS12-381 field element +* +* Integer modulo 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +*/ +class BOTAN_TEST_API FieldElement final { + public: + static constexpr size_t BITS = 381; + static constexpr size_t BYTES = (BITS + 7) / 8; + static constexpr size_t N = (BYTES + sizeof(word) - 1) / sizeof(word); + + static_assert(BYTES == N * sizeof(word)); + + // Default zero initialized + constexpr FieldElement() : m_val({}) {} + + static FieldElement from_u32(uint32_t v); + + static FieldElement zero() { return FieldElement(); } + + static FieldElement one(); + + static std::optional deserialize(std::span bytes); + + static FieldElement from_bytes_wide(std::span bytes); + + CT::Choice is_zero() const; + + void serialize_to(std::span bytes) const; + + std::array serialize() const { + std::array buf{}; + this->serialize_to(buf); + return buf; + } + + FieldElement add(const FieldElement& x) const; + + FieldElement sub(const FieldElement& x) const; + + FieldElement mul(const FieldElement& x) const; + + FieldElement& operator+=(const FieldElement& x) { + (*this) = this->add(x); + return (*this); + } + + FieldElement& operator-=(const FieldElement& x) { + (*this) = this->sub(x); + return (*this); + } + + FieldElement& operator*=(const FieldElement& x) { + (*this) = this->mul(x); + return (*this); + } + + FieldElement negate() const; + + FieldElement invert() const; + + FieldElement square() const; + + /** + * Return the square root of this element, if one exists + * + * If x and -x are both square roots, which of the two is returned + * is unspecified. + */ + std::optional sqrt() const; + + /** + * Constant time equality + */ + CT::Choice operator==(const FieldElement& other) const; + + CT::Choice operator!=(const FieldElement& other) const { return !(*this == other); } + + /** + * Set iff this is larger than its negation + * + * Ie set if this is > (p-1)/2 + */ + CT::Choice _is_lexicographically_largest() const; + + /** + * The input is assumed to be valid and already in Montgomery representation + */ + static constexpr FieldElement _unchecked_from_words(std::array v) { + return FieldElement(v); + } + + /** + * The Montgomery representation words + */ + constexpr const std::array& _words() const { return m_val; } + + /** + * If cnd is set assign other to this, in constant time + */ + void _conditional_assign(CT::Choice cnd, const FieldElement& other); + + private: + // Squaring in place + void square_n(size_t n); + + constexpr const std::array& value() const { return m_val; } + + constexpr const word* data() const { return m_val.data(); } + + static FieldElement from_words(std::array v); + + explicit constexpr FieldElement(std::array v) : m_val(v) {} + + std::array m_val; +}; + +inline FieldElement operator+(const FieldElement& a, const FieldElement& b) { + return a.add(b); +} + +inline FieldElement operator-(const FieldElement& a, const FieldElement& b) { + return a.sub(b); +} + +inline FieldElement operator*(const FieldElement& a, const FieldElement& b) { + return a.mul(b); +} + +/** +* An element of the extension field Fp2 = Fp[u]/(u^2 + 1) +* +* Represented as c0 + c1*u +*/ +class BOTAN_TEST_API FieldElement2 final { + public: + static constexpr size_t BYTES = 2 * FieldElement::BYTES; + + // Default zero initialized + constexpr FieldElement2() = default; + + constexpr FieldElement2(const FieldElement& c0, const FieldElement& c1) : m_c0(c0), m_c1(c1) {} + + static FieldElement2 zero() { return FieldElement2(); } + + static FieldElement2 one() { return FieldElement2(FieldElement::one(), FieldElement::zero()); } + + /** + * Deserialization; the c1 coefficient is encoded first, following + * the ZCash convention for G2 point encoding + */ + static std::optional deserialize(std::span bytes); + + void serialize_to(std::span bytes) const; + + std::array serialize() const { + std::array buf{}; + this->serialize_to(buf); + return buf; + } + + const FieldElement& c0() const { return m_c0; } + + const FieldElement& c1() const { return m_c1; } + + CT::Choice is_zero() const; + + FieldElement2 add(const FieldElement2& x) const; + + FieldElement2 sub(const FieldElement2& x) const; + + FieldElement2 mul(const FieldElement2& x) const; + + FieldElement2& operator+=(const FieldElement2& x) { + (*this) = this->add(x); + return (*this); + } + + FieldElement2& operator-=(const FieldElement2& x) { + (*this) = this->sub(x); + return (*this); + } + + FieldElement2& operator*=(const FieldElement2& x) { + (*this) = this->mul(x); + return (*this); + } + + FieldElement2 negate() const; + + FieldElement2 invert() const; + + FieldElement2 square() const; + + /** + * Return the conjugate c0 - c1*u, which is also the image of this + * element under the Frobenius endomorphism x -> x^p + */ + FieldElement2 conjugate() const; + + /** + * Multiplication by the sextic nonresidue u + 1 + */ + FieldElement2 mul_by_nonresidue() const; + + /** + * Return the square root of this element, if one exists + * + * If x and -x are both square roots, which of the two is returned + * is unspecified. + */ + std::optional sqrt() const; + + /** + * Constant time equality + */ + CT::Choice operator==(const FieldElement2& other) const; + + CT::Choice operator!=(const FieldElement2& other) const { return !(*this == other); } + + /** + * Set iff this is larger than its negation + * + * The comparison is lexicographic on (c1, c0), matching the ZCash + * convention: c1 > (p-1)/2, or c1 == 0 and c0 > (p-1)/2. + */ + CT::Choice _is_lexicographically_largest() const; + + /** + * The inputs are assumed to be valid and already in Montgomery representation + */ + static constexpr FieldElement2 _unchecked_from_words(std::array c0, + std::array c1) { + return FieldElement2(FieldElement::_unchecked_from_words(c0), FieldElement::_unchecked_from_words(c1)); + } + + /** + * If cnd is set assign other to this, in constant time + */ + void _conditional_assign(CT::Choice cnd, const FieldElement2& other); + + private: + FieldElement m_c0; + FieldElement m_c1; +}; + +inline FieldElement2 operator+(const FieldElement2& a, const FieldElement2& b) { + return a.add(b); +} + +inline FieldElement2 operator-(const FieldElement2& a, const FieldElement2& b) { + return a.sub(b); +} + +inline FieldElement2 operator*(const FieldElement2& a, const FieldElement2& b) { + return a.mul(b); +} + +} // namespace Botan::BLS12_381 + +#endif diff --git a/src/lib/math/bls12_381/bls12_381_fp12.cpp b/src/lib/math/bls12_381/bls12_381_fp12.cpp new file mode 100644 index 00000000000..84cdee1946d --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_fp12.cpp @@ -0,0 +1,154 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::BLS12_381 { + +CT::Choice Fp12::is_zero() const { + return m_c0.is_zero() && m_c1.is_zero(); +} + +void Fp12::serialize_to(std::span bytes) const { + constexpr size_t fe_bytes = FieldElement::BYTES; + + const std::array coeffs = { + &m_c0.c0().c0(), + &m_c0.c0().c1(), + &m_c0.c1().c0(), + &m_c0.c1().c1(), + &m_c0.c2().c0(), + &m_c0.c2().c1(), + &m_c1.c0().c0(), + &m_c1.c0().c1(), + &m_c1.c1().c0(), + &m_c1.c1().c1(), + &m_c1.c2().c0(), + &m_c1.c2().c1(), + }; + + for(size_t i = 0; i != coeffs.size(); ++i) { + coeffs[i]->serialize_to(bytes.subspan(i * fe_bytes).first()); + } +} + +Fp12 Fp12::add(const Fp12& x) const { + return Fp12(m_c0 + x.m_c0, m_c1 + x.m_c1); +} + +Fp12 Fp12::sub(const Fp12& x) const { + return Fp12(m_c0 - x.m_c0, m_c1 - x.m_c1); +} + +Fp12 Fp12::negate() const { + return Fp12(m_c0.negate(), m_c1.negate()); +} + +Fp12 Fp12::mul(const Fp12& x) const { + // Karatsuba multiplication using 3 Fp6 multiplications, with w^2 = v + const auto aa = m_c0 * x.m_c0; + const auto bb = m_c1 * x.m_c1; + const auto c1 = (m_c1 + m_c0) * (x.m_c0 + x.m_c1) - aa - bb; + + return Fp12(bb.mul_by_nonresidue() + aa, c1); +} + +Fp12 Fp12::square() const { + const auto ab = m_c0 * m_c1; + const auto c0c1 = m_c0 + m_c1; + const auto c0 = (m_c1.mul_by_nonresidue() + m_c0) * c0c1 - ab - ab.mul_by_nonresidue(); + const auto c1 = ab + ab; + + return Fp12(c0, c1); +} + +Fp12 Fp12::invert() const { + const auto t = (m_c0.square() - m_c1.square().mul_by_nonresidue()).invert(); + return Fp12(m_c0 * t, (m_c1 * t).negate()); +} + +Fp12 Fp12::conjugate() const { + return Fp12(m_c0, m_c1.negate()); +} + +Fp12 Fp12::mul_by_014(const FieldElement2& b0, const FieldElement2& b1, const FieldElement2& b4) const { + const auto aa = m_c0.mul_by_01(b0, b1); + const auto bb = m_c1.mul_by_1(b4); + const auto c1 = (m_c1 + m_c0).mul_by_01(b0, b1 + b4) - aa - bb; + + return Fp12(bb.mul_by_nonresidue() + aa, c1); +} + +Fp12 Fp12::frobenius_map() const { + // (u+1)^((p-1)/6) + constexpr auto FP12_FROBENIUS_C1 = FieldElement2::_unchecked_from_words( + hex_to_words( + "08f2220fb0fb66eb1ce393ea5daace4da35baecab2dc29ee97e83cccd117228fc6695f92b50a831307089552b319d465"), + hex_to_words( + "110eefda88847faf2e3813cbe5a0de89c11b9cba40a8e8d0cf4895d42599d3945842a06bfc497cecb2f66aad4ce5d646")); + + const auto c0 = m_c0.frobenius_map(); + const auto c1 = m_c1.frobenius_map(); + + // c1 *= (u+1)^((p-1)/6) + return Fp12(c0, c1.mul_by_01(FP12_FROBENIUS_C1, FieldElement2::zero())); +} + +Fp12 Fp12::cyclotomic_square() const { + // See "Guide to Pairing-Based Cryptography", Algorithm 5.5.4 + + // Squaring in Fp4 = Fp2[w]/(w^2 - v), on coefficients directly + auto fp4_square = [](const FieldElement2& a, const FieldElement2& b) -> std::pair { + const auto t0 = a.square(); + const auto t1 = b.square(); + const auto c0 = t1.mul_by_nonresidue() + t0; + const auto c1 = (a + b).square() - t0 - t1; + return {c0, c1}; + }; + + auto z0 = m_c0.c0(); + auto z4 = m_c0.c1(); + auto z3 = m_c0.c2(); + auto z2 = m_c1.c0(); + auto z1 = m_c1.c1(); + auto z5 = m_c1.c2(); + + const auto [t0a, t1a] = fp4_square(z0, z1); + + z0 = t0a - z0; + z0 = z0 + z0 + t0a; + z1 = t1a + z1; + z1 = z1 + z1 + t1a; + + const auto [t0b, t1b] = fp4_square(z2, z3); + const auto [t2, t3] = fp4_square(z4, z5); + + z4 = t0b - z4; + z4 = z4 + z4 + t0b; + z5 = t1b + z5; + z5 = z5 + z5 + t1b; + + const auto t = t3.mul_by_nonresidue(); + z2 = t + z2; + z2 = z2 + z2 + t; + z3 = t2 - z3; + z3 = z3 + z3 + t2; + + return Fp12(Fp6(z0, z4, z3), Fp6(z2, z1, z5)); +} + +CT::Choice Fp12::operator==(const Fp12& other) const { + return (m_c0 == other.m_c0) && (m_c1 == other.m_c1); +} + +void Fp12::_conditional_assign(CT::Choice cnd, const Fp12& other) { + m_c0._conditional_assign(cnd, other.m_c0); + m_c1._conditional_assign(cnd, other.m_c1); +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_fp2.cpp b/src/lib/math/bls12_381/bls12_381_fp2.cpp new file mode 100644 index 00000000000..1383c1adbe5 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_fp2.cpp @@ -0,0 +1,168 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan::BLS12_381 { + +//static +std::optional FieldElement2::deserialize(std::span bytes) { + if(bytes.size() != FieldElement2::BYTES) { + return {}; + } + + // The c1 coefficient is encoded first + const auto c1 = FieldElement::deserialize(bytes.first(FieldElement::BYTES)); + const auto c0 = FieldElement::deserialize(bytes.last(FieldElement::BYTES)); + + if(!c0 || !c1) { + return {}; + } + + return FieldElement2(*c0, *c1); +} + +void FieldElement2::serialize_to(std::span bytes) const { + m_c1.serialize_to(bytes.first()); + m_c0.serialize_to(bytes.last()); +} + +CT::Choice FieldElement2::is_zero() const { + return m_c0.is_zero() && m_c1.is_zero(); +} + +FieldElement2 FieldElement2::add(const FieldElement2& x) const { + return FieldElement2(m_c0 + x.m_c0, m_c1 + x.m_c1); +} + +FieldElement2 FieldElement2::sub(const FieldElement2& x) const { + return FieldElement2(m_c0 - x.m_c0, m_c1 - x.m_c1); +} + +FieldElement2 FieldElement2::mul(const FieldElement2& x) const { + // Karatsuba multiplication using 3 base field multiplications: + // (a0 + a1*u)(b0 + b1*u) = (a0*b0 - a1*b1) + ((a0+a1)(b0+b1) - a0*b0 - a1*b1)*u + const auto v0 = m_c0 * x.m_c0; + const auto v1 = m_c1 * x.m_c1; + const auto s = (m_c0 + m_c1) * (x.m_c0 + x.m_c1); + + return FieldElement2(v0 - v1, s - v0 - v1); +} + +FieldElement2 FieldElement2::square() const { + // Complex squaring using 2 base field multiplications: + // (a + b*u)^2 = (a+b)(a-b) + (2ab)*u + const auto t0 = (m_c0 + m_c1) * (m_c0 - m_c1); + const auto t1 = m_c0 * m_c1; + + return FieldElement2(t0, t1 + t1); +} + +FieldElement2 FieldElement2::negate() const { + return FieldElement2(m_c0.negate(), m_c1.negate()); +} + +FieldElement2 FieldElement2::conjugate() const { + return FieldElement2(m_c0, m_c1.negate()); +} + +FieldElement2 FieldElement2::mul_by_nonresidue() const { + // (a + b*u)(u + 1) = (a - b) + (a + b)*u using u^2 = -1 + return FieldElement2(m_c0 - m_c1, m_c0 + m_c1); +} + +FieldElement2 FieldElement2::invert() const { + /* + * We wish to compute the inverse of (a + b*u) where u^2 = -1 + * + * Consider the identity (a + b*u)(a - b*u) = a^2 + b^2. Shifting + * the terms implies the inverse of (a + b*u) is (a - b*u)/(a^2 + b^2) + */ + + // First compute (a^2 + b^2)^-1 + const auto ninv = (m_c0.square() + m_c1.square()).invert(); + + // Apply the inverse then additional negate c1 + return FieldElement2(m_c0 * ninv, (m_c1 * ninv).negate()); +} + +CT::Choice FieldElement2::operator==(const FieldElement2& other) const { + return (m_c0 == other.m_c0) && (m_c1 == other.m_c1); +} + +void FieldElement2::_conditional_assign(CT::Choice cnd, const FieldElement2& other) { + m_c0._conditional_assign(cnd, other.m_c0); + m_c1._conditional_assign(cnd, other.m_c1); +} + +CT::Choice FieldElement2::_is_lexicographically_largest() const { + return m_c1._is_lexicographically_largest() || (m_c1.is_zero() && m_c0._is_lexicographically_largest()); +} + +std::optional FieldElement2::sqrt() const { + // Algorithm 9 of https://eprint.iacr.org/2012/685.pdf for q == 3 (mod 4) + + constexpr auto FP2_P = hex_to_words( + "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"); + + // (p-3)/4; since p == 3 (mod 4) this is p >> 2 + constexpr auto FP2_SQRT_EXP = [FP2_P] { + auto x = FP2_P; + shift_right<2>(x); + return x; + }(); + + // (p-1)/2, ie p >> 1 + constexpr auto FP2_P_HALF = [FP2_P] { + auto x = FP2_P; + shift_right<1>(x); + return x; + }(); + + auto fp2_pow = [](const FieldElement2& x, std::span exp) -> FieldElement2 { + auto r = FieldElement2::one(); + for(size_t i = 0; i != exp.size(); ++i) { + const word w = exp[exp.size() - 1 - i]; + for(size_t b = 0; b != WordInfo::bits; ++b) { + r = r.square(); + // The exponent is a public constant so this branch leaks nothing + if(((w >> (WordInfo::bits - 1 - b)) & 1) == 1) { + r = r * x; + } + } + } + return r; + }; + + // a1 = x^((p-3)/4) + const auto a1 = fp2_pow(*this, FP2_SQRT_EXP); + + // alpha = a1^2 * x = x^((p-1)/2) + const auto alpha = a1.square() * (*this); + + // x0 = a1 * x = x^((p+1)/4) + const auto x0 = a1 * (*this); + + // If alpha == -1 the square root is x0 * u; otherwise it is + // (1 + alpha)^((p-1)/2) * x0. In the first case 1 + alpha == 0 so the + // generic computation yields zero and is overridden in constant time. + auto candidate = fp2_pow(alpha + FieldElement2::one(), FP2_P_HALF) * x0; + + const auto x0_u = FieldElement2(x0.c1().negate(), x0.c0()); + const auto alpha_is_neg_one = (alpha == FieldElement2::one().negate()); + candidate._conditional_assign(alpha_is_neg_one, x0_u); + + if((candidate.square() == (*this)).as_bool()) { + return candidate; + } else { + return {}; + } +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_fp6.cpp b/src/lib/math/bls12_381/bls12_381_fp6.cpp new file mode 100644 index 00000000000..265da1ca503 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_fp6.cpp @@ -0,0 +1,115 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include + +namespace Botan::BLS12_381 { + +CT::Choice Fp6::is_zero() const { + return m_c0.is_zero() && m_c1.is_zero() && m_c2.is_zero(); +} + +Fp6 Fp6::add(const Fp6& x) const { + return Fp6(m_c0 + x.m_c0, m_c1 + x.m_c1, m_c2 + x.m_c2); +} + +Fp6 Fp6::sub(const Fp6& x) const { + return Fp6(m_c0 - x.m_c0, m_c1 - x.m_c1, m_c2 - x.m_c2); +} + +Fp6 Fp6::negate() const { + return Fp6(m_c0.negate(), m_c1.negate(), m_c2.negate()); +} + +Fp6 Fp6::mul(const Fp6& x) const { + // Karatsuba multiplication using 6 Fp2 multiplications, with xi = u+1: + // c0 = t0 + xi*((a1+a2)(b1+b2) - t1 - t2) + // c1 = (a0+a1)(b0+b1) - t0 - t1 + xi*t2 + // c2 = (a0+a2)(b0+b2) - t0 - t2 + t1 + const auto t0 = m_c0 * x.m_c0; + const auto t1 = m_c1 * x.m_c1; + const auto t2 = m_c2 * x.m_c2; + + const auto s12 = (m_c1 + m_c2) * (x.m_c1 + x.m_c2); + const auto s01 = (m_c0 + m_c1) * (x.m_c0 + x.m_c1); + const auto s02 = (m_c0 + m_c2) * (x.m_c0 + x.m_c2); + + return Fp6((s12 - t1 - t2).mul_by_nonresidue() + t0, s01 - t0 - t1 + t2.mul_by_nonresidue(), s02 - t0 - t2 + t1); +} + +Fp6 Fp6::square() const { + const auto s0 = m_c0.square(); + const auto ab = m_c0 * m_c1; + const auto s1 = ab + ab; + const auto s2 = (m_c0 - m_c1 + m_c2).square(); + const auto bc = m_c1 * m_c2; + const auto s3 = bc + bc; + const auto s4 = m_c2.square(); + + return Fp6(s3.mul_by_nonresidue() + s0, s4.mul_by_nonresidue() + s1, s1 + s2 + s3 - s0 - s4); +} + +Fp6 Fp6::invert() const { + const auto c0 = m_c0.square() - (m_c1 * m_c2).mul_by_nonresidue(); + const auto c1 = m_c2.square().mul_by_nonresidue() - (m_c0 * m_c1); + const auto c2 = m_c1.square() - (m_c0 * m_c2); + + const auto t = (((m_c1 * c2) + (m_c2 * c1)).mul_by_nonresidue() + (m_c0 * c0)).invert(); + + return Fp6(t * c0, t * c1, t * c2); +} + +Fp6 Fp6::mul_by_nonresidue() const { + // (c0 + c1*v + c2*v^2)*v = c2*(u+1) + c0*v + c1*v^2 using v^3 = u+1 + return Fp6(m_c2.mul_by_nonresidue(), m_c0, m_c1); +} + +Fp6 Fp6::mul_by_1(const FieldElement2& b1) const { + return Fp6((m_c2 * b1).mul_by_nonresidue(), m_c0 * b1, m_c1 * b1); +} + +Fp6 Fp6::mul_by_01(const FieldElement2& b0, const FieldElement2& b1) const { + const auto a_a = m_c0 * b0; + const auto b_b = m_c1 * b1; + + const auto t1 = (m_c2 * b1).mul_by_nonresidue() + a_a; + const auto t2 = (b0 + b1) * (m_c0 + m_c1) - a_a - b_b; + const auto t3 = m_c2 * b0 + b_b; + + return Fp6(t1, t2, t3); +} + +Fp6 Fp6::frobenius_map() const { + // (u+1)^((p-1)/3) + constexpr auto FP6_FROBENIUS_C1 = FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "18f020655463874103f97d6e83d050d28eb60ebe01bacb9e587042afd3851b955dab22461fcda5d2cd03c9e48671f071")); + + // (u+1)^((2p-2)/3) + constexpr auto FP6_FROBENIUS_C2 = FieldElement2::_unchecked_from_words( + hex_to_words( + "14e56d3f1564853a14e4f04fe2db9068a20d1b8c7e88102450880866309b7e2c2af322533285a5d5890dc9e4867545c3"), + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + + return Fp6(m_c0.conjugate(), m_c1.conjugate() * FP6_FROBENIUS_C1, m_c2.conjugate() * FP6_FROBENIUS_C2); +} + +CT::Choice Fp6::operator==(const Fp6& other) const { + return (m_c0 == other.m_c0) && (m_c1 == other.m_c1) && (m_c2 == other.m_c2); +} + +void Fp6::_conditional_assign(CT::Choice cnd, const Fp6& other) { + m_c0._conditional_assign(cnd, other.m_c0); + m_c1._conditional_assign(cnd, other.m_c1); + m_c2._conditional_assign(cnd, other.m_c2); +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_g1.cpp b/src/lib/math/bls12_381/bls12_381_g1.cpp new file mode 100644 index 00000000000..f5fee9a70d0 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_g1.cpp @@ -0,0 +1,407 @@ +/* +* (C) 2024,2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +namespace { + +static_assert(G1Affine::BYTES == FieldElement::BYTES); + +// Standard generator coordinates, pre-converted into Montgomery form +constexpr auto G1_X = FieldElement::_unchecked_from_words(hex_to_words( + "120177419e0bfb75edce6ecc21dbf440f0ae6acdf3d0e747154f95c7143ba1c17817fc679976fff55cb38790fd530c16")); + +constexpr auto G1_Y = FieldElement::_unchecked_from_words(hex_to_words( + "0bbc3efc5008a26a0e1c8c3fad0059c051ac582950405194dd595f13570725ce8c22631a7918fd8ebaac93d50ce72271")); + +// A nontrivial cube root of unity, acting on G1 as multiplication by -z^2 +constexpr auto G1_BETA = FieldElement::_unchecked_from_words(hex_to_words( + "051ba4ab241b61603636b76660701c6ec26a2ff874fd029b16a8ca3ac61577f7f3b8ddab7ece5a2a30f1361b798a64e8")); + +FieldElement fe_load(const std::array& w) { + return FieldElement::_unchecked_from_words(w); +} + +inline FieldElement mul_by_3b(const FieldElement& fe) { + // b == 4 so 3*b == 12 + const auto fe2 = fe + fe; + const auto fe4 = fe2 + fe2; + return fe4 + fe4 + fe4; +} + +} // namespace + +G1Affine::G1Affine(const FieldElement& x, const FieldElement& y, uint32_t infinity) : + m_x(x._words()), m_y(y._words()), m_infinity(infinity) {} + +//static +G1Affine G1Affine::identity() { + return G1Affine(FieldElement::zero(), FieldElement::one(), 1); +} + +//static +G1Affine G1Affine::generator() { + return G1Affine(G1_X, G1_Y, 0); +} + +FieldElement G1Affine::_x() const { + return fe_load(m_x); +} + +FieldElement G1Affine::_y() const { + return fe_load(m_y); +} + +bool G1Affine::is_identity() const { + return m_infinity != 0; +} + +//static +std::optional G1Affine::deserialize(std::span bytes) { + if(bytes.size() != G1Affine::BYTES) { + return {}; + } + + const uint8_t flags = bytes[0]; + + // Only the compressed encoding is supported + if((flags & 0x80) != 0x80) { + return {}; + } + + const bool is_infinity = (flags & 0x40) == 0x40; + const bool y_is_largest = (flags & 0x20) == 0x20; + + std::array x_bytes{}; + copy_mem(x_bytes.data(), bytes.data(), bytes.size()); + x_bytes[0] &= 0x1F; + + if(is_infinity) { + // The identity is encoded as the infinity flag with all other bits zero + if(y_is_largest || !CT::all_zeros(x_bytes.data(), x_bytes.size()).as_bool()) { + return {}; + } + return G1Affine::identity(); + } + + const auto x = FieldElement::deserialize(x_bytes); + if(!x) { + return {}; + } + + const auto y2 = x->square() * (*x) + FieldElement::from_u32(4); + auto y = y2.sqrt(); + if(!y) { + return {}; + } + + // Choose either y or -y depending on the sign flag + const auto flip = (y->_is_lexicographically_largest() != CT::Choice::from_int(static_cast(flags & 0x20))); + y->_conditional_assign(flip, y->negate()); + + G1Affine pt(*x, *y, 0); + + // Fast subgroup check (https://eprint.iacr.org/2021/1130 section 6): + // P is in the prime order subgroup iff phi(P) == -[z^2]P, where + // phi(x, y) = (beta*x, y) + using Mul = PointMul; + const auto zz_p = Mul::mul_by_z_abs(Mul::mul_by_z_abs(G1Projective::from_affine(pt))).negate(); + + // Compare the projective -[z^2]P with the affine phi(P) + const auto zz_p_z = fe_load(zz_p.m_z); + const auto x_eq = (fe_load(zz_p.m_x) == (*x * G1_BETA) * zz_p_z); + const auto y_eq = (fe_load(zz_p.m_y) == *y * zz_p_z); + if(!(x_eq && y_eq).as_bool()) { + return {}; + } + + return pt; +} + +std::array G1Affine::serialize() const { + auto bytes = fe_load(m_x).serialize(); + + // Set the compressed point indicator bit + bytes[0] |= 0x80; + + const auto identity = CT::Choice::from_int(m_infinity); + + // If the identity element, set the identity bit + bytes[0] |= (identity.into_bitmask() & 0x40); + + // If y is the larger choice *and* not the point at identity, set the large-y bit + const auto large_y = fe_load(m_y)._is_lexicographically_largest(); + bytes[0] |= ((!identity && large_y).into_bitmask() & 0x20); + + return bytes; +} + +G1Projective::G1Projective(const FieldElement& x, const FieldElement& y, const FieldElement& z) : + m_x(x._words()), m_y(y._words()), m_z(z._words()) {} + +G1Projective::G1Projective() : m_x({}), m_y(FieldElement::one()._words()), m_z({}) {} + +//static +G1Projective G1Projective::from_affine(const G1Affine& affine) { + // z == 0 if the identity element or 1 otherwise + auto z = FieldElement::one(); + z._conditional_assign(CT::Choice::from_int(affine.m_infinity), FieldElement::zero()); + return G1Projective(affine._x(), affine._y(), z); +} + +//static +G1Projective G1Projective::_unchecked_from_affine_coords(const FieldElement& x, const FieldElement& y) { + return G1Projective(x, y, FieldElement::one()); +} + +//static +G1Projective G1Projective::generator() { + return G1Projective(G1_X, G1_Y, FieldElement::one()); +} + +G1Affine G1Projective::to_affine() const { + const auto z = fe_load(m_z); + const auto zinv = z.invert(); + const auto inf = z.is_zero(); + + // Canonicalize the identity to (0, 1); otherwise a round trip + // through from_affine would produce the invalid triple (0, 0, 0), + // which acts as an absorbing element of the addition formulas + auto y = fe_load(m_y) * zinv; + y._conditional_assign(inf, FieldElement::one()); + + return G1Affine(fe_load(m_x) * zinv, y, inf.into_bitmask() & 1); +} + +//static +std::vector G1Projective::to_affine_batch(std::span points) { + const size_t n = points.size(); + + std::vector affine; + affine.reserve(n); + + if(n == 0) { + return affine; + } + + /* + Batch inversion of the z coordinates using Montgomery's trick, with a + single field inversion plus 3*(n-1) multiplications. + + See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography" + (Hankerson, Menezes, Vanstone) + + An identity element (z == 0) would zero the running product, so + identity z's are replaced by one, and the affine identity is instead + assigned at the end; this handles identities in constant time, rather + than leaking their presence by falling back to serial conversion. + */ + + auto masked_z = [](const G1Projective& pt) { + auto z = fe_load(pt.m_z); + z._conditional_assign(z.is_zero(), FieldElement::one()); + return z; + }; + + auto affine_from = [](const G1Projective& pt, const FieldElement& z_inv) { + const auto inf = fe_load(pt.m_z).is_zero(); + + auto x = fe_load(pt.m_x) * z_inv; + auto y = fe_load(pt.m_y) * z_inv; + + // Canonicalize the identity to (0, 1), as in to_affine + x._conditional_assign(inf, FieldElement::zero()); + y._conditional_assign(inf, FieldElement::one()); + + return G1Affine(x, y, inf.into_bitmask() & 1); + }; + + std::vector prefix; + prefix.reserve(n); + + prefix.push_back(masked_z(points[0])); + for(size_t i = 1; i != n; ++i) { + prefix.push_back(prefix[i - 1] * masked_z(points[i])); + } + + auto inv = prefix[n - 1].invert(); + + for(size_t i = n; i > 1; --i) { + const auto& pt = points[i - 1]; + affine.push_back(affine_from(pt, inv * prefix[i - 2])); + inv = inv * masked_z(pt); + } + affine.push_back(affine_from(points[0], inv)); + + std::reverse(affine.begin(), affine.end()); + + return affine; +} + +G1Projective G1Projective::negate() const { + return G1Projective(fe_load(m_x), fe_load(m_y).negate(), fe_load(m_z)); +} + +bool G1Projective::is_identity() const { + return CT::all_zeros(m_z.data(), m_z.size()).as_bool(); +} + +G1Projective G1Projective::dbl() const { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + const auto x = fe_load(m_x); + const auto y = fe_load(m_y); + const auto z = fe_load(m_z); + + auto t0 = y.square(); + auto z3 = t0 + t0; + z3 = z3 + z3; + z3 = z3 + z3; + auto t1 = y * z; + auto t2 = z.square(); + t2 = mul_by_3b(t2); + auto x3 = t2 * z3; + auto y3 = t0 + t2; + z3 = t1 * z3; + t1 = t2 + t2; + t2 = t1 + t2; + t0 = t0 - t2; + y3 = t0 * y3; + y3 = x3 + y3; + t1 = x * y; + x3 = t0 * t1; + x3 = x3 + x3; + + return G1Projective(x3, y3, z3); +} + +G1Projective G1Projective::add(const G1Projective& other) const { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + const auto x1 = fe_load(m_x); + const auto y1 = fe_load(m_y); + const auto z1 = fe_load(m_z); + const auto x2 = fe_load(other.m_x); + const auto y2 = fe_load(other.m_y); + const auto z2 = fe_load(other.m_z); + + auto t0 = x1 * x2; + auto t1 = y1 * y2; + auto t2 = z1 * z2; + auto t3 = x1 + y1; + auto t4 = x2 + y2; + t3 = t3 * t4; + t4 = t0 + t1; + t3 = t3 - t4; + t4 = y1 + z1; + auto x3 = y2 + z2; + t4 = t4 * x3; + x3 = t1 + t2; + t4 = t4 - x3; + x3 = x1 + z1; + auto y3 = x2 + z2; + x3 = x3 * y3; + y3 = t0 + t2; + y3 = x3 - y3; + x3 = t0 + t0; + t0 = x3 + t0; + t2 = mul_by_3b(t2); + auto z3 = t1 + t2; + t1 = t1 - t2; + y3 = mul_by_3b(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 = y3 * t0; + t1 = t1 * z3; + y3 = t1 + y3; + t0 = t0 * t3; + z3 = z3 * t4; + z3 = z3 + t0; + + return G1Projective(x3, y3, z3); +} + +G1Projective G1Projective::add_mixed(const G1Affine& other) const { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + // + // The formula assumes other is not the identity; that case is + // handled by conditional assignment at the end + + const auto x1 = fe_load(m_x); + const auto y1 = fe_load(m_y); + const auto z1 = fe_load(m_z); + const auto x2 = fe_load(other.m_x); + const auto y2 = fe_load(other.m_y); + + auto t0 = x1 * x2; + auto t1 = y1 * y2; + auto t3 = x2 + y2; + auto t4 = x1 + y1; + t3 = t3 * t4; + t4 = t0 + t1; + t3 = t3 - t4; + t4 = y2 * z1; + t4 = t4 + y1; + auto y3 = x2 * z1; + y3 = y3 + x1; + auto x3 = t0 + t0; + t0 = x3 + t0; + auto t2 = mul_by_3b(z1); + auto z3 = t1 + t2; + t1 = t1 - t2; + y3 = mul_by_3b(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 = y3 * t0; + t1 = t1 * z3; + y3 = t1 + y3; + t0 = t0 * t3; + z3 = z3 * t4; + z3 = z3 + t0; + + auto result = G1Projective(x3, y3, z3); + + const auto other_is_identity = CT::Choice::from_int(other.m_infinity); + CT::conditional_assign_mem(other_is_identity, result.m_x.data(), m_x.data(), FE_WORDS); + CT::conditional_assign_mem(other_is_identity, result.m_y.data(), m_y.data(), FE_WORDS); + CT::conditional_assign_mem(other_is_identity, result.m_z.data(), m_z.data(), FE_WORDS); + + return result; +} + +G1Projective G1Projective::mul(const Scalar& scalar) const { + return PointMul::mul(*this, scalar); +} + +//static +G1Projective G1Projective::mul2(const G1Projective& p, const Scalar& a, const G1Projective& q, const Scalar& b) { + return PointMul::mul2(p, a, q, b); +} + +//static +G1Projective G1Projective::mul2_vartime(const G1Projective& p, + const Scalar& a, + const G1Projective& q, + const Scalar& b) { + return PointMul::mul2_vartime(p, a, q, b); +} + +//static +G1Projective G1Projective::msm_vartime(std::span points, std::span scalars) { + return PointMul::msm_vartime(points, scalars); +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_g2.cpp b/src/lib/math/bls12_381/bls12_381_g2.cpp new file mode 100644 index 00000000000..408b5e7b7e2 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_g2.cpp @@ -0,0 +1,469 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +namespace { + +FieldElement2 fe2_load(const std::array& w) { + std::array c0{}; + std::array c1{}; + copy_mem(c0.data(), w.data(), c0.size()); + copy_mem(c1.data(), w.data() + c0.size(), c1.size()); + return FieldElement2::_unchecked_from_words(c0, c1); +} + +std::array fe2_store(const FieldElement2& v) { + std::array w{}; + copy_mem(w.data(), v.c0()._words().data(), FieldElement::N); + copy_mem(w.data() + FieldElement::N, v.c1()._words().data(), FieldElement::N); + return w; +} + +// Standard generator coordinates, pre-converted into Montgomery form +constexpr auto G2_X = FieldElement2::_unchecked_from_words( + hex_to_words( + "058191924350bcd76f67b7631863366b9894999d1a3caee9a1a893b53e2ae580b3f5fb2687b4961af5f28fa202940a10"), + hex_to_words( + "11922a097360edf3c2b6ed0ef21585471b1ab6cc8541b3673bb17e18e2867806aaa0c59dbccd60c3a5a9c0759e23f606")); + +constexpr auto G2_Y = FieldElement2::_unchecked_from_words( + hex_to_words( + "0083fd8e7e80dae507d3a975f0ef25a2bbefb5e96e0d495fe7e6856caa0a635a597cfa1f5e369c5a4c730af860494c4a"), + hex_to_words( + "0b2bc2a163de1bf2e7175850a43ccaed79495c4ec93da33a86adac6a3be4eba018aa270a2b1461dcadc0fc92df64b05d")); + +// 1/(u+1)^((p-1)/3) and 1/(u+1)^((p-1)/2), for the psi endomorphism +constexpr auto G2_PSI_X = FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "14e56d3f1564853a14e4f04fe2db9068a20d1b8c7e88102450880866309b7e2c2af322533285a5d5890dc9e4867545c3")); +constexpr auto G2_PSI_Y = FieldElement2::_unchecked_from_words( + hex_to_words( + "0bd592fc7d825ec81d794e4fac7cf0b992ad2afd19103e18382844c88b6237324294213d86c181833e2f585da55c9ad1"), + hex_to_words( + "0e2b7eedbbfd87d22da2596696cebc1dd1ca2087da74d4a72f088dd86b4ebef1dc17dec12a927e7c7bcfa7a25aa30fda")); + +// The x coefficient of psi^2; the y coefficient is -1 +constexpr auto G2_PSI2_X = FieldElement2::_unchecked_from_words( + hex_to_words( + "18f020655463874103f97d6e83d050d28eb60ebe01bacb9e587042afd3851b955dab22461fcda5d2cd03c9e48671f071"), + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + +inline FieldElement2 mul_by_3b(const FieldElement2& fe) { + // b == 4*(u+1) so 3*b == 12*(u+1) + const auto fe2 = fe + fe; + const auto fe4 = fe2 + fe2; + return (fe4 + fe4 + fe4).mul_by_nonresidue(); +} + +FieldElement2 g2_curve_b() { + const auto four = FieldElement::from_u32(4); + return FieldElement2(four, four); +} + +} // namespace + +G2Affine::G2Affine(const FieldElement2& x, const FieldElement2& y, uint32_t infinity) : + m_x(fe2_store(x)), m_y(fe2_store(y)), m_infinity(infinity) {} + +//static +G2Affine G2Affine::identity() { + return G2Affine(FieldElement2::zero(), FieldElement2::one(), 1); +} + +//static +G2Affine G2Affine::generator() { + return G2Affine(G2_X, G2_Y, 0); +} + +FieldElement2 G2Affine::_x() const { + return fe2_load(m_x); +} + +FieldElement2 G2Affine::_y() const { + return fe2_load(m_y); +} + +bool G2Affine::is_identity() const { + return m_infinity != 0; +} + +//static +std::optional G2Affine::deserialize(std::span bytes) { + if(bytes.size() != G2Affine::BYTES) { + return {}; + } + + const uint8_t flags = bytes[0]; + + // Only the compressed encoding is supported + if((flags & 0x80) != 0x80) { + return {}; + } + + const bool is_infinity = (flags & 0x40) == 0x40; + const bool y_is_largest = (flags & 0x20) == 0x20; + + std::array x_bytes{}; + copy_mem(x_bytes.data(), bytes.data(), bytes.size()); + x_bytes[0] &= 0x1F; + + if(is_infinity) { + // The identity is encoded as the infinity flag with all other bits zero + if(y_is_largest || !CT::all_zeros(x_bytes.data(), x_bytes.size()).as_bool()) { + return {}; + } + return G2Affine::identity(); + } + + const auto x = FieldElement2::deserialize(x_bytes); + if(!x) { + return {}; + } + + const auto y2 = x->square() * (*x) + g2_curve_b(); + auto y = y2.sqrt(); + if(!y) { + return {}; + } + + // Choose either y or -y depending on the sign flag + const auto flip = (y->_is_lexicographically_largest() != CT::Choice::from_int(static_cast(flags & 0x20))); + y->_conditional_assign(flip, y->negate()); + + G2Affine pt(*x, *y, 0); + + // Fast subgroup check (https://eprint.iacr.org/2021/1130 section 4): + // P is in the prime order subgroup iff psi(P) == [z]P, where psi is + // the untwist-Frobenius-twist endomorphism + const auto z_p = PointMul::mul_by_z_abs(G2Projective::from_affine(pt)).negate(); + + const auto psi_x = x->conjugate() * G2_PSI_X; + const auto psi_y = y->conjugate() * G2_PSI_Y; + + // Compare the projective [z]P with the affine psi(P) + const auto z_p_z = fe2_load(z_p.m_z); + const auto x_eq = (fe2_load(z_p.m_x) == psi_x * z_p_z); + const auto y_eq = (fe2_load(z_p.m_y) == psi_y * z_p_z); + if(!(x_eq && y_eq).as_bool()) { + return {}; + } + + return pt; +} + +std::array G2Affine::serialize() const { + auto bytes = fe2_load(m_x).serialize(); + + // Set the compressed point indicator bit + bytes[0] |= 0x80; + + const auto identity = CT::Choice::from_int(m_infinity); + + // If the identity element, set the identity bit + bytes[0] |= (identity.into_bitmask() & 0x40); + + // If y is the larger choice *and* not the point at identity, set the large-y bit + const auto large_y = fe2_load(m_y)._is_lexicographically_largest(); + bytes[0] |= ((!identity && large_y).into_bitmask() & 0x20); + + return bytes; +} + +G2Projective::G2Projective(const FieldElement2& x, const FieldElement2& y, const FieldElement2& z) : + m_x(fe2_store(x)), m_y(fe2_store(y)), m_z(fe2_store(z)) {} + +G2Projective::G2Projective() : m_x({}), m_y(fe2_store(FieldElement2::one())), m_z({}) {} + +//static +G2Projective G2Projective::from_affine(const G2Affine& affine) { + // z == 0 if the identity element or 1 otherwise + auto z = FieldElement2::one(); + z._conditional_assign(CT::Choice::from_int(affine.m_infinity), FieldElement2::zero()); + return G2Projective(affine._x(), affine._y(), z); +} + +//static +G2Projective G2Projective::_unchecked_from_affine_coords(const FieldElement2& x, const FieldElement2& y) { + return G2Projective(x, y, FieldElement2::one()); +} + +//static +G2Projective G2Projective::generator() { + return G2Projective(G2_X, G2_Y, FieldElement2::one()); +} + +G2Affine G2Projective::to_affine() const { + const auto z = fe2_load(m_z); + const auto zinv = z.invert(); + const auto inf = z.is_zero(); + + // Canonicalize the identity to (0, 1); otherwise a round trip + // through from_affine would produce the invalid triple (0, 0, 0), + // which acts as an absorbing element of the addition formulas + auto y = fe2_load(m_y) * zinv; + y._conditional_assign(inf, FieldElement2::one()); + + return G2Affine(fe2_load(m_x) * zinv, y, inf.into_bitmask() & 1); +} + +//static +std::vector G2Projective::to_affine_batch(std::span points) { + const size_t n = points.size(); + + std::vector affine; + affine.reserve(n); + + if(n == 0) { + return affine; + } + + /* + Batch inversion of the z coordinates using Montgomery's trick, with a + single field inversion plus 3*(n-1) multiplications. + + See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography" + (Hankerson, Menezes, Vanstone) + + An identity element (z == 0) would zero the running product, so + identity z's are replaced by one, and the affine identity is instead + assigned at the end; this handles identities in constant time, rather + than leaking their presence by falling back to serial conversion. + */ + + auto masked_z = [](const G2Projective& pt) { + auto z = fe2_load(pt.m_z); + z._conditional_assign(z.is_zero(), FieldElement2::one()); + return z; + }; + + auto affine_from = [](const G2Projective& pt, const FieldElement2& z_inv) { + const auto inf = fe2_load(pt.m_z).is_zero(); + + auto x = fe2_load(pt.m_x) * z_inv; + auto y = fe2_load(pt.m_y) * z_inv; + + // Canonicalize the identity to (0, 1), as in to_affine + x._conditional_assign(inf, FieldElement2::zero()); + y._conditional_assign(inf, FieldElement2::one()); + + return G2Affine(x, y, inf.into_bitmask() & 1); + }; + + std::vector prefix; + prefix.reserve(n); + + prefix.push_back(masked_z(points[0])); + for(size_t i = 1; i != n; ++i) { + prefix.push_back(prefix[i - 1] * masked_z(points[i])); + } + + auto inv = prefix[n - 1].invert(); + + for(size_t i = n; i > 1; --i) { + const auto& pt = points[i - 1]; + affine.push_back(affine_from(pt, inv * prefix[i - 2])); + inv = inv * masked_z(pt); + } + affine.push_back(affine_from(points[0], inv)); + + std::reverse(affine.begin(), affine.end()); + + return affine; +} + +G2Projective G2Projective::negate() const { + return G2Projective(fe2_load(m_x), fe2_load(m_y).negate(), fe2_load(m_z)); +} + +bool G2Projective::is_identity() const { + return CT::all_zeros(m_z.data(), m_z.size()).as_bool(); +} + +G2Projective G2Projective::dbl() const { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + const auto x = fe2_load(m_x); + const auto y = fe2_load(m_y); + const auto z = fe2_load(m_z); + + auto t0 = y.square(); + auto z3 = t0 + t0; + z3 = z3 + z3; + z3 = z3 + z3; + auto t1 = y * z; + auto t2 = z.square(); + t2 = mul_by_3b(t2); + auto x3 = t2 * z3; + auto y3 = t0 + t2; + z3 = t1 * z3; + t1 = t2 + t2; + t2 = t1 + t2; + t0 = t0 - t2; + y3 = t0 * y3; + y3 = x3 + y3; + t1 = x * y; + x3 = t0 * t1; + x3 = x3 + x3; + + return G2Projective(x3, y3, z3); +} + +G2Projective G2Projective::add(const G2Projective& other) const { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + const auto x1 = fe2_load(m_x); + const auto y1 = fe2_load(m_y); + const auto z1 = fe2_load(m_z); + const auto x2 = fe2_load(other.m_x); + const auto y2 = fe2_load(other.m_y); + const auto z2 = fe2_load(other.m_z); + + auto t0 = x1 * x2; + auto t1 = y1 * y2; + auto t2 = z1 * z2; + auto t3 = x1 + y1; + auto t4 = x2 + y2; + t3 = t3 * t4; + t4 = t0 + t1; + t3 = t3 - t4; + t4 = y1 + z1; + auto x3 = y2 + z2; + t4 = t4 * x3; + x3 = t1 + t2; + t4 = t4 - x3; + x3 = x1 + z1; + auto y3 = x2 + z2; + x3 = x3 * y3; + y3 = t0 + t2; + y3 = x3 - y3; + x3 = t0 + t0; + t0 = x3 + t0; + t2 = mul_by_3b(t2); + auto z3 = t1 + t2; + t1 = t1 - t2; + y3 = mul_by_3b(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 = y3 * t0; + t1 = t1 * z3; + y3 = t1 + y3; + t0 = t0 * t3; + z3 = z3 * t4; + z3 = z3 + t0; + + return G2Projective(x3, y3, z3); +} + +G2Projective G2Projective::add_mixed(const G2Affine& other) const { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + // + // The formula assumes other is not the identity; that case is + // handled by conditional assignment at the end + + const auto x1 = fe2_load(m_x); + const auto y1 = fe2_load(m_y); + const auto z1 = fe2_load(m_z); + const auto x2 = fe2_load(other.m_x); + const auto y2 = fe2_load(other.m_y); + + auto t0 = x1 * x2; + auto t1 = y1 * y2; + auto t3 = x2 + y2; + auto t4 = x1 + y1; + t3 = t3 * t4; + t4 = t0 + t1; + t3 = t3 - t4; + t4 = y2 * z1; + t4 = t4 + y1; + auto y3 = x2 * z1; + y3 = y3 + x1; + auto x3 = t0 + t0; + t0 = x3 + t0; + auto t2 = mul_by_3b(z1); + auto z3 = t1 + t2; + t1 = t1 - t2; + y3 = mul_by_3b(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 = y3 * t0; + t1 = t1 * z3; + y3 = t1 + y3; + t0 = t0 * t3; + z3 = z3 * t4; + z3 = z3 + t0; + + auto result = G2Projective(x3, y3, z3); + + const auto other_is_identity = CT::Choice::from_int(other.m_infinity); + CT::conditional_assign_mem(other_is_identity, result.m_x.data(), m_x.data(), FE2_WORDS); + CT::conditional_assign_mem(other_is_identity, result.m_y.data(), m_y.data(), FE2_WORDS); + CT::conditional_assign_mem(other_is_identity, result.m_z.data(), m_z.data(), FE2_WORDS); + + return result; +} + +G2Projective G2Projective::mul(const Scalar& scalar) const { + return PointMul::mul(*this, scalar); +} + +//static +G2Projective G2Projective::mul2(const G2Projective& p, const Scalar& a, const G2Projective& q, const Scalar& b) { + return PointMul::mul2(p, a, q, b); +} + +//static +G2Projective G2Projective::mul2_vartime(const G2Projective& p, + const Scalar& a, + const G2Projective& q, + const Scalar& b) { + return PointMul::mul2_vartime(p, a, q, b); +} + +//static +G2Projective G2Projective::msm_vartime(std::span points, std::span scalars) { + return PointMul::msm_vartime(points, scalars); +} + +G2Projective G2Projective::psi() const { + return G2Projective( + fe2_load(m_x).conjugate() * G2_PSI_X, fe2_load(m_y).conjugate() * G2_PSI_Y, fe2_load(m_z).conjugate()); +} + +G2Projective G2Projective::psi2() const { + return G2Projective(fe2_load(m_x) * G2_PSI2_X, fe2_load(m_y).negate(), fe2_load(m_z)); +} + +G2Projective G2Projective::clear_cofactor() const { + // Budroni-Pintore cofactor clearing (https://eprint.iacr.org/2017/419 section 4.1) + + // [z]P; the parameter z is negative + const auto t1 = PointMul::mul_by_z_abs(*this).negate(); + const auto t2 = this->psi(); + + auto r = this->dbl().psi2(); + r = r.add(PointMul::mul_by_z_abs(t1.add(t2)).negate()); + r = r.add(t1.negate()); + r = r.add(t2.negate()); + r = r.add(this->negate()); + + return r; +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_h2c.cpp b/src/lib/math/bls12_381/bls12_381_h2c.cpp new file mode 100644 index 00000000000..e515964f707 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_h2c.cpp @@ -0,0 +1,487 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +namespace { + +// G1 SSWU (RFC 9380 8.8.1) and 11-isogeny (E.2) constants, pre-converted to Montgomery +constexpr auto G1_SSWU_A = FieldElement::_unchecked_from_words(hex_to_words( + "155455c3e5071d8528376eda6bfc183527e11c91b5f24e7cb85ce591b7bd31e286464c2d1e8416c32f65aa0e9af5aa51")); +constexpr auto G1_SSWU_B = FieldElement::_unchecked_from_words(hex_to_words( + "06824061418a386bca72b5e45a52d888873e27c3a221e5718c476013de99c5c49aa93eb35b742d6ffb996971fe22a1e0")); +constexpr auto G1_SSWU_Z = FieldElement::_unchecked_from_words(hex_to_words( + "078c712fbe0ab6e850553f1b9c1315219dac23e943dc174077672417ed5828c30f70008d3090001d886c00000023ffdc")); + +constexpr std::array G1_ISO_XNUM = { + FieldElement::_unchecked_from_words(hex_to_words( + "0913be200a20bef4d15b58d2ffc0005423dcea34f2ffb3043f2885f1467f19ae19fa219793fee28c4d18b6f3af00131c")), + FieldElement::_unchecked_from_words(hex_to_words( + "07097bc5998784ad11b22deed20d827b8637ef1e4d6623ad1597e193f4cd233a3c79e43cc7d966aa898985385cdbbd8b")), + FieldElement::_unchecked_from_words(hex_to_words( + "0784151ed7605524df6e99707d2a00795b5491c05102f0e75ba2ef314ed8b5a6fc7169c026e568c6a542583a480b664b")), + FieldElement::_unchecked_from_words(hex_to_words( + "0e93d431ea011aeb65dadd7828505289049dfee82aefbd6026f5577994e34c3dab9be52fbda43021494e212870f72741")), + FieldElement::_unchecked_from_words(hex_to_words( + "09f20ead8e532bf6169139d319ea7a8f104c24211be4805c0f1a8953b325f4647ada1c8a41bfb18590ee774bd6a74d45")), + FieldElement::_unchecked_from_words(hex_to_words( + "18f4bfcbb4368929b0282d480e56489f2e0a94ccf77ec0db143245631883f4bda5482c9aa1ccd7bd6ddd93e2f43626b7")), + FieldElement::_unchecked_from_words(hex_to_words( + "1277ffc72f25e8feffd89869a572b297d0df5c98e1f9d70f2c390d3d2da5df637a43ff6958ce4fe923c5f0c953402dfd")), + FieldElement::_unchecked_from_words(hex_to_words( + "08b76279f621d028bba074f260e400f1e2a57f6505880d6512da3054b18b641085f894a88030fd8179f4f0490f06a8a6")), + FieldElement::_unchecked_from_words(hex_to_words( + "09e699dd9adfa5ac05a69cdcef55feeee21585b9a30f86cb7888bff6e6b33bb48456ba9a1f186475e67245ba78d5b00b")), + FieldElement::_unchecked_from_words(hex_to_words( + "098c4bf7de8b63750443915f50fd41798ad456574e9db24fe256bb67b3b3cd8d0a0db4ae6b1a10b20de5c357bff57107")), + FieldElement::_unchecked_from_words(hex_to_words( + "054fdf4bbf1d821c4a51d8667f0fe1cfe4efd1ad3f767ceb1dafdeda137a489efe6e37d442537375e6b0617e7dd929c7")), + FieldElement::_unchecked_from_words(hex_to_words( + "18ae6a856f40715db14f01aadb30be2f464170142a1009ebe969d6833764ab478abf91faa257b3d572db2a50658d767b")), +}; +constexpr std::array G1_ISO_XDEN = { + FieldElement::_unchecked_from_words(hex_to_words( + "0165aa6c93ad115f9c0b3ac929599016b43fc37b908b133ec14d568c3ed6c544a6a9740fefda13a0b962a077fdb0f945")), + FieldElement::_unchecked_from_words(hex_to_words( + "083383d6ed81f1ceed4530924cec2045116dda1c5070ae933b294ab13755f0ff92cfca0a9465176a23279a3ba506c1d9")), + FieldElement::_unchecked_from_words(hex_to_words( + "0f29c13c660523e2ff364f36e54b6812a0fba72732b3fafd17da9ffd8738c1424a2b54ccd37733f09885c2a6449fecfc")), + FieldElement::_unchecked_from_words(hex_to_words( + "12025184f407440c1c2c7844bc417be443a92bd69c15c2dfc9d325849ade5150d487228f2f3204fbe349cc118278f041")), + FieldElement::_unchecked_from_words(hex_to_words( + "13b93c63edf6c0157408904f0f186bb2ccda066072436a42fbf995e71270da491444ef325140201f587f65ae6acb057b")), + FieldElement::_unchecked_from_words(hex_to_words( + "06cc402dd594bbebaeedd424d780f38830f94df6f83a3dc20beb232927f7fb264a4c64423ecaddb4fb918622cd141920")), + FieldElement::_unchecked_from_words(hex_to_words( + "0c6f7f7237b466066933a38d5b594c81df70a9a1f757c6e464f436e888c62cb932a92465435719b3d41f761151b23f8f")), + FieldElement::_unchecked_from_words(hex_to_words( + "17916987aa14a122bc6be2d8dad57c2389bc62d61c7baf238e9071dab950c12422c9850bf9cf80f0693c08747876c8f7")), + FieldElement::_unchecked_from_words(hex_to_words( + "0c102cbac531bb34bbc2ee18e1c227f432c6aa9af394361cc7f7f62962f5cd819965243a7571dfa71be3ff439c1316fd")), + FieldElement::_unchecked_from_words(hex_to_words( + "12a6dcd7f0f4e0e816178f5bbf698711ca2b066c2a87492f5b8c95fc14353fc361f86372b99192c0997614c97bacbf07")), + FieldElement::_unchecked_from_words(hex_to_words( + "15f65ec3fa80e4935c071a97a256ec6d77ce5853705257455f48985753c758baebf4000bc40c0002760900000002fffd")), +}; +constexpr std::array G1_ISO_YNUM = { + FieldElement::_unchecked_from_words(hex_to_words( + "0d300744d42a03107d7b18a682692693cc31a30a0b6cd3dfce028fea04bd73731d4d9e57b958a7672b567ff3e2837267")), + FieldElement::_unchecked_from_words(hex_to_words( + "0a8dadd9c2414555706326a6957dd5a414e03832052b49c85df0608b8f97608afe7f53cc4874f87899c2555fa542493f")), + FieldElement::_unchecked_from_words(hex_to_words( + "13f70bf38bbf290583d0c7532f8c1fde0000bd1de7ba50f0cf05a27c8456088d357e33e36e261e7d13d942922a5cf63a")), + FieldElement::_unchecked_from_words(hex_to_words( + "0bb6cde49d8ba257b3468f4550192bf7afe19ff6f97e6d533983ceb4f6360b6d28a359a65e5417075c57fd95bfafbdbb")), + FieldElement::_unchecked_from_words(hex_to_words( + "0386213c651b888d64eace4cb09821916ddf84a095713d5f6bef32ce94b8a800314b4ce372cacefd590b62c7ff8a513f")), + FieldElement::_unchecked_from_words(hex_to_words( + "01fddf5aed881793cf5b1f022e1c9107aa6ec095283ee4a7f9ad9cc95423d2e9a14ac0f5da148982a5310a31111bbcdd")), + FieldElement::_unchecked_from_words(hex_to_words( + "07eb1b29c1dfde1fba12961be86e9efb05b2d36c769a89b0c2fcebe7cb877dbde25c2d8183473a1965a572b0d7a7d950")), + FieldElement::_unchecked_from_words(hex_to_words( + "0ad52ba3e6695a79f4fa918082e44d64a47da89439f5340f8569467e68af51b5364e92907679509193e09572f7c4cd24")), + FieldElement::_unchecked_from_words(hex_to_words( + "149c9c326a5e7393a90ed5adf1ed5537fa86d2a3a9a734823d587e5640536e66d03f51a3516bb233911429844e0d5f54")), + FieldElement::_unchecked_from_words(hex_to_words( + "1862bd62c291dacb8100e1652b3cdc62649ef8f11a4fae469a558ebde836ebeddc9af5fa0a274a17462bbeb03c12921a")), + FieldElement::_unchecked_from_words(hex_to_words( + "12c7e1c3b28962e5bb1d0d53af3ff6bf14665bdd8846e19d6a643d5a6879fa2c0194160fa9b9ac4f05c9b8ca89f12c26")), + FieldElement::_unchecked_from_words(hex_to_words( + "02419f98165871a4389547f2334a53910dfbd15dc41a594d1f07db10ea1a4df4fedc77ec1a9201c4b55ebf900b8a3e17")), + FieldElement::_unchecked_from_words(hex_to_words( + "0d2d7b829ce324d28346fe421f96bb1301458ef0159ebbef7c763e17763a06528e563e9d1ea6d0f5b416af000745fc20")), + FieldElement::_unchecked_from_words(hex_to_words( + "046959cfcfd0bf49724b136c4cf2d9faf563e63704f7092f8f66b3ea59514fa46f2a2619951d823a93096bb538d64615")), + FieldElement::_unchecked_from_words(hex_to_words( + "1017290919210e5f07f897e267a33f1ba06731f1d2bbe1ee41064965946d9b5991e9079c2c02d58fea748d4b6e405346")), + FieldElement::_unchecked_from_words(hex_to_words( + "106d87d1b51d13b9c4b92d15db8acca854759078e5be683807afe37afff55002eecc53161264562a872aa6c17d985097")), +}; +constexpr std::array G1_ISO_YDEN = { + FieldElement::_unchecked_from_words(hex_to_words( + "0a8b981ee47691f152f45700b70d5c69723e71dcc5fc1323ddfa71a0889d5b7e18ef5f8a10634d60eb6c359d47e52b1c")), + FieldElement::_unchecked_from_words(hex_to_words( + "0fadeff77b6bfe3e6a33dca5235607763e50dffea3c62658f25f4cc5e35c65da6f5f037395dbd911616a3c4f5535b9fb")), + FieldElement::_unchecked_from_words(hex_to_words( + "01c2a7a256fe9c4104fe8bb2b8d81af4b6634a652ee5884d115dbe7ad10c2a3724a2c159a3d367422be9b66df470059c")), + FieldElement::_unchecked_from_words(hex_to_words( + "11037cd58b3dbfbd59b0c17f7631448ac8e0bbd6fe11080624482e6b8c2f4e5f898b367476c9073ff27bf8ef3b75a386")), + FieldElement::_unchecked_from_words(hex_to_words( + "044393bb632d94fbb1cce69b6aa9ad9a3cae528fbee9a2a4d30d4fe3ba86fdb11dbf6f1c5fcdb70031c7912ea267eec6")), + FieldElement::_unchecked_from_words(hex_to_words( + "096c3a09773272d40e49df01d942a628104fc1aafb0919cc71b1a4d2f119981d9824c289dd72bb55c66ef6efeeb5c7e8")), + FieldElement::_unchecked_from_words(hex_to_words( + "0c81d4645f4cb6edd466a75599ce728ec4b76271ea6506b3fb1fa3721569734c32dca50a885728f09abc11eb5fadeff4")), + FieldElement::_unchecked_from_words(hex_to_words( + "0d3378023e4c7406f08d33680a2374659e9efb24aa6424c6cb353efe9b33e4ffda64e495b1e879304199f10e5b8be45b")), + FieldElement::_unchecked_from_words(hex_to_words( + "0ef3c33371e2fdb5564c2935a96bfa9303bfd9cca75cbdeb5be603899e907687c341b4aa9fac34977eb4ae92ec74d3a5")), + FieldElement::_unchecked_from_words(hex_to_words( + "0d22d5a40cec7cff6cb95e0fa776aeadd0fda172174ed023773a8ca5196b1380e5d5bd5cb9357a307ee91fd449f6ac2e")), + FieldElement::_unchecked_from_words(hex_to_words( + "0467ffaef23fc49efce95ebdeb5b490a178419613d90d8f87549d8bd057894aedc9d55a83017897bf727e09285fd8519")), + FieldElement::_unchecked_from_words(hex_to_words( + "116aefa749127bff828e0f1e772a53cd6e20829e5c230c455461c75a23ede3b579bc930deac01c03c1769e6a7c385f1b")), + FieldElement::_unchecked_from_words(hex_to_words( + "189e5fe4470cd73cb9000209d5bd08d3fc009d4996dc5153a0ecf39ef026f602bbf18d053a6a3154101c10bf2744c10a")), + FieldElement::_unchecked_from_words(hex_to_words( + "13dddbc4799d81d698738983c2107ff3b0a1ba04228520cc57b2b625b6d4ca21e47d5a981d081b557ebd546ca1575ed2")), + FieldElement::_unchecked_from_words(hex_to_words( + "0ef9c24eccaf5e0efb95832e7d78742efd04e3dfc608646755ba77a9a2f76493039e952cbdb05c2109319f2e39834935")), + FieldElement::_unchecked_from_words(hex_to_words( + "15f65ec3fa80e4935c071a97a256ec6d77ce5853705257455f48985753c758baebf4000bc40c0002760900000002fffd")), +}; + +// The precomputed values -B/A and B/(Z*A) (RFC 9380 6.6.2) +constexpr auto G1_SSWU_C1 = FieldElement::_unchecked_from_words(hex_to_words( + "097cab54770ca0d399fffd1f34fc181d2527e7dc638517671b75faa0105ec9833b40d72430f93c82052583c93555a7fe")); +constexpr auto G1_SSWU_C2 = FieldElement::_unchecked_from_words(hex_to_words( + "1469e7cf3b7ec553bb16a0c0d526ff964ed257417860c764af05f2a3b113ce5770cca69e8ca26edcaefbc579583dc22f")); + +// h_eff = 0xD201000000010001 (RFC 9380 8.8.1) +constexpr uint64_t G1_H_EFF = 0xD201000000010001; + +// G2 SSWU (RFC 9380 8.8.2) and 3-isogeny (E.3) constants, preconverted to Montgomery +constexpr auto G2_SSWU_A = FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "1220b4e979ea546702d6985717c744ab0b51375126310601e7889edbe340f6bd01080c0fdef80285e53a000003135242")); + +constexpr auto G2_SSWU_B = FieldElement2::_unchecked_from_words( + hex_to_words( + "125cdb5e74dc4fd13dd3a569412c0a3475bf3c53a79473ba6e1b94403db5a66e6ec832df71380aa422ea00000cf89db2"), + hex_to_words( + "125cdb5e74dc4fd13dd3a569412c0a3475bf3c53a79473ba6e1b94403db5a66e6ec832df71380aa422ea00000cf89db2")); + +constexpr auto G2_SSWU_Z = FieldElement2::_unchecked_from_words( + hex_to_words( + "0815664c7dfe040dde291a3d41e980d3d951e663066576f40fd0749345d33ad2656fffe5da8ffffa87ebfffffff9555c"), + hex_to_words( + "040ab3263eff0206ef148d1ea0f4c069eca8f3318332bb7a07e83a49a2e99d6932b7fff2ed47fffd43f5fffffffcaaae")); + +// The precomputed values -B/A and B/(Z*A) (RFC 9380 6.6.2) +constexpr auto G2_SSWU_C1 = FieldElement2::_unchecked_from_words( + hex_to_words( + "0e7146f483e23a05467a4ad10ee6de53c68946b6aebbd0629f8e582eefe0fade5f98cc95ce451105903c555555474fb3"), + hex_to_words( + "0b8fcaf5b59dac9504a15ce53464ce839dee04ce44c9425cc7a27a7206cffb45bf133368e30eeefa29c2aaaaaab85af8")); + +constexpr auto G2_SSWU_C2 = FieldElement2::_unchecked_from_words( + hex_to_words( + "127db28a3ce062c407f5d9fd91c1fa91fd963b744ea89b535dd35cd05d972c422585c28393a69d00f2d8444444414324"), + hex_to_words( + "168a1e1ff5de8b82c971692a149d16d034a33031ee9566441c186171cb4d5da5eb72b871590828fc55743333333b3695")); + +constexpr std::array G2_ISO_XNUM = { + FieldElement2::_unchecked_from_words( + hex_to_words( + "13808f550920ea41c54516acc8d037f6048103ea9e6cd0627c80cd2af3fd71a206dd57071206393e47f671c71ce05e62"), + hex_to_words( + "13808f550920ea41c54516acc8d037f6048103ea9e6cd0627c80cd2af3fd71a206dd57071206393e47f671c71ce05e62")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "0ac73310a7fd5abd2836cda7028cabc521c28884088749456a6b4619b26ef918873fffdd236aaaa35fe55555554c71d0")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "149d7861e581393bb70040e2c20556f4d3960742ef416e1cb1fb2f941d797997db0c00101f9eaaae0a0c5555555971c3"), + hex_to_words( + "0563998853fead5e941b66d3814655e290e144420443a4a2b535a30cd9377c8c439fffee91b55551aff2aaaaaaa638e8")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "198e1a74328002d2696eb479f885d059d86485d4c87f6fb1d817050a8f41abc3190955557a84e38e40aac71c71c725ed"), + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), +}; +constexpr std::array G2_ISO_XDEN = { + FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "04f69db13f39a95203977bc86095b0893e6427366f8cec18ca3757cb3819b208f25bfc611da3ff3e1f3affffff13ab97")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "0381be097f0bb4e16140b1fcfb1e54b7b10330b7c0a95bc66f7ee9ce4a6e8b59dcb8009a43480020447600000027552e"), + hex_to_words( + "167f53e0ba7431b8e9daf5b9482d581fb3741acd32dbb6f8f7b1e8d2ac426aca41f3ff646e0bffdf7588ffffffd8557d")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "15f65ec3fa80e4935c071a97a256ec6d77ce5853705257455f48985753c758baebf4000bc40c0002760900000002fffd"), + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), +}; + +constexpr std::array G2_ISO_YNUM = { + FieldElement2::_unchecked_from_words( + hex_to_words( + "08c8055e31c5d5c30fd2e39eada3eba957cb23ecfae804e1184a88ff379652fdb530e4f43b66d0e296d8f684bdfc77be"), + hex_to_words( + "08c8055e31c5d5c30fd2e39eada3eba957cb23ecfae804e1184a88ff379652fdb530e4f43b66d0e296d8f684bdfc77be")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "0c7d13420b09807f02c3b2b2d2938e86a27aa27b1d1a18d59d82f98e5f205aee4d6d55d28b7638fdbf0a71c71c91b406")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "02b1ccc429ff56af4a0db369c0a32af14870a2210221d2515a9ad1866c9bbe4621cffff748daaaa8d7f9555555531c74"), + hex_to_words( + "174f45260f808feb010df44c82a881e61c06a963f163406e0c96011a8a1537ddfcdc000768795556e205aaaaaaac8e37")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "117c5e6e28aa905427f6c0e2f07467641c55c9935b5a982ec9d3d0f2c6f0678dc0fe38e23327b425a470bda12f67f35c"), + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), +}; +constexpr std::array G2_ISO_YDEN = { + FieldElement2::_unchecked_from_words( + hex_to_words( + "03c6a03d41da1151ca713efc0036766011e19fc1a9c875d5561b3c2259e936118f7bea480083fb750162fffffa765adf"), + hex_to_words( + "03c6a03d41da1151ca713efc0036766011e19fc1a9c875d5561b3c2259e936118f7bea480083fb750162fffffa765adf")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hex_to_words( + "0ee3d913bdacfbf60ac6735921c1119bbb2c75a34ea6c44a5ea60761a84d161ad713f52358ebfdba5db0fffffd3b02c5")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "05429d0e3e918f5211e10afb78ad7f138984c913a0fe09a9a73e5eb56fa5d106cb1400e764ec003066b10000003affc5"), + hex_to_words( + "14be74dbfaee5748393a9cbaca9e2dc3daf2827152870915bff273eb870b251d5397ff174c67ffcf534dffffffc4aae6")), + FieldElement2::_unchecked_from_words( + hex_to_words( + "15f65ec3fa80e4935c071a97a256ec6d77ce5853705257455f48985753c758baebf4000bc40c0002760900000002fffd"), + hex_to_words( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), +}; + +constexpr size_t H2C_L = 64; + +FieldElement fe_from_uniform(std::span bytes64) { + std::array padded{}; + copy_mem(padded.data() + 32, bytes64.data(), 64); + return FieldElement::from_bytes_wide(padded); +} + +uint8_t sgn0(const FieldElement& x) { + return x.serialize()[FieldElement::BYTES - 1] & 1; +} + +uint8_t sgn0(const FieldElement2& x) { + // RFC 9380 4.1 sgn0 for extension fields, on (c0, c1) + const uint8_t sign_0 = sgn0(x.c0()); + const uint8_t zero_0 = x.c0().is_zero().into_bitmask() & 1; + const uint8_t sign_1 = sgn0(x.c1()); + return sign_0 | (zero_0 & sign_1); +} + +template +FE eval_polynomial(const std::array& coeffs, const FE& x) { + auto acc = coeffs[N - 1]; + for(size_t i = N - 1; i > 0; --i) { + acc = acc * x + coeffs[i - 1]; + } + return acc; +} + +/** +* Simplified SWU for AB == 0 (RFC 9380 6.6.2 and 6.6.3), returning an +* affine point on the isogenous curve E': y^2 = x^3 + A*x + B +* +* C1 and C2 are the precomputed constants -B/A and B/(Z*A) +*/ +template +std::pair map_to_curve_sswu(const FE& u, const FE& A, const FE& B, const FE& Z, const FE& C1, const FE& C2) { + const auto zu2 = Z * u.square(); + + // invert() maps zero to zero, as inv0 requires + const auto tv1 = (zu2.square() + zu2).invert(); + + FE x1; + if(tv1.is_zero().as_bool()) { + // x1 = B / (Z * A) + x1 = C2; + } else { + // x1 = (-B / A) * (1 + tv1) + x1 = C1 * (FE::one() + tv1); + } + + const auto gx1 = (x1.square() + A) * x1 + B; + const auto x2 = zu2 * x1; + const auto gx2 = (x2.square() + A) * x2 + B; + + FE x; + FE y; + if(const auto y1 = gx1.sqrt()) { + x = x1; + y = *y1; + } else { + const auto y2 = gx2.sqrt(); + BOTAN_ASSERT_NOMSG(y2.has_value()); + x = x2; + y = *y2; + } + + if(sgn0(u) != sgn0(y)) { + y = y.negate(); + } + + return {x, y}; +} + +std::optional> map_to_curve_g1(const FieldElement& u) { + const auto [xp, yp] = map_to_curve_sswu(u, G1_SSWU_A, G1_SSWU_B, G1_SSWU_Z, G1_SSWU_C1, G1_SSWU_C2); + + // The 11-isogeny map from E' to E (RFC 9380 E.2) + const auto xnum = eval_polynomial(G1_ISO_XNUM, xp); + const auto xden = eval_polynomial(G1_ISO_XDEN, xp); + const auto ynum = eval_polynomial(G1_ISO_YNUM, xp); + const auto yden = eval_polynomial(G1_ISO_YDEN, xp); + + if((xden.is_zero() || yden.is_zero()).as_bool()) { + // The exceptional case maps to the point at infinity + return {}; + } + + // One shared inversion for both denominators + const auto den_inv = (xden * yden).invert(); + + return std::pair{xnum * yden * den_inv, yp * ynum * xden * den_inv}; +} + +std::optional> map_to_curve_g2(const FieldElement2& u) { + const auto [xp, yp] = map_to_curve_sswu(u, G2_SSWU_A, G2_SSWU_B, G2_SSWU_Z, G2_SSWU_C1, G2_SSWU_C2); + + // The 3-isogeny map from E' to E (RFC 9380 E.3) + const auto xnum = eval_polynomial(G2_ISO_XNUM, xp); + const auto xden = eval_polynomial(G2_ISO_XDEN, xp); + const auto ynum = eval_polynomial(G2_ISO_YNUM, xp); + const auto yden = eval_polynomial(G2_ISO_YDEN, xp); + + if((xden.is_zero() || yden.is_zero()).as_bool()) { + // The exceptional case maps to the point at infinity + return {}; + } + + // One shared inversion for both denominators + const auto den_inv = (xden * yden).invert(); + + return std::pair{xnum * yden * den_inv, yp * ynum * xden * den_inv}; +} + +G1Projective clear_cofactor_g1(const G1Projective& pt) { + // Multiplication by h_eff (RFC 9380 8.8.1) + auto accum = G1Projective::identity(); + for(size_t b = 64; b > 0; --b) { + accum = accum.add(accum); + if(((G1_H_EFF >> (b - 1)) & 1) == 1) { + accum = accum.add(pt); + } + } + return accum; +} + +template +std::array bls_expand_message(std::span input, std::span dst) { + if(dst.empty()) { + // RFC 9380 section 3.1: "Tags MUST have nonzero length." + throw Invalid_Argument("BLS12_381 requires a non-empty domain separation tag"); + } + + // Tags longer than 255 bytes are handled inside expand_message_xmd, + // by hashing per RFC 9380 section 5.3.3 + std::array output{}; + expand_message_xmd("SHA-256", output, input, dst); + return output; +} + +} // namespace + +//static +Scalar Scalar::hash(std::span input, std::span dst) { + // L = ceil((ceil(log2(r)) + k) / 8) = 48, for k = 128 (RFC 9380) + auto uniform = bls_expand_message<48>(input, dst); + + std::array padded{}; + copy_mem(padded.data() + padded.size() - uniform.size(), uniform.data(), uniform.size()); + const auto s = Scalar::from_bytes_wide(padded); + + // The result may be a secret (eg a key derived from a seed) + secure_scrub_memory(uniform); + secure_scrub_memory(padded); + + return s; +} + +namespace { + +G1Projective g1_from_mapped(const std::optional>& m) { + return m ? G1Projective::_unchecked_from_affine_coords(m->first, m->second) : G1Projective::identity(); +} + +G2Projective g2_from_mapped(const std::optional>& m) { + return m ? G2Projective::_unchecked_from_affine_coords(m->first, m->second) : G2Projective::identity(); +} + +} // namespace + +//static +G1Projective G1Projective::hash_to_curve_ro(std::span input, std::span dst) { + const auto uniform = bls_expand_message<2 * H2C_L>(input, dst); + + const auto u0 = fe_from_uniform(std::span{uniform}.first()); + const auto u1 = fe_from_uniform(std::span{uniform}.last()); + + const auto q0 = g1_from_mapped(map_to_curve_g1(u0)); + const auto q1 = g1_from_mapped(map_to_curve_g1(u1)); + + return clear_cofactor_g1(q0.add(q1)); +} + +//static +G1Projective G1Projective::hash_to_curve_nu(std::span input, std::span dst) { + const auto uniform = bls_expand_message(input, dst); + + return clear_cofactor_g1(g1_from_mapped(map_to_curve_g1(fe_from_uniform(uniform)))); +} + +//static +G2Projective G2Projective::hash_to_curve_ro(std::span input, std::span dst) { + const auto uniform = bls_expand_message<4 * H2C_L>(input, dst); + + const auto us = std::span{uniform}; + const auto u0 = FieldElement2(fe_from_uniform(us.subspan(0, H2C_L)), fe_from_uniform(us.subspan(H2C_L, H2C_L))); + const auto u1 = + FieldElement2(fe_from_uniform(us.subspan(2 * H2C_L, H2C_L)), fe_from_uniform(us.subspan(3 * H2C_L, H2C_L))); + + const auto q0 = g2_from_mapped(map_to_curve_g2(u0)); + const auto q1 = g2_from_mapped(map_to_curve_g2(u1)); + + return q0.add(q1).clear_cofactor(); +} + +//static +G2Projective G2Projective::hash_to_curve_nu(std::span input, std::span dst) { + const auto uniform = bls_expand_message<2 * H2C_L>(input, dst); + + const auto u = FieldElement2(fe_from_uniform(std::span{uniform}.first()), + fe_from_uniform(std::span{uniform}.last())); + + return g2_from_mapped(map_to_curve_g2(u)).clear_cofactor(); +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_pairing.cpp b/src/lib/math/bls12_381/bls12_381_pairing.cpp new file mode 100644 index 00000000000..cbaa9aa53ea --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_pairing.cpp @@ -0,0 +1,285 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +namespace { + +/** +* The Miller loop accumulator point, in Jacobian coordinates +*/ +struct MillerG2 { + FieldElement2 x; + FieldElement2 y; + FieldElement2 z; +}; + +struct LineEval { + FieldElement2 c0; + FieldElement2 c1; + FieldElement2 c2; +}; + +struct PairingTerm { + FieldElement px; + FieldElement py; + FieldElement2 qx; + FieldElement2 qy; + MillerG2 cur; +}; + +LineEval doubling_step(MillerG2& r) { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + const auto tmp0 = r.x.square(); + const auto tmp1 = r.y.square(); + const auto tmp2 = tmp1.square(); + auto tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; + tmp3 = tmp3 + tmp3; + const auto tmp4 = tmp0 + tmp0 + tmp0; + auto tmp6 = r.x + tmp4; + const auto tmp5 = tmp4.square(); + const auto zsquared = r.z.square(); + r.x = tmp5 - tmp3 - tmp3; + r.z = (r.z + r.y).square() - tmp1 - zsquared; + r.y = (tmp3 - r.x) * tmp4; + auto tmp2_8 = tmp2 + tmp2; + tmp2_8 = tmp2_8 + tmp2_8; + tmp2_8 = tmp2_8 + tmp2_8; + r.y = r.y - tmp2_8; + auto ltmp3 = tmp4 * zsquared; + ltmp3 = ltmp3 + ltmp3; + ltmp3 = ltmp3.negate(); + tmp6 = tmp6.square() - tmp0 - tmp5; + auto tmp1_4 = tmp1 + tmp1; + tmp1_4 = tmp1_4 + tmp1_4; + tmp6 = tmp6 - tmp1_4; + auto ltmp0 = r.z * zsquared; + ltmp0 = ltmp0 + ltmp0; + + return LineEval{ltmp0, ltmp3, tmp6}; +} + +LineEval addition_step(MillerG2& r, const FieldElement2& qx, const FieldElement2& qy) { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + const auto zsquared = r.z.square(); + const auto ysquared = qy.square(); + const auto t0 = zsquared * qx; + const auto t1 = ((qy + r.z).square() - ysquared - zsquared) * zsquared; + const auto t2 = t0 - r.x; + const auto t3 = t2.square(); + auto t4 = t3 + t3; + t4 = t4 + t4; + const auto t5 = t4 * t2; + const auto t6 = t1 - r.y - r.y; + auto t9 = t6 * qx; + const auto t7 = t4 * r.x; + r.x = t6.square() - t5 - t7 - t7; + r.z = (r.z + t2).square() - zsquared - t3; + auto t10 = qy + r.z; + const auto t8 = (t7 - r.x) * t6; + auto t0y = r.y * t5; + t0y = t0y + t0y; + r.y = t8 - t0y; + t10 = t10.square() - ysquared; + const auto ztsquared = r.z.square(); + t10 = t10 - ztsquared; + t9 = t9 + t9 - t10; + const auto lt10 = r.z + r.z; + const auto t6neg = t6.negate(); + const auto lt1 = t6neg + t6neg; + + return LineEval{lt10, lt1, t9}; +} + +Fp12 ell(const Fp12& f, const LineEval& line, const FieldElement& px, const FieldElement& py) { + const auto c0 = FieldElement2(line.c0.c0() * py, line.c0.c1() * py); + const auto c1 = FieldElement2(line.c1.c0() * px, line.c1.c1() * px); + + return f.mul_by_014(line.c2, c1, c0); +} + +Fp12 multi_miller_loop(std::span terms) { + auto f = Fp12::one(); + + bool found_one = false; + for(size_t b = 64; b > 0; --b) { + const bool i = (((BLS_Z_ABS >> 1) >> (b - 1)) & 1) == 1; + if(!found_one) { + found_one = i; + continue; + } + + for(auto& term : terms) { + f = ell(f, doubling_step(term.cur), term.px, term.py); + } + + if(i) { + for(auto& term : terms) { + f = ell(f, addition_step(term.cur, term.qx, term.qy), term.px, term.py); + } + } + + f = f.square(); + } + + for(auto& term : terms) { + f = ell(f, doubling_step(term.cur), term.px, term.py); + } + + // z is negative + return f.conjugate(); +} + +Fp12 cyclotomic_exp(const Fp12& f) { + // Exponentiation by |z|, in the cyclotomic subgroup, negated by + // conjugation since z is negative + auto tmp = Fp12::one(); + bool found_one = false; + for(size_t b = 64; b > 0; --b) { + const bool i = ((BLS_Z_ABS >> (b - 1)) & 1) == 1; + if(found_one) { + tmp = tmp.cyclotomic_square(); + } else { + found_one = i; + } + + if(i) { + tmp = tmp * f; + } + } + + return tmp.conjugate(); +} + +Fp12 final_exponentiation(const Fp12& ml) { + auto f = ml; + auto t0 = f.frobenius_map().frobenius_map().frobenius_map().frobenius_map().frobenius_map().frobenius_map(); + auto t1 = f.invert(); + auto t2 = t0 * t1; + t1 = t2; + t2 = t2.frobenius_map().frobenius_map(); + t2 = t2 * t1; + t1 = t2.cyclotomic_square().conjugate(); + auto t3 = cyclotomic_exp(t2); + auto t4 = t3.cyclotomic_square(); + auto t5 = t1 * t3; + t1 = cyclotomic_exp(t5); + t0 = cyclotomic_exp(t1); + auto t6 = cyclotomic_exp(t0); + t6 = t6 * t4; + t4 = cyclotomic_exp(t6); + t5 = t5.conjugate(); + t4 = t4 * t5 * t2; + t5 = t2.conjugate(); + t1 = t1 * t2; + t1 = t1.frobenius_map().frobenius_map().frobenius_map(); + t6 = t6 * t5; + t6 = t6.frobenius_map(); + t3 = t3 * t0; + t3 = t3.frobenius_map().frobenius_map(); + t3 = t3 * t1; + t3 = t3 * t6; + f = t3 * t4; + + return f; +} + +PairingTerm make_term(const G1Affine& p, const G2Affine& q) { + const auto qx = q._x(); + const auto qy = q._y(); + return PairingTerm{p._x(), p._y(), qx, qy, MillerG2{qx, qy, FieldElement2::one()}}; +} + +} // namespace + +Gt::Gt(const Fp12& v) : m_coeffs{} { + constexpr size_t N = FieldElement::N; + + const std::array c = { + &v.c0().c0(), &v.c0().c1(), &v.c0().c2(), &v.c1().c0(), &v.c1().c1(), &v.c1().c2()}; + + for(size_t i = 0; i != c.size(); ++i) { + copy_mem(m_coeffs.data() + (2 * i) * N, c[i]->c0()._words().data(), N); + copy_mem(m_coeffs.data() + (2 * i + 1) * N, c[i]->c1()._words().data(), N); + } +} + +Fp12 Gt::_to_fp12() const { + constexpr size_t N = FieldElement::N; + + std::array c; + for(size_t i = 0; i != c.size(); ++i) { + std::array c0{}; + std::array c1{}; + copy_mem(c0.data(), m_coeffs.data() + (2 * i) * N, N); + copy_mem(c1.data(), m_coeffs.data() + (2 * i + 1) * N, N); + c[i] = FieldElement2::_unchecked_from_words(c0, c1); + } + + return Fp12(Fp6(c[0], c[1], c[2]), Fp6(c[3], c[4], c[5])); +} + +//static +Gt Gt::identity() { + return Gt(Fp12::one()); +} + +bool Gt::is_identity() const { + return (*this) == Gt::identity(); +} + +bool Gt::operator==(const Gt& other) const { + // The Montgomery representation of a reduced element is unique + return CT::is_equal(m_coeffs.data(), other.m_coeffs.data(), m_coeffs.size()).as_bool(); +} + +std::array Gt::serialize() const { + return this->_to_fp12().serialize(); +} + +Gt Gt::pairing(const G1Affine& p, const G2Affine& q) { + if(p.is_identity() || q.is_identity()) { + return Gt::identity(); + } + + std::array terms = {make_term(p, q)}; + + return Gt(final_exponentiation(multi_miller_loop(terms))); +} + +Gt Gt::multi_pairing(std::span p, std::span q) { + if(p.size() != q.size()) { + throw Invalid_Argument("BLS12_381::multi_pairing spans must have equal length"); + } + + std::vector terms; + terms.reserve(p.size()); + + for(size_t i = 0; i != p.size(); ++i) { + // By bilinearity e(I,Q) = e(P,I) = I, contributing nothing to the product + if(p[i].is_identity() || q[i].is_identity()) { + continue; + } + terms.push_back(make_term(p[i], q[i])); + } + + if(terms.empty()) { + return Gt::identity(); + } + + return Gt(final_exponentiation(multi_miller_loop(terms))); +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_point_mul.h b/src/lib/math/bls12_381/bls12_381_point_mul.h new file mode 100644 index 00000000000..1ed279d8f4d --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_point_mul.h @@ -0,0 +1,293 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BLS12_381_POINT_MUL_H_ +#define BOTAN_BLS12_381_POINT_MUL_H_ + +#include +#include +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +// z = -0xd201000000010000 (negative; the Miller loop and final exponentiation +// use |z| and account for the sign by conjugation) +constexpr uint64_t BLS_Z_ABS = 0xD201000000010000; + +/** +* Window size heuristic for the Pippenger bucket method, +* approximately ln(n) + 2 +*/ +inline size_t pippenger_window_bits(size_t n) { + if(n < 32) { + return 3; + } + size_t log2n = 5; + while(n >= (static_cast(2) << log2n)) { + ++log2n; + } + return std::min((log2n * 69) / 100 + 2, 16); +} + +/** +* Extract the c-bit window starting at bit offset bit (counted from the +* least significant end) of a big-endian encoded scalar +*/ +inline size_t scalar_window_at(std::span bytes, size_t bit, size_t c) { + size_t digit = 0; + for(size_t k = 0; k != c; ++k) { + const size_t b = bit + k; + if(b >= 8 * Scalar::BYTES) { + break; + } + const uint8_t byte = bytes[Scalar::BYTES - 1 - (b / 8)]; + digit |= static_cast((byte >> (b % 8)) & 1) << k; + } + return digit; +} + +/** +* Return the i'th 4-bit window of a big-endian encoded scalar, counting +* from the most significant +*/ +inline uint8_t scalar_nibble(std::span bytes, size_t i) { + return (i % 2 == 0) ? (bytes[i / 2] >> 4) : (bytes[i / 2] & 0x0F); +} + +/** +* The scalar multiplication algorithms, shared by G1 and G2; the group +* specific formulas remain member functions of the point types. +*/ +template +class PointMul final { + public: + /** + * Return pts[idx], without leaking idx + */ + static Pt ct_select(std::span pts, size_t idx) { + auto result = Pt::identity(); + + for(size_t i = 0; i != pts.size(); ++i) { + const auto cnd = CT::Mask::is_equal(i, idx).as_choice(); + CT::conditional_assign_mem(cnd, result.m_x.data(), pts[i].m_x.data(), result.m_x.size()); + CT::conditional_assign_mem(cnd, result.m_y.data(), pts[i].m_y.data(), result.m_y.size()); + CT::conditional_assign_mem(cnd, result.m_z.data(), pts[i].m_z.data(), result.m_z.size()); + } + + return result; + } + + /** + * Constant time fixed window multiplication + */ + static Pt mul(const Pt& pt, const Scalar& scalar) { + constexpr size_t WINDOW_BITS = 4; + constexpr size_t TABLE_SIZE = 1 << WINDOW_BITS; + + std::array tbl; + for(size_t i = 1; i != TABLE_SIZE; ++i) { + tbl[i] = tbl[i - 1].add(pt); + } + + auto sbytes = scalar.serialize(); + + auto accum = Pt::identity(); + + for(size_t i = 0; i != 2 * sbytes.size(); ++i) { + if(i > 0) { + for(size_t j = 0; j != WINDOW_BITS; ++j) { + accum = accum.dbl(); + } + } + accum = accum.add(ct_select(tbl, scalar_nibble(sbytes, i))); + } + + secure_scrub_memory(sbytes); + + return accum; + } + + /** + * Constant time a*p + b*q + */ + static Pt mul2(const Pt& p, const Scalar& a, const Pt& q, const Scalar& b) { + constexpr size_t WINDOW_BITS = 4; + constexpr size_t TABLE_SIZE = 1 << WINDOW_BITS; + + // Interleaved Strauss-Shamir; both scalars share one doubling chain + std::array ptbl; + std::array qtbl; + for(size_t i = 1; i != TABLE_SIZE; ++i) { + ptbl[i] = ptbl[i - 1].add(p); + qtbl[i] = qtbl[i - 1].add(q); + } + + auto abytes = a.serialize(); + auto bbytes = b.serialize(); + + auto accum = Pt::identity(); + + for(size_t i = 0; i != 2 * abytes.size(); ++i) { + if(i > 0) { + for(size_t j = 0; j != WINDOW_BITS; ++j) { + accum = accum.dbl(); + } + } + accum = accum.add(ct_select(ptbl, scalar_nibble(abytes, i))); + accum = accum.add(ct_select(qtbl, scalar_nibble(bbytes, i))); + } + + secure_scrub_memory(abytes); + secure_scrub_memory(bbytes); + + return accum; + } + + /** + * Variable time a*p + b*q + */ + static Pt mul2_vartime(const Pt& p, const Scalar& a, const Pt& q, const Scalar& b) { + constexpr size_t WINDOW_BITS = 4; + constexpr size_t TABLE_SIZE = 1 << WINDOW_BITS; + + std::array ptbl; + std::array qtbl; + for(size_t i = 1; i != TABLE_SIZE; ++i) { + ptbl[i] = ptbl[i - 1].add(p); + qtbl[i] = qtbl[i - 1].add(q); + } + + auto abytes = a.serialize(); + auto bbytes = b.serialize(); + + auto accum = Pt::identity(); + + for(size_t i = 0; i != 2 * abytes.size(); ++i) { + if(i > 0) { + for(size_t j = 0; j != WINDOW_BITS; ++j) { + accum = accum.dbl(); + } + } + const uint8_t wa = scalar_nibble(abytes, i); + const uint8_t wb = scalar_nibble(bbytes, i); + if(wa > 0) { + accum = accum.add(ptbl[wa]); + } + if(wb > 0) { + accum = accum.add(qtbl[wb]); + } + } + + secure_scrub_memory(abytes); + secure_scrub_memory(bbytes); + + return accum; + } + + /** + * Variable time multiscalar multiplication + */ + template + static Pt msm_vartime(std::span points, std::span scalars) { + if(points.size() != scalars.size()) { + throw Invalid_Argument("BLS12_381 msm_vartime spans must have equal length"); + } + + if(points.empty()) { + return Pt::identity(); + } + + // Below this size a chain of 2-ary multiplications beats the + // Pippenger bucket method (measured crossover; not very sensitive) + constexpr size_t PIPPENGER_MIN_SIZE = 16; + + if(points.size() < PIPPENGER_MIN_SIZE) { + auto accum = Pt::identity(); + size_t i = 0; + for(; i + 2 <= points.size(); i += 2) { + accum = accum.add( + mul2_vartime(Pt::from_affine(points[i]), scalars[i], Pt::from_affine(points[i + 1]), scalars[i + 1])); + } + if(i < points.size()) { + accum = accum.add(mul(Pt::from_affine(points[i]), scalars[i])); + } + return accum; + } + + // The Pippenger bucket method; process the scalars in c-bit windows + // from the most significant down, adding each point into the bucket + // its window digit selects, then form the window sum with a running + // sum over the buckets. + const size_t c = pippenger_window_bits(points.size()); + const size_t windows = (8 * Scalar::BYTES + c - 1) / c; + + std::vector> sbytes; + sbytes.reserve(scalars.size()); + for(const auto& scalar : scalars) { + sbytes.push_back(scalar.serialize()); + } + + std::vector buckets((static_cast(1) << c) - 1); + + auto accum = Pt::identity(); + + for(size_t w = 0; w != windows; ++w) { + if(w > 0) { + for(size_t j = 0; j != c; ++j) { + accum = accum.dbl(); + } + } + + std::ranges::fill(buckets, Pt::identity()); + + const size_t bit = (windows - 1 - w) * c; + for(size_t i = 0; i != points.size(); ++i) { + const size_t digit = scalar_window_at(sbytes[i], bit, c); + if(digit > 0) { + buckets[digit - 1] = buckets[digit - 1].add_mixed(points[i]); + } + } + + auto running = Pt::identity(); + auto window_sum = Pt::identity(); + for(size_t b = buckets.size(); b > 0; --b) { + running = running.add(buckets[b - 1]); + window_sum = window_sum.add(running); + } + + accum = accum.add(window_sum); + } + + secure_scrub_memory(sbytes.data(), sbytes.size() * Scalar::BYTES); + + return accum; + } + + /** + * Multiplication by |z|, the absolute value of the BLS parameter, + * used for the subgroup checks and cofactor clearing + */ + static Pt mul_by_z_abs(const Pt& pt) { + auto accum = Pt::identity(); + + for(size_t b = 64; b > 0; --b) { + accum = accum.dbl(); + // The BLS parameter is a public constant so this branch leaks nothing + if(((BLS_Z_ABS >> (b - 1)) & 1) == 1) { + accum = accum.add(pt); + } + } + + return accum; + } +}; + +} // namespace Botan::BLS12_381 + +#endif diff --git a/src/lib/math/bls12_381/bls12_381_scalar.cpp b/src/lib/math/bls12_381/bls12_381_scalar.cpp new file mode 100644 index 00000000000..cea77f13bb2 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_scalar.cpp @@ -0,0 +1,246 @@ +/* +* (C) 2024,2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan::BLS12_381 { + +namespace { + +constexpr auto SCALAR_P = hex_to_words("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + +// R1 = 2**256 % p +constexpr auto SCALAR_R1 = hex_to_words("1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe"); + +// R2 = (R1**2) % p +constexpr auto SCALAR_R2 = hex_to_words("748d9d99f59ff1105d314967254398f2b6cedcb87925c23c999e990f3f29c6d"); + +// R3 = (R1*R2) % p +constexpr auto SCALAR_R3 = hex_to_words("6e2a5bb9c8db33e973d13c71c7b5f4181b3e0d188cf06990c62c1807439b73af"); + +constexpr word SCALAR_P_DASH = monty_inverse(SCALAR_P[0]); + +std::array scalar_redc(const std::array& v) { + std::array r{}; + std::array ws{}; + bigint_monty_redc(r.data(), v.data(), SCALAR_P.data(), SCALAR_P.size(), SCALAR_P_DASH, ws.data(), ws.size()); + return r; +} + +std::array scalar_to_rep(std::array v) { + std::array z{}; + comba_mul(z.data(), v.data(), SCALAR_R2.data()); + return scalar_redc(z); +} + +std::array scalar_wide_to_rep(const std::array& v) { + auto redc_v = scalar_redc(v); + std::array z{}; + comba_mul(z.data(), redc_v.data(), SCALAR_R3.data()); + return scalar_redc(z); +} + +std::array scalar_from_rep(const std::array& v) { + std::array z{}; + copy_mem(z.data(), v.data(), v.size()); + return scalar_redc(z); +} + +} // namespace + +void Scalar::wipe() { + secure_scrub_memory(m_val); +} + +//static +Scalar Scalar::from_words(std::array v) { + return Scalar(scalar_to_rep(v)); +} + +//static +Scalar Scalar::from_u32(uint32_t v) { + std::array w{}; + w[0] = v; + return Scalar::from_words(w); +} + +//static +Scalar Scalar::one() { + return Scalar(SCALAR_R1); +} + +std::optional Scalar::deserialize(std::span bytes) { + if(bytes.size() != Scalar::BYTES) { + return {}; + } + + const auto words = bytes_to_words(bytes.first()); + + if(!bigint_ct_is_lt(words.data(), N, SCALAR_P.data(), N).as_bool()) { + return {}; + } + + // Reject the zero scalar; a zero secret key, challenge, or nonce is + // essentially always a mistake. This matches EC_Scalar::deserialize. + if(CT::all_zeros(words.data(), N).as_bool()) { + return {}; + } + + return Scalar::from_words(words); +} + +Scalar Scalar::from_bytes_wide(std::span bytes) { + return Scalar(scalar_wide_to_rep(bytes_to_words(bytes))); +} + +void Scalar::serialize_to(std::span bytes) const { + auto v = scalar_from_rep(m_val); + std::reverse(v.begin(), v.end()); + store_be(bytes, v); + // The scalar may be a secret; don't leave an extra copy on the stack + secure_scrub_memory(v); +} + +Scalar Scalar::add(const Scalar& other) const { + std::array t{}; + word carry = 0; + for(size_t i = 0; i != Scalar::N; ++i) { + t[i] = word_add(m_val[i], other.m_val[i], &carry); + } + + std::array r{}; + bigint_monty_maybe_sub(r.data(), carry, t.data(), SCALAR_P.data()); + return Scalar(r); +} + +Scalar Scalar::sub(const Scalar& other) const { + return this->add(other.negate()); +} + +Scalar Scalar::mul(const Scalar& other) const { + std::array z{}; + comba_mul(z.data(), data(), other.data()); + return Scalar(scalar_redc(z)); +} + +Scalar Scalar::square() const { + std::array z{}; + comba_sqr(z.data(), data()); + return Scalar(scalar_redc(z)); +} + +void Scalar::square_n(size_t n) { + std::array z{}; + for(size_t i = 0; i != n; ++i) { + comba_sqr(z.data(), this->data()); + m_val = scalar_redc(z); + } +} + +Scalar Scalar::negate() const { + auto v_is_zero = CT::all_zeros(this->data(), N); + + std::array r{}; + bigint_sub3(r.data(), SCALAR_P.data(), N, this->data(), N); + v_is_zero.if_set_zero_out(r.data(), N); + return Scalar(r); +} + +Scalar Scalar::invert() const { + // Addition chain for exponentiation to p - 2 + // Found using https://github.com/mmcloughlin/addchain + auto t3 = this->square(); + auto z = t3 * (*this); + auto t14 = (*this) * z; + auto t2 = t14 * t3; + auto t4 = t2.square(); + auto t8 = t2 * t4; + auto t5 = t8 * (*this); + auto t11 = t5 * z; + auto t0 = t11 * t3; + auto t9 = t0 * t3; + auto t1 = t11 * t4; + auto t10 = t1 * t5; + auto t6 = t10 * t2; + t2 = t10 * t11; + auto t7 = t2 * t3; + t5 = t6 * t9; + t8 *= t5; + t3 *= t8; + t9 *= t3; + auto t12 = t11 * t9; + auto t13 = t12 * t14; + t1 *= t13; + t11 = t0 * t1; + t14 *= t11; + auto t15 = t14 * t4; + t4 = t0 * t11; + t0 *= t15; + t15.square_n(8); + t14 *= t15; + t14.square_n(9); + t13 *= t14; + t13.square_n(9); + t13 *= t12; + t13.square_n(9); + t12 *= t13; + t12.square_n(8); + t11 *= t12; + t11.square_n(6); + t10 *= t11; + t10.square_n(10); + t9 *= t10; + t9.square_n(9); + t8 *= t9; + t8.square_n(8); + t8 *= t6; + t8.square_n(8); + t8 *= (*this); + t8.square_n(14); + t7 *= t8; + t7.square_n(10); + t6 *= t7; + t6.square_n(15); + t5 *= t6; + t5.square_n(10); + t4 *= t5; + t4.square_n(8); + t3 *= t4; + t3.square_n(16); + t3 *= t1; + t3.square_n(8); + t3 *= t0; + t3.square_n(7); + t2 *= t3; + t2.square_n(9); + t2 *= t0; + t2.square_n(8); + t2 *= t1; + t2.square_n(8); + t2 *= t0; + t2.square_n(8); + t2 *= t0; + t2.square_n(8); + t2 *= t0; + t2.square_n(8); + t1 *= t2; + t1.square_n(8); + t1 *= t0; + t1.square_n(8); + t1 *= t0; + t1.square_n(8); + t0 *= t1; + t0.square_n(2); + z *= t0; + return z; +} + +} // namespace Botan::BLS12_381 diff --git a/src/lib/math/bls12_381/bls12_381_tower.h b/src/lib/math/bls12_381/bls12_381_tower.h new file mode 100644 index 00000000000..15cf53c7608 --- /dev/null +++ b/src/lib/math/bls12_381/bls12_381_tower.h @@ -0,0 +1,193 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BLS12_381_TOWER_H_ +#define BOTAN_BLS12_381_TOWER_H_ + +#include + +namespace Botan::BLS12_381 { + +/** +* An element of Fp6 = Fp2[v]/(v^3 - (u + 1)) +* +* Represented as c0 + c1*v + c2*v^2 +*/ +class BOTAN_TEST_API Fp6 final { + public: + // Default zero initialized + constexpr Fp6() = default; + + constexpr Fp6(const FieldElement2& c0, const FieldElement2& c1, const FieldElement2& c2) : + m_c0(c0), m_c1(c1), m_c2(c2) {} + + static Fp6 zero() { return Fp6(); } + + static Fp6 one() { return Fp6(FieldElement2::one(), FieldElement2::zero(), FieldElement2::zero()); } + + const FieldElement2& c0() const { return m_c0; } + + const FieldElement2& c1() const { return m_c1; } + + const FieldElement2& c2() const { return m_c2; } + + CT::Choice is_zero() const; + + Fp6 add(const Fp6& x) const; + + Fp6 sub(const Fp6& x) const; + + Fp6 mul(const Fp6& x) const; + + Fp6 square() const; + + Fp6 negate() const; + + /** + * Return the inverse of this element, or zero for zero + */ + Fp6 invert() const; + + /** + * Multiplication by v + */ + Fp6 mul_by_nonresidue() const; + + /** + * Sparse multiplication by b1*v + */ + Fp6 mul_by_1(const FieldElement2& b1) const; + + /** + * Sparse multiplication by b0 + b1*v + */ + Fp6 mul_by_01(const FieldElement2& b0, const FieldElement2& b1) const; + + /** + * The Frobenius endomorphism x -> x^p + */ + Fp6 frobenius_map() const; + + CT::Choice operator==(const Fp6& other) const; + + void _conditional_assign(CT::Choice cnd, const Fp6& other); + + private: + FieldElement2 m_c0; + FieldElement2 m_c1; + FieldElement2 m_c2; +}; + +inline Fp6 operator+(const Fp6& a, const Fp6& b) { + return a.add(b); +} + +inline Fp6 operator-(const Fp6& a, const Fp6& b) { + return a.sub(b); +} + +inline Fp6 operator*(const Fp6& a, const Fp6& b) { + return a.mul(b); +} + +/** +* An element of Fp12 = Fp6[w]/(w^2 - v) +* +* Represented as c0 + c1*w +*/ +class BOTAN_TEST_API Fp12 final { + public: + static constexpr size_t BYTES = 12 * FieldElement::BYTES; + + // Default zero initialized + constexpr Fp12() = default; + + constexpr Fp12(const Fp6& c0, const Fp6& c1) : m_c0(c0), m_c1(c1) {} + + static Fp12 zero() { return Fp12(); } + + static Fp12 one() { return Fp12(Fp6::one(), Fp6::zero()); } + + const Fp6& c0() const { return m_c0; } + + const Fp6& c1() const { return m_c1; } + + CT::Choice is_zero() const; + + /** + * Serialization for tests and Gt; the coefficients are encoded in + * the order c0.c0.c0, c0.c0.c1, c0.c1.c0, ..., c1.c2.c1 with each + * Fp coefficient as 48 big-endian bytes + */ + void serialize_to(std::span bytes) const; + + std::array serialize() const { + std::array buf{}; + this->serialize_to(buf); + return buf; + } + + Fp12 add(const Fp12& x) const; + + Fp12 sub(const Fp12& x) const; + + Fp12 mul(const Fp12& x) const; + + Fp12 square() const; + + Fp12 negate() const; + + /** + * Return the inverse of this element, or zero for zero + */ + Fp12 invert() const; + + /** + * Conjugation c0 - c1*w; for elements of the cyclotomic subgroup + * (such as pairing values) this is the inverse + */ + Fp12 conjugate() const; + + /** + * Sparse multiplication by b = (b0 + b1*v)*1 + (b4*v)*w, the shape + * produced by pairing line evaluations + */ + Fp12 mul_by_014(const FieldElement2& b0, const FieldElement2& b1, const FieldElement2& b4) const; + + /** + * The Frobenius endomorphism x -> x^p + */ + Fp12 frobenius_map() const; + + /** + * Squaring specialized for elements of the cyclotomic subgroup + */ + Fp12 cyclotomic_square() const; + + CT::Choice operator==(const Fp12& other) const; + + void _conditional_assign(CT::Choice cnd, const Fp12& other); + + private: + Fp6 m_c0; + Fp6 m_c1; +}; + +inline Fp12 operator+(const Fp12& a, const Fp12& b) { + return a.add(b); +} + +inline Fp12 operator-(const Fp12& a, const Fp12& b) { + return a.sub(b); +} + +inline Fp12 operator*(const Fp12& a, const Fp12& b) { + return a.mul(b); +} + +} // namespace Botan::BLS12_381 + +#endif diff --git a/src/lib/math/bls12_381/info.txt b/src/lib/math/bls12_381/info.txt new file mode 100644 index 00000000000..b4cd611ade9 --- /dev/null +++ b/src/lib/math/bls12_381/info.txt @@ -0,0 +1,23 @@ + +BLS12_381 -> 20241115 + + + +name -> "BLS12-381" + + + +bls12_381.h + + + +bls12_381_fields.h +bls12_381_point_mul.h +bls12_381_tower.h + + + +mp +sha2_32 +xmd + diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h index 1a7aea03c73..2db4261bb19 100644 --- a/src/lib/math/mp/mp_core.h +++ b/src/lib/math/mp/mp_core.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1066,6 +1067,34 @@ constexpr std::array redc_crandall(std::span z) { return r; } +template +inline constexpr auto bytes_to_words(std::span bytes) { + static_assert(L <= WordInfo::bytes * N); + + std::array r = {}; + + constexpr size_t full_words = L / WordInfo::bytes; + constexpr size_t extra_bytes = L % WordInfo::bytes; + + static_assert(full_words + (extra_bytes ? 1 : 0) <= N); + + for(size_t i = 0; i != full_words; ++i) { + r[i] = load_be(bytes.data(), full_words - 1 - i); + } + + if constexpr(extra_bytes > 0) { + constexpr size_t shift = extra_bytes * 8; + shift_left(r); + + for(size_t i = 0; i != extra_bytes; ++i) { + const W b0 = bytes[WordInfo::bytes * full_words + i]; + r[0] |= (b0 << (8 * (extra_bytes - 1 - i))); + } + } + + return r; +} + // Extract a WindowBits sized window out of s, depending on offset. template constexpr size_t read_window_bits(std::span words, size_t offset) { diff --git a/src/lib/math/pcurves/pcurves_impl/pcurves_util.h b/src/lib/math/pcurves/pcurves_impl/pcurves_util.h index a058b323eb6..9f18e89d8c7 100644 --- a/src/lib/math/pcurves/pcurves_impl/pcurves_util.h +++ b/src/lib/math/pcurves/pcurves_impl/pcurves_util.h @@ -283,34 +283,6 @@ inline consteval size_t count_bits(const std::array& p) { return b; } -template -inline constexpr auto bytes_to_words(std::span bytes) { - static_assert(L <= WordInfo::bytes * N); - - std::array r = {}; - - constexpr size_t full_words = L / WordInfo::bytes; - constexpr size_t extra_bytes = L % WordInfo::bytes; - - static_assert(full_words + (extra_bytes ? 1 : 0) <= N); - - for(size_t i = 0; i != full_words; ++i) { - r[i] = load_be(bytes.data(), full_words - 1 - i); - } - - if constexpr(extra_bytes > 0) { - constexpr size_t shift = extra_bytes * 8; - shift_left(r); - - for(size_t i = 0; i != extra_bytes; ++i) { - const W b0 = bytes[WordInfo::bytes * full_words + i]; - r[0] |= (b0 << (8 * (extra_bytes - 1 - i))); - } - } - - return r; -} - } // namespace Botan #endif diff --git a/src/tests/data/bls12_381/fp12_arith.vec b/src/tests/data/bls12_381/fp12_arith.vec new file mode 100644 index 00000000000..e380d8dd380 --- /dev/null +++ b/src/tests/data/bls12_381/fp12_arith.vec @@ -0,0 +1,82 @@ +# Arithmetic tests for the field Fp12, X and Y inputs and expected results M = X*Y, I = 1/X + +X = 19a124097ac6a9dcc25dd086ad3c8940e35bf120c8974eabd7595850299ac53b8e5113f3916ad9dd2358a9a59ac963f517e568eb94f4661c4e4204363bd44480ad9dcda0db7755c3863bf9977a5fdf4ad11f88240b0d8d6cb45924fae90bc20c188a7d3ba85d556422e530c54b624ec86677e0eec4003bd44ef740e97e4881b76d36472a0f5091e207c4baf01855c89b07a85c528987041fb14437c9904151440ba42f2f56b6ece9149f099093bb2dc02deb1cdbba235fa49d6cfa28674a9a280e22428d6e125d7017668c8fee58fcbc664e4ab75b04949bd8a5faf887aeade2c6647b0b98590ea1ce8a3e667220d0421596060c0a46f23e246958a569ef865370473a507565aad458a7155a133b4dd5664fe57dadcf7eaa105a5d7d41c6523616c958f332dff9572551e072dcc4bff80d70841d5006080a66663e97e00a06631aaf2a3199d38242f6e95c679d0d029f151d66aa003f369586492ef0d3f8b99ee5bc03f6202ca8adcc61c81645e7c9e8eebec2ce95d0f59b80781175e06d1d3f010319ec9476356ec317f25b48612291d3ca0633c5bf9474be0a62067f655a1ed006c295b6850155ba3e087a37409bef0ac39ea470a71ae7282a7a4f45eb664c0146da351fca79135b5879b79f74566ca9c8d0aa19361a0f5cbdf8cf373c99800e0858ec221e5e11aca857e0f94393eacdb62540bb9f2a314e8ba3439e26ed36f6c6ac4c5f73f746a0b94512d8103e950050dd7708c3c2d4571443ea6005896e271bb5db8ad670570be1944a83c2d14f9071d9f5d1c424824092b53ebc1e2158 +Y = 0c3bd5df544cadfbba0d7e788874bdb6670295f8aa36de8041bd6d7d1a50acbef22131f8e62c1816207cc02e97d0b1d709d8b8695c766d4dac10d8e5fa9dd62c64abc59dcb7b64cfefdac94ace537645813c02db374a24c8e854c4c06e555f4301a69606d68036e19d1ede5969b719169adeb544e0f35ee98042b55d610c10fc5e374613226cf1c7d1fcdb0aee8b1d6018e9f03bfac7383e0013816a193d604529d3d906312e14b177c54019e578fe65b6c5cf3f930ae2e2984066af06f538d6185440a187cc7404cede67b2838eed418010b99f1209dc3fb07bc797b1f92151d81a8ca7431be0429451c4979b65c9c61473994e0d334fb05184c92a01fb59e76d94a77611dcbebd1edd0f6fd46f967106d19c633e5f79cbabd187e2ae10004d0604769ea09c0d215b5712aeb8a16bf2737a33a191ae9c6f0be5ca14db0a4e447f2b5b65b4a09fe8cf692bcbd4094c44091dc308dc11d8861fea55143b7429d86c5ae5398d7c6f04ce4087ebcdb37fac7863fcb40809221c26b8026473edfe6b1190e5eb6b7db3267d2d344d002d1be23e7d1094adaaffd5c44fa8c2a31e2c4834fa576a98135cc4a3072448b1cdc81406ba611d94551282b349d60453b50a65fd3aefa5937cb658fcd646108f58a870c276a4d3709af01b370f6baba85c59ee150d684c59acbc3dce0303a3fd8d8f71f9d7a6b5c4c1534e3a7026c318b4643193a42d57d26ef4940e47328ed2978e630cce463ad44ede7caf2a6d3d458b56bacacfe00d08cf531d8b827d932c65479d659911314db88f245abbe4bfc67ffd4e +M = 0dbab7f040477cde7f6ea2f03f14c07e35ee2f0317041525921d83cec78ff13a157be85775a688806c0df155545f17e3070ddefeb475a259cdde0f83f79f287095dd70f04733496575e8cbb7d70889dd72ada29956e2e33045a50383d05550c7173c1e618734e146bc994566c78aeef7638fad29b551e27947cef7e5c05090fbe16709b0cd0c72682f53339880105d8a1698cb3b87e2aa40259ca162115db0f7c67216c355fcd0f53dc86b52a9b256375f36f9fafcebd98e9d40d77cc43af33e0bea4536cd77bcb6f639a53fdc6d15165cf0e2a08995f3210fbcf4b9c38a88296286216a6023c754de5cf0dcdc0961630135a41efaba6072b3823cdb5569da5bcf923df80e7bf016e7a290fa4283512d106a2c37d419e308408025c6ecb94d4512a05e25ea0be381403f6197bc9231556b8fadad5872412ad7336e27755552a8eeb7b292b092ba0e3fbc8d715c0b6e6b16c7149fafa7d3a2e15c2381c2116951537f94844e8ee97a1bfbe776b45279fd1bb92ebd063cc9e5339a0de984a1dde805219fb8bbcc84ac002836af900ad4fb7b92cd7ab4fabcf4da3ef391cfe2d14ee81757f972cc0ed39810926a06d60b4210c171f431ba38bc7517f2b2e576785ddc9026605e926c8d31f63c41524afdf470a48149e580777ae51c4c4a83d2dcdb08c63fd7e6f9632d4720ed9c4f3af4cf918548dd5e812bfd07131da287e52a4ba33994e393f429795174e0b667f4fdbb0754fcd95e2f4fdbbf34d53338fe5183bd544de35f3319e12eb2b3c67dcb36fb430c600620c4ad43850dd6df3fd8c625 +I = 020fe9796848ee0703a7ea57743a31e68522ee184fb4240c6d043fc30e080ed675670aab2751d2bfd1d3b60d7664ce35035c8f7e6947f94d62bc5fc27aa2cfee335e4fa81dc78b08e2bc9abfa648fa173e18a2d206813ea44c973816b0042e960c900410bfeb8d6182b538c39f2894ae7b1f1b8a9084d96ea23241268917c918ee2f8978df0f3a5ff3ff1edc576e2a7d0838cf357f760e32aee081a51dee3b7f6a3c749c6ff2d4e19c49103bcfc32df39a3286ff620dddf6cf725351896773a918f732448467255bf1c32b14520126aeab417772efa1895971b80ec3b0d142a450fba895a5cad014f4c40470f0813ced05888ce8eaaf496f172dd5ce05f85126787dfdfed24390eb7a6042dfcef2d209e933bd5222f6795f90c4721f201e666318f3202ae140bc75326a52084263179038f84a75b69fe23d8d1ceafd752269ef9b57d5c9cb75646ea73e52f4c1b4b13b03c453e40836af18cc84a6c3db6281fad6361528417ff9381b007a7988d3e25b57ac3b3fc4a399171aa0e3505becb1fa17cee35946a470b5c78d63a297cfecfd9f69c7d6f7cdc393cce0e1505d30ee250a6aa8f07ca3ed529cd50e4a7faf3348129d567f68bb657530525f00abf9cc330a481835be6a968037d7129ab9d235309abacb2889d4ac3a4fb0fea5e32b4ee30b7ba63fd19a6bc4a3e9f25878aebe7e921d19295029c4e744348c81802c40139d3700331c50632fce79592f8eaf195d001e965eba9a9416518cddf7baac364ccf943e06c87e36258fc94126eb20872594aaaea6e38cd4a3e29f1c581fd01d6b + +X = 15df481fb0fc4a754dc57ea7ced729ae898ce2a1df778289d6fd1aa6d20f06cf1d6a426024d4cb40a86f3886cbaf120a09803e3fafdee616bb803e3e3b15770a5002825e6d1cc25008be779e70d816181f1948501d8ed9dfdbe575d42127d8d30c643cb0b7336489ddd44cd49053f483645da5c26d001f984f655d1612c04068fd1a3a7ff969d4626db52512222b97db0c70150aeee09bc1358ac3f312182f9928e82f35aa7d9cc8f0325a142683644465f9a4dba159545247198f4d07408ded0fb955338233da9c77df81478486b8df99e5c8f7629a486f360486a91824cc26f2f77dfbc13f1098cf2c3e4f99a3d43b126a4e5731c0e440864c179a0a76cbeeff9463d2e66d7f8f3e54d1b1d1e4da0699be2ff7aa20b0220d1837aa8aaabe8a07929fda7d60315e6d22a0354f09a34c7e70015506be482b41da06c4d8052fff487155494aa048e2bae28969809da49206e8f8cfb1043a8c64a55848f86014762bc6e92e66414ca8ab1f67af607fc1ddf484f676eaa8891185462bd142f72080047915a11ba7eff1b139b42ddec7bafbd6372726c7b9325f0d6950b01f0c713ae9ccd5d9dc1b694840dbc5b3201120f313325932e031573bf620da6e621bd96e712837373dd728a9114ae0ef001cb8d5e745ad73791347c5d6cb9247eb99c1680514bfaaf11d78a5957dc348482466bc28b02308060c4b562a362327eacf8c4448a75bd4eb96627233aa5e18da7dd75f0c79929a192dbe7d9d76ee7b7a52160e7fa83ef373e223629f8342ff6ffc726e7b50be27e37fc6373978c5e566080241 +Y = 01451a8f1c85902fb310cd909f9ad2016bc7e6f1b4e47de70618e2c0a26df711a229fdcae0d3bbf01c18dd33dd6b8ec00d6a7c4eeb221f3295605df32d4bd5a511364e509cf6450ba1f28dd4ba07422e4abf014982d8ba3d978f0af1f1c50e2813cc5462ccaac447de3529839ce6906fbe8f192d9de4fa4702d2a6dbc5f9da6e7a5dd5929efd7f52b58ac853d0b14012016e09878123b539975719bda8a50a273137a9aca8f0574cc0974701273d5324560b5e7a0c9e9700899a8f927f36a3be00db9b14c62452cd5919676d594f3d0208a3ce83ee47e714ce1b2419a792da2a5103c4dcbe297e3c962a898dd8f47da708cb7ea9873e9f343c0f07f1bb78832baf508f9fb8cc7e2c3f427ed055d80d31cf25863199c9bb65eed30a3b60ba2480020315d53216728659fd7b647afcb4694bb51f70cbe7af0379395a6e8964a18293b059c2a12f50d317681f5ea6eab380051458e8f8951786dd0d5f155b526eeb55cbe3fca38455a5b788ebeebe83e6c7632206e8a6f55ffa920c52cb13648f2317614440601a9a371acfe4581f62176190397d0d07ba188512456f5df64613ad66a575e5ca5a818d42f224cddf79385209754ca681f7a36881c7924296e7d816d90f5e65179ffc9b87a8b92246cbbb22ed27ca1bbf5aa96618f199ecb61468840040ff55107c16c23b8e48bbc7247be449ca5b6a79d6e8f4e67e66268bd3854073c3bc2d2c37e4e0abfccaedca139b0c0479df9bf992ad89f676742467090383dad9d3248546f6eb62c5cf5c750d782b7690f59f5b03ae98e5f5509c137fdca9 +M = 144792966164c8ddf8b7a519572764088bf13d6c2fe87ffffce1a51e0144beee153e2f8e9370724f832d20da2259a6820013b5a218aefbac3642207a3c2ba0d2a23088f001cab418d26b87619b29768f6dc6cb4a25c06e2b6680ed1901b058d90bd3c1211d531608e4ae6f84e264a52b7ade7685bd9f3f559a89cb1781d02b7fadacf4e95bf5416cccd91c3e81658caf05223b71ef63c075bb2a4e1ca40478844103b960fab5454e8cf763b463819d5ac5d895ea1937a3defe55f98bb23084b000eac5004c82bb808ab816f17d6bf8fd3e1b0f6b10f867253e26963fe3259277b0699675e634e295693d6915de7688a419fbdbb08caf37b9ea138398920ab9f1a7c5f5fbaac144afbe989cbc9d6b5943cc630cfb80f150c9956b108b40b2e09a0c003025bd7741118c272185b44ef28cdead61c53878921db5de7e080ff4c3a2095905eba070257046fe309eaeebc5ef02dd66d6ed585316ed3ae4ecbd3e2114c7da58004b4ebedd746a489312f298976d076c2a06e2e49f7259b05b946d4fcc08c0c3cf6e41724a21d1a28615e34e54d3288d950e09c56a51783f77f2891899de930caab6d33eea3e76de8e16e02bac124119ca72f989b90777d9d268fd579938baedd11cf21564ca5f50cb00adca03de19f68512ef9a713e714136b30c078d1033ca9073acece76002a95363d1bc1fc80ac5650a3258c08c1c8e762b5351e1faff9ee85586190d08f86af487746d4b0493acff7dbed3e00daa396f200b910e7fc085d34726b47c6f512fab0501e1e61aa26d66bb21e79e36bd9d681d15a5d4 +I = 033af099afe7e7d626a0576bb12c2d7725e076d6cb064f68e4748a6f9d343adca2add18ec44e959db505a062b2a02e810b68d6c3725521ae659feabd8ce53c35af4e7d786bd72956b981ee95d0aed2fe591977783a877bea73dcd23284dd4bdd03eef701a302410f4fa48959e37193cef5a4dfde6efba06702b5ed2bfd729b3998ad1b0bc8ea7819b3fa4dafe46568f802a6090f6fa8ebab2066f0617db86c7b129bb4e10e467874f2e3957d8c2f579002af9ef69a7176b97ba0c38b209b19bd0fc30b133889e891a11df3fdc02609f1e538f3682f68f608918fe2f54db0b4b42fc57d3698aa8b69ba33bd6cbfba08570976c76fc9b57a640cfaf974d94e812d6db394f405f7e375caafcee4662264a2e8a98b96ba885e4908c43e241c82a29c16485b035598373b26f843cb695b46d00bb10964e55dc4b4a7d830a5a65453816de68a724bb2e3309acfa5ac52aa34b50eb70de7e354a91881b939eda773423a3811e4d349fbeed97e4562693636364be2120c2352455ae7aaa31975ddbd583402693207c365deda07a939d5936e335cff44698ce51712f09ff49da946c362619ed133be92f34f04638948f67e1ec77a1580fdfdc7b45a2aacb3305b260a23f36e4aa90de4b17860c37624bb9684fc3bbaad10683c4c17fdb026252883c5b1b40f020be936188f979f361570a5eca7386f32a7cfb5860c28797bda094018165066c620d90c5f2326f784eb94f967a034139ad698e95bded37e4134ec7d543f4ebed910ecdb5cda35060a9b6d72bb8b152a477bc8c12e89a0e5979578b38242bc + +X = 128f50cc1f74413d93f7dd7fcb9ea2a6d9c8d8c910ec05d4d25330e2b69c59a9794e5f4883dcfe48d063512df297c4f6145a55cc9bcb19dd1a7312158bdbb6a2cd8f14b80bff703d66115225395a8d02feb4dff1214fafb4e1d7b91077d2f60e089a9b48e9bec02a074546899200bb72086b634aa1f7ff8e604c7beb4f5d833832a42bd3363a94a7629f12310e783cf311994e5fc46db1a75a2459701e0bcf501ad82101028354aed365ebdde8a3f066b8fb4534f7bf9ea3ea771193324f4bc204823f2b1237bca6a8aa38f9dafe86e7a0a0125a9c38f1664e428c5b80638b160c818903c82d34a2899e402060aaa3270cc3a3266ccc35d73704f2d7cded5b6c8032ebf6bed46bc60231718e95f95e9f342ccd2285adf3ae54442811dc76ec71127d1850b396fa64a632a1d8f5a119f28f86dd91aac243398b303ae0fb270b2b7da3d94f1a0ff2c45b04edb7986b5b550ea2bcb5c683607faff35c973cb0ce6fa4978ccd1a30861c83241394ca78c1cd68d152d961af6297bb7046e75c94456d11c0b08246fde26c9cf3a8e4b34f1a3f9ba3a1cd8cd20f6583441b3722185c0ae06595ff25e06d9dea4a26c44b23448e13f62e741b0c3aeb5fa24bb8a28f075ff679626e0456dc8196bbee442bb31b45abd12ec0c3147fb39395b880037551980f222e1328885bfb97ee4892659ecaec078b3c9c2ecaa33b788c898a0c3aa3ae280f76f8740c9474470317328e411de10d75940815f6ec96f1c2c0f8d0349b380fafb9220c0cb250c47d42d8e65e7be6faacb252b69d98f51fc15c8b98221f17 +Y = 038bb4cbb29a345a05f17287ea10a344d967360b24c587b22997b659f5d9cf4d71c4e76f9ad1811bc92292bbc6796ecf1325035e49c2cb269dc59217d7d40602658e1fcf5680ec9c1798ac3b08485e04ccbaa3dcdb7f269172fbba5e11edf0b9101640a194f0e5037421d9aaae604cd73163037ccd6ead4457965f117211c7bacba2f1a18fc66f7ddeee90918f4ea3a009b6e44111273d4b50ddd59a6f60c755e2171c4c3e58258e63c20c398f868779c15376f41d2f1a7f50f6135d01deb0a40e8f1c3b15d127349dc6c16ab02873b4546e678e3c56934ef047b0c05fcf1af71622b89b19ad115bde73b1608d33017915f6281ba2c2e93cbdde535d03759a28e489a615c8c80ab960090d57d4c7371372882202719dfff90366866f1700cd240e20c2e3fe825e8b0b0b1be0a7802fd02fa8da8cc78701cc2224be6c0722e4a36ee04ae9acecd95b266ecf9633e1776c1331dedcc34966cabca9b32f7c11245f6ee31e49bce4d700ce60f4f1bbc745177576e9eb9205d982b2fd9f0cdfacf6b71956394c0270289323871568a3ebf07a382dde618f4e2693457d57723fa2ec9b20322f6d30928997b7f33ffb725d23a20d3f7910ae96fd05a7c29b0ebb43be786494116946796bcdba94e2be0e083b49d9524aef96f95ba8ad477499ee2780fe054c2b2dd79cf6bd41656c75c9061a6a793859c6d129b55d45521e336fc8138229379adb00c8e52a0921ab88e07e87470cb945fe7537cd2c5e27d5b4ea2e6690815ca4e31bf80450068c78f42a20bd01e80dd5f93ab29e5c91ff839569c347f4 +M = 055d89f20e457e1634fc5d7c974edb7d212d7bb4ce160be24a28cad76b5f850b38e7539e1ce33739eb687e539cbbf2b50847d91d1be37ff0e17c0cd7e6b3bb6fa80edd960a1c80a767ea2d32c9c18d8b4982a0e0bb4a08030e9a74e5c027950e14012a46c72b5286abd61272abe7507c9233d0068cf0c7b7b36a27cf72e4ff46250b354b8b965c9e215009bcb66905b40f7a3de4c0b725a7266336ffbcb6fbeb0995f7a96f334af96ac3de2dde8234162b20a0a7e7b01d709654f3e6845c4b0c11904d49492b346130b3b5e695cc308226326ec6676c7673b242ea4b67bb6265119acd883693c3b9f8051111f9ee7cea03e9ec88ecd4a826a8160369753a06c496d70c7342d31958a64bfc0b7627169ae4a8c3954c20f0eb001512521304b34216a572044417012b941a6e7096493fcabf3cdd3fd2e94d9c131a8141330433f580f00846ad730acdae7d293f1a055fa801cef5ed23e6d66d33c04c90a911244aff4872f00ea863938dee5e95ef55204ed6bfa4b4f29b9372670b89c7dbe9ed180b57c7d715c746bc63d7f73a2b8fe40c604ed87ae6e9ed2e47b01bdffe9fb6a19f37da4d55f193f38a3775acca26a65b0937e2be6c6c105832805e5fad6fe7f0ac23dd6cc3e9dd7c51ef1f8d742e06bb67fb8412d13ec324702c9d8e7eae9cea10cf1564f0db8983fca71c932f797bb275dceacd92e4337f176571a3f81b296abb54d8292987e9392ef1294a0ca692f1084592135d4de87fcacd5fbf82d8fa1a52c0b941bddc0b4af82685ed3da88ccdf7a1075f402a5462e5580cf4c1caf656 +I = 0b2e71b4d80a57a8c6a117f559f6a755835dc4f25713fc56a6377ffe8a62cb410d40c5b3f6d02d9937823c1de80529390f6e9289fe4caeaadf7638a7fb7c5d843f028b112571e097993dfa654cf7e0ace84ebf89b34840d838ceb03f4840681a0d120d01942d8b9fa02bde837d9e7b6d4618bf6c85d9fbfccc42869f3a84fd566539f217a07c302b2addb3a74c7f6cb106aa3f32c4cff6f0f83badfa2fa8fe3a901dd6875e51a3a76a83b916838ab28240d98c78fbde4f79d556f4f03e95350b1738a3465655bd3699ac2de8aa628dfa1c331a245d9deb119291b54fb531aa3a270cf86914a5f336bc7de85556082eee177b9fb925f0e71de4343e2c48b4c2fe778aa3057c82422c40ea24c963a69c3eba341d6b60a671dd9689c95115ef8e1500cbcb737b84da56bacd6d0b7fbea7a3c2d466ecaa97947d8cefdb79cc2d36e2bff2a7f7581078e84982800c29d7da33078ac39e8c379a680a93695417552ecd3ab867cb95530ad1f387e8e5202276c4f82c822c0998800792a2d37b2d79ec720d98cc4e0aca3c0aa31ebaa8f2c411b46ce9b89cebe5738b72a04918f8ca78a9f51c8a0459a0aa7e24b0dd54aa67c9ba15081f3bdf15e09d0e2b234b27cdb10487a9c6cd5edcf1398f44c2db6148f8b99bb44772f63c87746eb40f54cef20f5001753ec8042085cf064ba6abf87b0fb82a6cc282c959647706642783959851510cd5a4dde909afd3684297c553edcbbb0a90e3925a7ee1dfd2953782cf2aff7ac3036fcaa801be127841284c0c4119f8edbc832b6611de66d9e50d88bc62d0a2 + +X = 06980247c4ca4d7ac17ae54ddfc84dbba7e2b7979f8309dddad457009531087fb21b2c3f1a5b7b75abd26e7df6f0fac500d9096aa0d72231420f57e024f256d07fe2dae37e1ce4424460caab11f13460a6980b27ffbeaae918db686ae58906960d0d96857e213a7968f43364242e25a653627dc005d5ef7e2bc48311a978166295ea98ae983e73106939b660d939f1e706e06c19f463128ba9539a176d4dea214731570efdd947731a46b29612039af46789b7e9c77e629fd8d726cdf6983cd309c83ac23748377fee01c9eeb244c61cee2d207da3d65ad90f9f82d79b1be199a0885d1b284ff79a02992ed59049245c085a84756e56d353ef75e0f56664bf46ddda8d2df9928be242b1f7b3b185a25f561700a11408abf688c3455c7c168dbb17216bf9cb56cd7a0d88c7accc90cb46c7de83ac1da6acf46defeb09294a95a2f52195c8e9d042d064a0d3d7edc14ac5199a50ff8bda8d1296e82ed9474423c06eb653b4fc03a76f488b3eb63df446a94e78997fc5350ebb45c90475fbee1854093ae23c6dea0aa87a89c68adefc9f5fdb1268c508672e9267d0897507157618d52fe6b0915ed135db6ea35bd9cb05ea0cdeb2a29c86c9ccef3d56059be873d29a5fe70cfd02ba66d5cee53b8277c773cc4b1c2f28c5336da4462865fe0bc69c0d374b5dc5b39d7181a3b69cef7f8d17d1f56423eebe653d21c2f8bdd7d1bb847e04e64d27c054a249cb6991d6db5b6c0590b9778663f0bc9ca7725baefcd3a8d5f9f6f128cccd6eb2604313682bc8a3e7e4b370e024e1b0510cfc00ae3034f5 +Y = 09e43e4f620e0d114f70392041e6ec0349da352b6a830d3f7fb849d2f3a7ba81b6664f71c903560f04b74c3e8fe84389153d7ef37d856833a037e8ee7a72713ef55b22c21682f01e3460e4b107431401e0b6d5d8e160050b954315e05bc2edb2131c67387b977bc09c07d665eea2abf4ad5bd7329ad5b87afa992aa32ef33fe56dcfdbb2eecd543b7c5c8976fb0c529a058d866444603fe98c426dc1e1bcb66388072f3d0095161057d766d6f10df2eae77fdeee22985a6d7cc7a6e672a2ceea193246fda20f975dd90a57f3f2055e7bd7d405590f869b30ccb11443e96f0f75243cf2ac900dd47ece24f3a17b98f86d0db9a7c6b99aff0614bf567f6c0b3901026a6da200d30dc080425f45e50ed0acdd8f0168043596c1c84868a5b9d0166b09b84de1927ea62b825b9885dcbf2a5881bebbccd1b9eefccb14f341ae65087014a57e76aa99c20a431c358cdddaf4a1011191ac9cfd011a3a0fbd52ae793e7cbe1c3ba9aedbee33b59e8c88588edfaea13b2bf3750e52ca94235b7ab87691130f4c62813d2f84104ac7ec51404c4247cd03a6f8aa73f6f5cc2465c9d0657310d97c94806bdbef34661a881ea883802a13d94f751a8f22e10973ca807062ccee219f84c3d0caae07bbb6122def80cf50c7367f0b4202316a7fa2d71d8753a8d50c18544feca55460a02794b1d3f6b47fb34caa5b1d983b886e816d38e01f3654edd31b405c412d55e41fc3a484c5fddb0ab2eb05c0c1e992871b3441ae6d7a8d88511d56fa7593c279e676ce537030b6f8c41833ec76040334360a846195751f +M = 126cb76f47cb9de1c8d341aab72ff353e5ea7896be3de50ed10d3737580e2337f2faeffb4443de4daade21054c0c31971888bc6b52d3f7f6878d08c168c0b861f28a6fa864ead35f0cf8c3a8fcbed32959ccf9af22341fe5b8b0ceb2e705b42302efe108a42453fa1f788bc88b8b50af43e5f2d7403f45256461e325a6e1e5353a9c5ece1406474712b81b4b63cb68f3044a2245ed556f873e749f096909d19e7022d7aa963c84875fa697813cfeb719f635082cc7e623fb95d000e788685ecd0cb20101d06c27942ea473f65e60f363f72d3951a4805a47f368b3c0998dba31933bbe581446f661e1f0df961b39465f02a7012bb88f5b0d38776256ecb56abd16f3a43a9d743b357ec35fe409cdcaa8d94565452df8f7c434f62576964f968a0ae95927c43e299eb8e65a77fc34f9a019cba750fc0f0bf1a0d524cadf33d9bf18ed06ce69ff78fc12fc79aaa44e01ae01048860c28322c2278725ca4360407a677de2bf199c5af92f8526d2599386f91bedf5c9f142e494b00c8b22758ed2050dd5148a0909989d259cc9c3d8d9929aa264d82bdfa0f6b95bdb70a983ace743bc14098bd8984953891a5076916e34ca145daf2f18b749b1a9df9290992565a21dc59b2edcbb34107926b4a697ef2b97a0cbd62ce8e250214794341b1e2aad570adcbfa6cdb29163207874d419d9eb0bd647b4c0231003d3f76397fca70f4bf1a3f02482c52da22259d1f55218cc7f7717cc014046256e0be5bc288cec7abb95ab447baaebdc4c5ec195b9c765e716d0cc5df73d998a94b1d7b48bc22030f4ed +I = 0a427639516ee4959e48289ff8bc3415aa915692af3562059565fc0f78b9403b1ca03953ef8c8b0049e0a33e598023c802bfcefe211aadd7d9471b2808eab5586dbf14b7206034a543a9b45af9aa23721def4b8768c2aaccf1d482f6668308bc10bf441c8c36ab7d111ee4975f37ea8863f3adfa857402c83aab7689271b46fcf8001bb457b3c36e7a07189dca418fe004a8254e30418db45fe6c7331cee397793d00fd774979e006e2da082a8454ca0007751f939b107e8d0b525b42ffb78941993a377515ae2de5ede479a47c8efd20057490fd1b4f960d6297d3b1ed60e20e9df8a39b3dad23d41f299cbb981d6490f014f241876fafddceb9269eaade8737633bd420d69d24b021112ac3f6abe7ea08875eff3823f840c3a3f669a96532c110142d481774bc1cc11e9501096b1c40cf7330fcd902e8819c09f33099c455c167060398844fbf88f37cff5fd1d239603f4f9da4fea60645f8ef05e73b18942a718d308f05b0a1f6966a1b0d254df6b646954c1b488f31362c2da922b7508eb00ca2ea347374f1bbe6563117ec6b0824f4dd85473d20ade3ec756d58c7586b36a8f94fff613bb0b2d4400781dad38a604218cf813f71d85b70d178c43897c3c86c960553587af7a46d975005216fef7df04eaf2907593650c73558dc4cf6e0a136b79afbb950adc2e56d1a355ba48a3017a20e18a684261cfbf8cf8c4ed94e7896ba4b91ce4bdb87dc7c6090b4778ef06e0c6f914823dae35cee28e2018d3bca7a8d1ecf8ae6262f6f8b7c43d5f59625380c646117d56b25f923a91ea71ea52 + +X = 13bc2e2f9d01cf17b3088a66ba19478c07c3bdf81ed926c583c894d77bf29a0f819e0a167c52e7aa9008af284d18ec721515fe59dfd9bf8c7d437c1a112922297a63ba502f7da26970f00abbea3ea16a2810d200c7c3e2b4fa44a4ee3ed0a0981661d10885300fc9be1b476d150c84b28bb4486388c5e463cbceae149caee7987c3303a90478474e6bfb6270035cb2e815583bd6f667fe35150867662a9efac9c1bf45ee1fde3752ce81dc55fe120d6c2f2b95f5a3bc551ee48e99b04acca5cc053b5fa351cfa7e4258be14d69e7059db038e123d95596b0a8174efec56b2a33a9652ee7c1bf48eebc54d969bc22a26c13e99933cef61236d3ae4de85e6f8cc83971faf139db6c54b5011cddc4bc034697c62258388d1a5d608667a155cc80fb0f2405558dea0e6d43025fdcc910580c2ac81ea3a55d8525fe24d50aec40e629217e809bc4ed519b3f7315c0dfc2512908704813c6bb5947ce1e29a89339c4960cc8cf408d9e95aa41ad6db1cb19e4d0414e4ea85e804582f01e78eea0a5f16817d7c91b135bab48cce22a771dd08f41a3dd2f90df2d38b2790efbfa20d44de97b5cd552324417b2aec6e7f304f23740135990e7d646b299ebf81ca404847132a919d2499b9663aee105d974832ad4174487466f39ff81af494eb01e6147b52e048315808c4b6ef642fb8ef61c9fafdae9cbf604137f0fcb718f06cc64158c0e7e759af740b88fa044490e491236782a12279e8f26595606a7fbc07d696040f3220a7cae0290ae0ccd00f6ffc9a6dfe015bd55ca26eb6385028a27a569b34185 +Y = 153302f77c0254395ae307ca3be459f68a82da6e567f81bf00108c83857e89a712e218236177e2e0a973e9b07e5274e308e8428459ace44920b0fafee5cb158cfb952c75bd7823f30b1beff7e90077acf3b179d0a59d214d451e18640d5786aa0a458cf8b1011ed88f71ac3e44baf88a415234527846d38edb4bd930a95cce14738d5a0e7e9900248d907742fee5392a05b8ad7bc1a14d1b7d681d14c1d1f8eb0354ec7d69a2469402b783473062c523afccd0f4f49f1fb22ca0d26f59194b5715b98c1c1a8f6f84312013afde9dcf6ac4de999deefebb14c7290e47dacd2db777c9f8fe25f95b525d39f415c53e78ac0eba1ffaf83f96b418316dd302290191d4a3152805e3ac1fb572e22068d6be7974e2bb14cc3ef39a82235c7b46ab202f0e49c4a321433726dc33f52cbe516f5f836e33b138fdb583156c04dede8ca7aa3ca34fda810e84a2e9fa17b374810b080073651243715a06a2613a06899f4d2dfa0f0ee2a523256310e35cb3555b8a3bc20d372ec4b5a196e36e8947c2a6b6ad003478a28ccb066e6affbca489b4a7789c74e8606988c79a6b2a1a2515f3cb7f5441dd702e2781ac66935bd3ee9401561366bfe302a9198b05969ff03b5d330d62d09eee3e8a44d1e32719d70b8615ad8c77e17501e1e0ced0b592febcedee530049469f0d466c1892950e61ffba450e92919f10314d4927a9741151af6b0f0172e8de5258d415666ebd374d6d63975402c7ee5fadda3e87f23e8dec5e357b1fcd197668c8a8bc6dc3513e91efc8de0884be0a8833b9066c46759fb3d0a09f65 +M = 0812b9af1d29fab7094773189a78b39ecc94ff5507343c6ee127e80a441dca29502b2b9eaec77f6caef45cdb6e22c042157a4b774da0b4a3f2accdeab9239d00c9a7bbec08e1c2b4270252341b4c2f0baf877ddd8ea4788423306f2cb661df1f19a2202b70aa858c2c091dc02e0878b8c15931c922b54f7099fa30b0249c4ec146cf5de45023547c9783af615929ed3d0c9ffba1369bccad15b3cba659631e7735e7ff819f65f1e64e1213686b4c65c859b18e8cb27e863f1ba5c3ca61ebe80a06f6dae21d47783ed976a85fb0c4256c810a8b6fa728ba3a640263d2fec82e000fc023663d472abb9b0509720f87c790137cb695465db50d90b35f992a9cf6533809de7dfbc6496d8f66d83f3763d6af4aa0f0bf3cd2919001df8900d22a78ae0707227918923fe7d2a0baeed7e9b172a83970238fa3e8c5bb389b58281a53e4f787812ae4266d6b8a2d644473786b0013bc67381ac0271f90bc4c438542c96908192ca20079b7a6d524063be6666a799736cc487d930bc63597e9d4dbcf29601759bb774433455054dad87cb7aac7dea3f035b6814d83933cfa10496a85d562a1d7179df49f5db0f988215bc5851f5011d3154bbeeef7b97a286444e2cbe6e66b96648fd3fca3132b4bcd87cf18e54b23520b6c8d4657ef9b78c20e2f8725e81889f27cc42e41d5f2720924241bf50fc4d726a4fed01a6ee571d32116f926c4a32a8857a718a943393a4eb639c467b20797dc77843444eb6341e74a24ec15fa01eb6b92951e8420cda512c837593615219ca9e8e7ec9ca0a557cab92939bf89 +I = 175950614787599d87c5375055e22bd7440876304f520159e40babc7dc93183bfbbb7fc7dc44bee9d49c82b8ee2ae8ce0a7dc2e9a832fa2c147c27d2e98e7ad6e6599563b6eb18deffc99281e5a099f56a4e05f82f75424c5f39a0a2e4f7ecba19a2814ce0acd12fc9b7417db78749f1fe8d5252c2cd2ff936178f5c1f4fa895d211d758ad42f6eeb16d2975e8e20d2602e6fb3253724314b37fe79b23b99c26c72033d8cd684cd967d832793f9d1be4d0fdc92f0148c65eea18a7605509d5f70981bae7c7c0e8dd91f08e99c1e05a798b9c7eac476ae685682663b87958f9ce187ce0c77a52fa6c825f726f3ecfed181712c174c182bf6e95c26f2ac88005f46eae81820a1fad48052e5f2b72a342c4dc8d19185920a7f3626a15c68a32d461020e38bbda133d6d218dc4280b8917705a8c302dbf2ef877e3f6adb569c11a61151cf26b814dee62cec2f9aa17b31b1201a725eea3365a58c8433a068b18d29c6bbd296a4beb8077f838a0ea6bca10b996de746c50693796ddedb3a0705d40820fb0c116c42a6076fab09dca2a822045cf0319e3ecfed46992b710c661e7cdb248b247025cd5a9d28bd45f70ce182f8b12b74330a6e6caeca82711e90566e7c908e1980a42e54f06a68daf71498707030f0fbc162ea07f6c2eb54cb588510069162a2b410f2b61b019bad58fc2971d2cdcb2e0714181fbed3589a310ddf890100bd5ed6a4c82ced792d928ffc737799b15b6a4a84dac455434a834aa7ff30fbe9ef26c165aa773338178bf17395e9ce2dca3080271ebdcd1e3f3ef1f566901ab + +X = 1369fa42bcba7dd1e4fb53080aa0c072ab3935e7e082c1d1a158e6daee3efb029a7c9fefc6aa4d88105f874737fd99651924b495baa63ad861f864af9bee1206058667062ea2f8d76c37fc9e0488c888c9525e13595c3c86a3ab70754f805c5f0c5e37a24e50abcf7bd498b9ce48981d30c7c006bfc6cb667dace3f12579d9faa4a4a3a88736c9f230117082a555643f0c876494027aa5d0536597e115306f35d5e2fc4393c10f1c54669ef539fbed2063097a1fd60452f7dea62f2c22d2b265175652c6d4a21408b637c1b469526ba5ccf2d1332255ab94a96b94be6def053a14cff274cb27d2db3669c967d9ae6a1d11eadbd602db2e2523597a2c07461648eea72fd445b672a2875f200fe25d7c7832a365851e30a80fb1deec84c6fa89ac01de74a2a16df347a405744fe6eb7384ce3f09b04a5a2a9fff32bef143c3bd52bdf11cea290bc379026dbf6afd7528640878f6fcea28537cf27080fe3aeca5f29ae0131f3affc88831f93b4d058237ad04b8c014dadc91038a5e3aa3ed350ac409d1be57f4220acc2e7611e9e1ad8628800456b3f870f17ba18635995be3576f1c35018a0c4551dd32cb71a594adb4120bb96c9292d6e2d88afb4f11bbcac9833fb1f046ddba8fe7b9045c113a4c4b1c7c1d66a364e61227cf8d6ea9c0fb2ae70102d3c6acaafc484efcbf1cc7296bc582249459164c0a20da117c550b4c9e2c973041af786d83b2386b6d9b0a0c6a14070fad4c5a93a5a11b64ef46f7136b70eb3224181676f44a7064b28b3f9e53f669f36de68ccb318277b6955433ca243a +Y = 09a11157d9c4ab664d0e0b79cc2d5a31ee71b009b5771b3f790389287f21e21fab85205f4e8ca47f45b7224a6e2c444d0a51604832b275741f78ec93cfdc5cdf25be709620b4a0ef720d1ec5546677ddf09d952e8c598cf470774b8718b5cded04eb622cc233867ffe8cb22976bf823ff77f8e339b57b7721db7666caaf58d01eb36cb270cdd4c98622e74db3f2afbd50a3e133b700bf857ca6fb91540c62e54d389e1af944a928f9dda304360b2de86a07b16b73c6da1e4a131bffbc45b002701f6bc637079d8956b173756472536b82563c94fa27dfd4c86b7f373d5cef918afca856b6cc9abb0f3b3ff6ea74f4cea18f332b4842844275220715d61d76bc3b2abe4e45b7cc49ba289d9edc17982e5322f09451266cb5aa63a35d3542c25500374b184bbbb994fb995b1651b606f1b3d76e6098a4aaec77203c9a5c5e29752855db3cb62e039c1656895a67acd6fc6132ca11c4f1bc0fc5b8d5fe91a09203af1994f9b6333f5bf45b8c694ba26b19786857cf59517835ef6dc9c6af8ee549b021ff5b7c2423f713a11c7bca7a6062e9f9240ea7281f7ebe5e58125cc53e68c840e50aa9a8cf1c5321fd6b6744f64711605a38f1c15436295dd4635783eb82a88a39ebc181ad9beae8d57b8278ffc1a0924ddc2f40e727ebc8a5cf952d40cbf00b802e86e64d4e786c5ba13730d55f1b29ffff35f3f78605bb4620b5d9faea837f11c2fa4b7f6b3c40a72f8de4d1c1b1551759384603cc9f7f8400d3683baf423631c196d43300f10bf43d190e8701c08f8209b1eaddd3da5cb23ef066cbca8 +M = 14844bf4778e38dcfd9fee1e8537f9252758a7f046abe0ebdf386f8eb286e58d6c3fda1edf56106183722761f2c29a9b0c5d32356a831ba01bf9000ec0865877035b37065186a2ab3585e7a51770dad6e25e968cfa75d0c0e720fe9a646fb8e9035e2415ad774ba5e81a3d36359d5bf6b32762b8f82dcd8ebd6252b7e64cbcbde548750073f985fe0221a1fb1efa98570e218ea680224a61a843792831596de1ebf0e76a466715a7229874649ba2950e28b00831f520eeeba64cc7d5ccc4027700c1cb3267b628f285e404636ac8512b9870c0fbebe222b931ac3a5c098b6928d1d2db1ac2a2a332300671122d0cf2de1272473601303800d5bf700d06865420aac6924c3ba10709d9beedeb47b0dd90896cf610dbb1dec8802a1b8d0678ac47190d2d0088f323ab9fb7cd713c50c9a0f30cd4d62f1f9a1fab52f5b10dc241db25917045b4169860a9cce61871cd138c1954fb2af1ebb1dc683ec69358797b91ed16d254a6c7d10859b1d20297cb2d47436351db557026f37a78de668dbd63270d2208f894b29b651ee1fc3ad48df9ba946ba27910162d4e77f809b7c523452ac056bf96bb45a348ec26e96beceba74d0be1a0de9c378e680e762c4b69ed39d1ee5c5dc3ef0ab477c3646710906246204166c50bb7ffa801c9a4a3e95e2e4ecd11a9f5fb8e4ff8124a5f6775c4680f9410ff98610c296ab840a9fc10f30a1be7e80d331aa175121f68e1b2729c165a26181ad2f3fa5dede945c8270686dbbd4edb24f79b42a181848e634c96d09033d225c405370265f3552394a5c67476d103 +I = 02bf71b57c2d745303be1084a324c206837fb8e57a0b9a9d6aaaa20cb3f3c93a6893c5485758172a8ac21080e63246650c4c3db2f3166d5c1b46d53bdc047b2f557e491ddd459c38d5e09a6b45076513f815d7d4954c538c08fa324193b8d3f614b5dcdf06c588947ad9263d805f7b186284cecc04682f3b6b2c82b28f1c2bd7d544078cc355fca79d62ada7b409f717134c3f5ed3aa5190f490178cf8636fd2b415213aef29705edcf33061f3c917e878a1f92434d0fb5195b57309d14ee84d19d726ac304e6a66833ebbcc6e197a4b1cc819db0f9476296c6f00c474be58d05eb2deab87cd96fa2c2751cc8c8308100161c887b044ab13768fc5c3ae4035cb173224db806a0f9e6c70affb7e1667a3484ec4eb6710e774e33c4d3c281aabb50f39434cb6c21e19ca2c7ad3e9b1396df58c6b16094935b2355047fe014967ed5f191d4bc431d67a3d4f94e91dbe86fe18cd2f493f6e7fd03486a248623584d25e41595bc5f2c88d2a47523b9107218ec1d95994f78fa3538b6954d53c92ac8011c78a843712e7e653534d918b492d808e220c8d22d978275f5bfeb0863e7a906f38ab7ea0478c593921f2c1f32f169e05a175f4ce13621c23966c5bbaf7b218387e39b2ea47d778761afaff3e0f1c752db3fc4792ff0f5ff098409cc140483e03571aad14b3296175d1b768ebf62c7b7d486f87cd7e1c31c421c2338503bfbb0d5a8ee838cd7de2eb4693b6d800921009cfc4b4e91a46b6a63b3e444f1645a63ff9228013806a2821e1acbbad2e49744eca7a5ebedc472c182b177367823557 + +X = 0875849464d0e4c72e221b17aaeb7d5e75b1daf2981d9ef99751e42251b3a6095b7eb298324a8fa3f302a1bae341fc85177a650c767a7cb8698f05d28007d74272c70bf198f6f7ffae8dd57109afe49cec752a2de2524558d1038549e80f4d7e03660d8a9b14b6bd8ae3105996b5511259d48259f176e8cb41d8ac905e0d2eef696631855c4bb1bd1180479814c35be90f8f753b96a2507f975f6196248cc62f0e5f654889c242856461800d7d11b1bff9bc24adad794901d2943cae12ad802816bfcb36c987dec693bd8e7868f3367aa47e5023d565e4f61d667f7e6c6cf0605be9f24c1c4c96c5381dad25e78d9efd0b395953a934418a80aa779570b48545f489a2e1c9fafb92e92cd2c6fb96bf88832cd8f5cdfbd9c908fe77487121edba0ada5d5edfa3204e105c7f3f61094d2df8d844c259ff156009f4fc066c0f1ea4b01b282a9642f441f134e2701e7673e416c9c6122a4a1ecbcc0ea155f2889369d85f393469bb521031b626576320d2fc584b7adce1cd61c0d5ecb54d04208ba0086c0b9aac3f297fa2b4e41d381cdd2d8652a54d18d2dcec3148aae924ccf2ca7de9334a1ee63d7d2c50fc1ed6cb6bf4190801cf123f3b9c2849c8ced4c9661110c4960566927aa6d0a663c3be5e6f957931237fcf38f64fecd6a88a1daa5b7d1502392daf96221967186766c8fb22028018368b6362966f889a51e492017d043066daaff0c1029ee907b65bd48d516612454e366937731551de1982b1dc134026910923a069bcb3e2d9f2c2ed2645e33d396f0a5040db990220b76f289529d1 +Y = 05b154b5640026a40afaa34c61e55350ffb58bd587e0cb91390c898f6cda370c29e18a20498f0a4cb7653083b62dd937116d9dceeea2c2e2b39ef299df99a4c5ef1b402a6221fea6ac5dbfd08d69be70e1c2d0346d3424c00243ee68ad3f44cd0ea8ebdc2f4986a41a5587d6f096aa59d82c504b95ee7bbee09c7b2d8a3405a3e352d30777999d44c4d1221231c117ec19f129a4f5eb152613a967b961fe1f22c7e3a965cdd0b42ecae2182ec5f77a645db7770ea74fa7998bafc0464d20b0151960ae08e51d5bb1d25f472e6fbb7120edc05242a007887a080dff178cc289e18115aff47b3bd07708f095df7df629de1986bed7a8c34b1eb74c0e5a17e3597f5f6ae9039f9bf06065766163b389def9e0178e88b1a3c01340df6e16231ff90808ad164d86896c7509e9de57d1c99ea5762e6a679a8a29e36e7cceb71ef06b7923189cd4d1c9f6617a9cfac4826bb42e06a8779bb9990d37ec95d6c15038aebbc471b24191e65e5e22236c3beee98eda1a0a08cef5c6d443fa8c4ee05acb6c3118ce0e61543517dfff06e33b7320f1b537c65f050cd679cee7d5a6c24708c1c099676633930c83cef3003507c9d5572518f863def54817d79cdcfc248e942307cc0deb65705aafdf4cd0b9fd541b291b475c94776685024fd4a1995452d1d8500195378da490981e71df8f87aedc52fb60b29f10f8bc9e1d90c6c9991d47b30dae8135dd2ca98347ab314f7232d3e84902e219e59a2709220767791631120b0e05452f887dbe39650d1ba5969f7bd60f899a26293797cec18da57f1f56d8af13 +M = 129c5fd201470c6c43136fbe1fda6038e61935ee3efedb1018aa0917fd664165571d4e7165f68a545d61430dfaf197710a3733ff2e511306d8ecbb73c92d29b3a7b3d6ce685f6cee66740f9b24ed4c90c153e4eae5ab2d391ff8c9ba10c177c40b70550c26561e4f10cea07a8f0774a07e5a57c00bc29f3c4e6e0575b7470826640f195c4381368d2c466c8848fc4cda0c353979021b2b3db98741127132baeb6896279799245943ad268736ef49ce6f812c1f880e306333fa1a8f1f0107722312987b2315541aa4e9f67796c9d508eefb21cdfcb49d06b05513e9efa7425b868065ab7752c002d7709de0922844a5db0e97a862c549063485b0fb8c047a3ae0d2ebc4631e7fa07fde517b68b0c02c34a22b9dd0d7172bedd018cf75872cd7d01522846f54987a4487f15832936d9e5cae5b8cd5647ae86e652cbbc0ba47d99ea72a1d4886a3220d33f04c4cae0706ea03632b0d60caf43b16468bf476e2030eba445c7ddd818e9ce1c019dd8ae2db03f734e6f92d4906df4931e554edf7a02a112540d4fd4cb51219eeee4f5c8eccaaeef9aa5062323f29692e1695e5bb9f8e71f15ccbc188a14d30bf692f663d388717274e60bffd45007b328b47d41aa1f7f4c595d7fbd4e4cd467617001ca934c1e4cb0ed09451826f0bc62fe1b083e2ce1258486e81fe982672c96fb1ede3066d628ae2451fb7030edbdd56b506c989f1d9b4340e58e2faa10b8b6b6720666529172c0dd6ca4cdcc61522cb9c2e43f140854f127f49392d04aaf8decc08dfa8a73219ff314d28f9e793cb803466cbb94d +I = 17fcefcfab016b16f655cb02798414e506b8bf30e6d11550a4ee2ac9c823cb1e85581ab5e6929582dd67f53e6af004af121c984b2b05e33e0414c9c577cedb4dc7da2d19678f892a05091ea07a2d22fa0b4dc6e16f8c828de13048fcf9c541cd0d715029045ed2323fcc0ee4e6d36aa220de4f1d18cd68cadda6b49d4ab05a1f1e2337d1763d3739719001e959a85cc412d6472037762b398f8f4495a5d42ebcc7728fd79bea7cbf353162726e260b5d8010648a3a2139ea7b7d3a3e02372a9d0bd1cefff51fd1b8800116a36e5a0a4201c81a8bae3d5ec8755f3891e895877aae97614ef75822a3ca7fdda18ad9d40d09e9bf24b19c483743557b158207125dc7adefbc50f67741949a47d3392e890ae0f53c7582b45f5f619b046045b8e32f0a5036ccfc6081447b9f72bf249970ee7daa33c5771aa3a041b64e4b613dd9501d8d7bf90c439905090109e2d2c74b95178cb59ad8cdae228385836b5adfeb3646cd3a3c02ec50ae74a364414b79db49f4044760d80ca081d044cd15d7d3fb4303db41ad9f20f6ba7ff1336f7f4ac30d5ae3df61ba7a3e7a4f1e458ddc7e623b70435a45239259ee396bd97c4815814d0c6efa25881fd3dbb78095e59fc069a4fcee60654ab63add0f2de556c59eff06d0e27c65f6a79b065e35aea42edc14d915a962c55819debc41e93254647d90d17c43abaf1f3fea7fdce706c6255de48d1a4a1bd58df3e24a41e6d413eb28036e062486d343af4b4ec53a4ec447a8c1a6fb76b77820d8c2f4195d8fb33f61cdc6fe4471f31586ddb272bfb4248c2a068c + +X = 1318ef78c9d3a3090ecfe3ffda6f72945178144eddb481cb7bd061c15fbff49a25be1f0f0f38e0b6e03f6bd81f4d2c6908ac197bf335ebfe4473ee949994a35bd808634a36edfbc50a81ab2a634a9d5363cea693aed4602554c6c264fdb41d7d047c45de6ff98a4b5d74d4ac7d7563d8ade1fc5fe566d34e833fc5ff686e1a48356f544dd7d16ca42457d7f3e69dc850037b1c4a31a595c7cc3e4a61cd4b7f6e4932553a2a3248c6471719101d53018de6ec97e80e45588b87fee7fc9766ea110c2dccb6d09543fda80c3d2703ac50bc82640756b42adcdeed75a64d9d582fb5a579b80df924a08b0929f3334d0435b5169f8fe8b352c13ad6f3679ad76ceaaa98d0ae7eea30180c6711154b699de8e5a58d8ec832bee279f8c9aa9d0c84f349056daca330c933b28fada4b2efc2fb0ba40fd93a9f8515186397b6ec5c12d216099e28f5070ce5c23e03761d998193340e5c5e43557bd2504bb93bca672afb6716575af15c99c209bbdb22333418812337a4a879e95297ff434c604623eac8ac14e19f1efb037d1fc3f7cf6ddee1ac32d43386816362a4402afea0d675fe1514a644b740475175fe47209e39eae0f3940e596086206caf2b428b26c0b78137e34cba1bbf88462d81532d73a7c51aa157c8b3fce36e78595ba1acdb768595e2b110bf0ef7f71d4f615e143e79c1a1c9fca5f30182bb89c0e22de09d489759768a574f615f1cf20a886e429f50b3c4af490cd8accda230d396b4fd2c99e5b010de6b1c96387506b4611c5d4873d917d479d36593db2a0ac154a227e961e8996ed4 +Y = 0e44113d58467ca2d7052accf22fd506c38a7334350c1a498500b126ab1c2247e6f7bfb0ad20ce28b360e7460d10b41b12d46420665d52faec49c927592a6b79581e5c75e1d5a037d5ab68779428a5223332f831fdabf8b8ae8dd0bad216543e1550dbd75e8533ab2173389a4402712fe40087855dda7295a1a439ee86d18552c0931f6d4cf585a691dde8b3bd81028e18ece5ab8a5bbc8171d3e5b2effee6f04933c9c68ec45a93a8f0c2896908bc62a95cff465873d48b40911c85dc5d16ab197cef94017a6b3eb7b48c498426c40e3184754c84d4ae33930ff4140bea103abcc32f1f39982ec88316724f9250a5a5071d02ab64ddd8b03711dba2d1fe299522c745b22db6b9fbd51ea9073cab106a874733855e27450009f925dee4ab7ec605bca83967be66f2efd2228a1b69ef43d48026aae7d5b6ea1f4280390d4d50e7f29008159c89921336bdff24328c89ed10921533635844a0d340c6a2d71928232485d47137339afc27675c7c77271921fd0409fb87e129348250b44bd7dd74980870b0685e9e840a53f39e3bb41023e5501bb59cd7cf0188f49a5993bbdb7d0ae407046106e560892e35c4db97be8bbd0a8ca69f1667303ad70df8d5fef1cae76de7b96ef6fa2caf21b5664c94b7d047ee24c7579787895349d8b3f92a0e98ec0eb93f96bc8d7e5ecabb1364fd396976ed0424222309891551982babb30d65d35fc602c1569768841ec21b215f777d540250701667399f15392b8196bb19de8df1f3ca6269a81df8bdd8a8357ee531b0a77d7fef3ec2180816bd9c9d398dd09e +M = 0090bda0bd7a033f17c8f10f2be47197c61573542e2404d6c4fad92dd994501d4a00064ec3f9a9a740f0cefead8359d00eaa1bb134b666f29eb5df658987fe40e4637b5e0b6e2d19ddf0e97ba6630db54ff02eaa6a76e2ecce60965ecfdaf8460736884fbef1f0370778aa10e3b3be2bd731932dcc89066318ae1231655d7c5b51629333542e3abc8293a055ac78f59a15a8645a13c4c786fbc51be240ba23dbf7bf9778283a7f4b9ced0e45423718d72978c9471b4af8d1baf8fcf776ff0f7209a138ac121ccaa53cfc3399eb46eef2deefd6b36d916c8ea10da159789b1b41be6518324b1a217bc903c41e993c32a703d1ce3abb964e0c992d14ed79363c5a50bc5bdfffc6b630b2ad9c6cfdef21c7d72623150ba5fb8275a4df3d69b2a03502cd38b5358657ca1cec04a58a959f7455bfa54bedd41ecc63803a6d48d458f493deeca755793d1f125194a92c1a990e04a146468e79338bbcb08014745d95b9fa72ec37fcafbd9b0baf782512b98a1f91e33e9ac81177552cb23e02fb13d4ec008f0171cb15a4db82662c53a77dce53c0d86b282f1c2806d9e5bdb3c3d6255fab5b6dc4a11d9a83ce661ba87500085f00b1dd728c623bc9f3b6ab3ce45cfa922291a4550a6cebd38129c88b7f29d851e365b63f60a4fdc85c965120541dc131021b4929b7ff35007b7bdc438d5f69a318932574b2f692dc2d590311a81d14e469dd95abd4d163a66e8ed935b6bf6a030c150f77da32ed6146869bce839d1a50b5520c196f3c3df70a3808788c9eec11a758a245bf87564d169b9f9c52b5b328 +I = 0865ba790cc0c9b408bde6765dc462063f3cac5556506ec95e89c83b8734a434b0602d6749a2543efa3ab6c1510071b000265e6c74ff555bc6111cdecb2fdc431a2d7f72ae37eca1adb329c51477d95800c42b483006b9bb4e164830cb6464740bd3a2b150c309a9d20280ea91879f721999d346916d52055b5e3f6d8661a1b1a7bad52b3ab76b08cb51b0d3188eb28107a0adf97189b0549e3f9021970434df1a12b46da5d50cefab7a99af4f6128577a7c0b8c7cae03728f4863efd1280e1510f55eb276010285ebce92153e8b6272ab29205b5fd800c4092ff281c0c2339dc402f046e2ce60d2471e888808bc9d460931514355b495778a9c885044895b57405aae2a78a4e60fdeaba822feea565384ca74d95938e4771e3336391ca3c1b0165a3e472b3608c19144a0b0e2c8a92e187ab82d32c3e2b29dc155a3d3b23b0a846a7924028e23af14298cf4a19853c3124591b8a0ae5e707aa9a58e232110fe032fbb7124161ccba64260191ee5aaa24cc085cab71542b98da8fc966a21242f168bfbeb40cbefd33d79539aa72fe98f205d831de046d53c9fbe3a47a3a65e9054179712ef8e6f667fff411341bc786206a9471df2a0a6d413253ea2a13a090daf8704bd443515d51d4c5d4b2cf84feab309be3749069d334d8001960049b5c106ddd78f17d2f408729491a34d19df5b12148b67e5e0dd02f291c70824fe9123bce2dc3ca1bd8fa9b024b36e439f5e5b169e3f41a81dc0167191ab5f374eed534a27bc67771f8197de8fe0075b59f42bd832556c407867c2a64df73f4b3775da + +X = 06030d3fc6a968054fafb6072012408d2bed798938fc3782c8fdf8f26ce2e1509ddcac0b7deaf2d3e213efcc6a7d1aeb00e86c45811f3a8847dffe345f1ab5ea38a8dd12c6dd4d4d607acac61af6fb0d3d9c59d2d394bd6edcf757c8aaf9a33a05369172d55ecf1b07d7ad32fbdba1c2a842048bc40f2575fd4ebcd9d4e425332ed70d84fdd8619c127ef3c78d33888c1426d1f96500207a09d43bbb42aa2eed8dc3f47a42ce1cebe88a866e856f5e4b4b6a8f577c198dc13517f613b5288aa5112e7a3136c373d70c8e89d668f66cc18eb498d71b9d55c1572212c83c128970137368badecdf992b3780b9991d6cec90a2113cc571c91d583e5c5daafff6e43fa36d3986d696a5d4dff24915367e68823a6aa9fdd17f7bd4e33150d7cf2e17807903d9960ce963543872f75a5b12558384671bedf5e1325cb71fa692daed4cd1c95c18a2e3fac51b35f40d4bfdd1b1f09a12f9394b1f5c1e50eb90f02e2cb8c680e0be24d94bed37ebee902c3aaaca0b04ff7d9b9fe799a474ae942884c721719d79d1f68180a6142e9503aea34fd2ed1638b2f5344b83a8cab4f8748432426e41ec2d228ba1f44536fb970ee9f93f1055b983f4529a252b55498e675626e3c9710b64590eae8aaf0ddd7e6e27df99056d7e33870f0683295745929a45f9c4f1430ee99e6a18a5fa3c4bd02c5166fa0aa5693bfefcc46e6dfb396bf2d7ac56d6fb91f5bbaee1c07148ff51d62a124d10c03d0a46887b71e4e4e99203a6eeb9d1074d103ea7bb92c670502b1d8d23b5b7c6fb94a6133ea1dd2a4101b1217af8b +Y = 196f55aa263eae81f592bcdb337229a69b21e119c56f524a81d54375d6c7dc9dae9c0b1361388ef467801f783b0d96e1104c07824f72f8b419a9aec969bbe5a8b44c90d20d85ae3afdf5da7fa1e13d09e6265fca6f2343007a7e3d71d84c75bc021007cbfb7e7cc6538fa5696879af3cbd41c95f7e884f19abbfc4237e3ded28afa50479747d7c6571ef87dc8049ab6312f98ad12c7dc04adcfa241933a14f99b1ddf550a1ccee4dcfc8863d3d73b0200bbbe4d549e59b19c850de9cf099ecdc144c5a89610f02290241bd7ce2b887cedf0f230822989abf25733cb69b1331097fd4c86f7eba863a3aa7928cf735032200d80ab9418dcce299483450e0a9f8a9fe976bda5ac27bcc841dcb8f5879fa4141c21ce99227e6b1ff827762f415686e06146b2d2e82af5a889b83c6fb34e2dabd3c815152342048934b3349003fd2ed67b02abbc82d32b27497e1f45b0237e707ff49dc044770b532408d55d11e743b31168396d8c2d93a636c221e96bba73a0135d2015b149fed45b7cc5e56dd9cff1952862085230a618e871eca14396e9e32b87982dcfa5e583a80e5d76ae80de6f62abbb28fb313aa3f501ab179d1f1df0ba80084357bb004312981d9e9dfa820228ef44e8a7f189b995df4a192d4945af32234ee3f2022e4604a3542810eb1c304db86aba95d5bc5787fae900cf213b52a56b200f93b95855872a77d73a122912b9a39e3861793c57ea4c75c754cea27107906a96bd76b98f06532dca2926b3d04757e4749470155b0fdd79fd82ebfd57b20af6e713cfad12afd685de51048a0 +M = 19ac0d23cab51f333aa4c9df8cac24eaf3b879aef72aaaf03afbf0524814bedeb37139ddcd157a0a802c3754478076cc1675eb7af43046cfabda169a2bf666b2c928809e12ec65559005839e928ac2b01b468336735a4939b1c0a4f4b69ecb02067203b50448da37191bff33f5159eeb18450dd39b6d437a15c08cdb65a53a53f0d36737f058c92db7eab04155c2b25c179ba7da8f2e615c9b41ebb895732c36f37bdaa4ef2413bf4204a12596ee7c96cefeb437bf87d163ca36523ee10b9eec163b0e02497d5ac22cf7705b194f9b500d093400dd91f721519e1e62b223ed3781db61d3d5d932ff2d120545ef1700641970643cafe474bbd0fb08714285c739af2d85f971f9a1bc4dacc443d1c7b36aa4ab396fe1da91f34faecbc539b326b2105027859524ac22c6ea6053d6f336efcacf3664395f99673690ad63f011850c7f3bbdee6b4d7f6cf11dc3ce6d747af21106ccb7238c42503b2ed596c512a4690754de0a4ffaf58fa5b60f05538242ec6830b3547d49b9301d53b3242a3ece300cf560719cb227f1968133dc14fe43d3f08a31aca0c383dc948d5f9846e730c740f76526c4fcf3a2cb7766ded04e202a0b02504af26350905b05e1453bbc3eea0a296b69d8ef4089fddb196362c5d3b2ff87855a4a9c7f3d6440536b04d66539062b846e4fc2cec8bf7731f4c06e9a546df97be4b5ad59c9fb04e737c4ad79add3e0b30a86b1eb7882babe40fafa8e840553dd19238136dca38919dc9efbb0f29cc732c7330933b62a98e5dfd961d3f92ad03765eb49f1c8384d430e991b4871 +I = 0b9ac943a67c5929eda7650e12ca06ed51a2e5f774e2767d232cc802bdf22390909e45ac0ceec2f026944b9a1243e585121717d63e118589005b2701c3a6cfb8d7e32ab612709dce3ebb1ba839b5fe28ce902b648e464f0e4362a84756956603125370552b8296be18f45a934eecb82dedea2d62e103af9ad3831c6e37a2ef5ffe248de089cbeb9764f377b5571633100e5c8d62c32688696ed1047d522b5ef5d72e1a546fdf67ba68a99dadc6a2fd4b4dc6f0cd2f90b66b6057a2b9f88bd306192b72c8744b8ebd977b1ebff148cbeb1b2a9104979c105134980bb6909d5642f8c6d911e27d9bc40aa54267a690376b01c377de60a541ff7cd9ffc2ca43051b95c567b54524220b480b5b4b583a9bd7b96de0b5953598cd72d6393e3ef6a29303fce3c9b83c0f53e74c9ed583af1701e2682a80a72a72a7e8a4af08a9a2afd5d5ad3e971a1df7c332329ff48be2fcde18226523d2c2634c22b39cdd66b16d2360bc6ea5d55b570bb6bb3cb29a450c33fa91b7f52af5f369d4c10ca022279ae30ca49a9c87c8d2595ffc01194cf63390b38a5863215e065a2eaafb85ab19f0480dda51e41f396598acbe961b96288e3a0ae368e375950270147024f9bab27932231d898cb00db9c92411b62c36d9e26e7d5b46d2d6838c9eb07a1122af60b6cb1769639392cb81f3313e785e2b755c1f1dbe1d48819a82435ce434ea7198e60dc6b79fe2e5e511911a6b46f2f4d4e6dd0f78bfdb36560ffeea59d8ee53101768772c063d69abd923debe1891501f5f31d73d59aed4bfeb862634390784d49f19 + +X = 1037b3000846f5f295f74b759c142e0ed6dd162d4973adcef1dd462a59cc1c01c9e6afc78cd2b91549a1a2f7f8884e9807d113f5d14ea9896304828b94f7715f0b6edc8358cb54b57e84bfd17338d6aa0ca42eb9c44491e5db06313ae317bd2d071a4436ded7347086516ae27a2d4ef6fb2a63467687ff3fccc837028d7fb5c65634311c62583b3111a6cbf3942f63c414cbaaa0491a19d30c66d96edc5bf943814d353de1d488d34d467de8237264ae1197d94271bc02a3445e916641739a920859be6a079113233b5fa9fae5d7306a55991edd79418e76804b3cf80136570ddf2d839603e9f9424b3ce5286a87ee2118100a4c4f3427b3bf34d631f0b6280064e3a1477d0d19d87b98ea0cdd8ea910d391625ad6dd6c1ee3ac4aa96d2269340640b0ef5b5f70486f8871680de818bf158359d46744e197a33fb62b84f3d707b9143dfcf7e7bbfd1ae60dfa947ab9aa02e9830dabac464c259dd457f30c7b77dc973ca7854114619be2d21c6c52dd2828b904cce52ae2b47d3f0e5938b41be307e14a3b78f34502aaed737c19a3448127f0b9f348c25c4c8f9d6980e996a902ce0577f07512834e833d583417ee6e6113b02f9b2ef7ac2625f849853dd707f77a97dc6996a995532d4090a431d0263954c7eeda6ce3099e671368df645ff64b1643a4774835fe274c5d2b9d51bdd6bcab08b1400e899688ac81c0e7eae4a15a073a4d0878bf24da302aff7b7fd5c3090467044c2029bb255616511e6173d709d6a2bdb92dea7cee313808bc2070faff200e406f491e3bcce82e393016709fe3 +Y = 166b57043873c692d67f894c91c36aa7af59fa17b7e5c820035c189092d3cabdd22b0a1f4afd81e8ec1000abbeb52c82043e7dfcda7f758703a13937d1bd4855336f4fa5f498a2ebec5f4c31fc9c41f9b9f104d7e66909a853be6fb9c99c2244078166ff52af4534a17544e9f1f205a1f5f94d82025e037a7b74ec0d8f17b69142e849dd33b0cd6f5ab84ddc7f65999818dfc9460ca6fea70dd50768bc51398803819896b4fa1ba8167513aa4cab738b6caf236cc768d934df3f89692e69ae860616334e170cca45e3a801ead75e17a57dbbcdef6cac34d06918743854f05a86cd6fee10529e8b39019f0e6f88504ef60807db6f84c12162d501dbf75e7b7930e77da930c89fc23fb926425d40d3d93fc949a7300c5310a77663e4bfbd973b3f0328dc02e15937f15df3ea2d4f59eb6774bc050fc334f8d8ddcf28c88e3ae6eb752e645517c294d19abc3a4bb83a91260e202111a9260d80203fb10a6bb1c97db63936fdfd1eefe6b9bbc31b2d5171282192785a73570066b35757339809be89074ef208308c6de69eb3f1de31edc6bc907ecae9d576a36331dfdedbdd2bc72b67ab976db47a4bfc8ed578d7d10721340ea06941632deab25090f4fcf09c7ef5f3a1ce62281e5c8373c16a36c3a20d6d5bdeb60b212634374d81569da74f4f7b0c9022713977e1fdbd33ef3fef559246f5b709afeb74647e7bf60bec6ffaf82e9c98d9faf89267cc893de04399263f98015f01c9835fea62a1135dcd58653f42c44f5d12267752f2ad5e27559c2e5b121e2cd895e3128e7954ea607f1dea1e14 +M = 18a89d4a206e870a2d3d58e8a511d23bd246b3204f9c3c102a356aa624d732dd7a86ad760c0da0f3c9ba1e51cf577018044239150133b1f349fe57e04658456b5758ff257ed5675dc0e79625ca03cf029572f39515b8576e6a8447ea1e458073102a56086fabd78a6b30211312b8db5a871453b5e0e6e6d5c6622406c58b5c118f26a788a6bb79cf0cbdfc87819e61f10150f48e758e7e2a513f95a7a4e28b8c7db9f85772ecc5d1d890447ebe1cdd6e147d7f225b0a3965e4e7eac28acb80fa180d91ebf87a4b617a65476e529c0db512eb2a377545e6dd9e7dd78f0cdfce8c21cb0c4d7b4ba843b4badcec45387b7c185358a741eb3e7d207f8dce9dee3c9a253e7856de498009111c53731973e10d4583b1df40314214bf18c80054545f25004be41e503b8dd58ab153f05a6cee7d6a079832734d24c70b099d4de7c68791d01b9f5683bb33fe29d1bfbbacbfc6550a6bc5892edb1b05c4f3362d2af12ff7afe65046dc7d55151035d83182510281b7586fd100bbc63c75fbc8e5c70ca6980de4c1b771a9391dc6dc0257249e0b1a44c88c45d5f8971b02a8f4fea6fdfee4bdf2bd7194be45fed26c19a6af9e2d2900d778e7e51e67c046331ad7f8b1cf2a6bef8c2c14bd1c056d444c43c465e0d17e8bca0707b0fa0b6c87514730892dda07cab6fa230eb97764588052152f78ac861ac55eed67c6ac666b891a0ecf196eedd67f2f88133425225487d04574949a0d05baafa418cac856c2876c4c53dcab9ab9fa706a1d6268040af63c8c9defc78dcc55d7675778748410215a85e7f9db +I = 02ad6b5ef26b8e083d6afc8d2329451109e5b323436f0f7f6289495e381ffecee310c53bd715f8435d2b7aebd85342ec1070b8ac6fe5d8bfe15d0d36875ae16fe896ecf19fa2e1baa0f9b2c5eec1995505a4dc379dccdc2e0d9dc478ea5cbce915e89d167d88b0169a7157f1025733533d99bc2f41cbec4b46b0d78413d0ed86e485b676bb644b4525c2cc5346476d560333f5aec5f2b36c9f0b43c27edffdcfcdfb0a3972b20ad8c4b83af97410f375383e678bd7bfd1859d6a4f08989667210edc1cecef6d8488f7f3f714c84e33f8323c1233a64864c6fbc3921fa0087195e7b0f86cf893223038b8ef7458bffea401d34102345c909c719482d67388f2c700363c11703cda8efd82d5269bfd42c5bef65d24cf6b68ec9da372b5016337571038fbce416b3a0ff6472f37d567afe8f832ababaab426532d14c834f0008aef8fb0605890eba8ceb20b8944e8b1319b0975a1d7e64aa5f5024f961f43ca187bdb640cba09aad276aba6eada6de7ec79ee6b8c30e898262c97ab7da632d42dfd02ea8ec3082cb8fe511ac805a7187dd27263178142adc2610b251ce1af47f066dc2327fb8948297decdb3c2920da060f0af63f000a626f276835b6b929b4a0f3d46ece6b12e32d424a87a8d4a5a0c203b77a21f0727f6377cac9e1364b063c7c09e4c94d4d909816737b4c6b572235a1fa3aef1e7dc91556d849ccd36ba1d97bc90160378c696c6ebd0b1e965ea066e50ec39246bbc39e6485222806083c06596c4da5b1a9f7226e123d12f94d555bbc9824112c32b833e9eb5aba15512a459d + +X = 0a0cea2b661fb36f0e1902ea037cd3f1ee87528cfb8ad0f9ce9441c0dcf34806dbaf53c1a0c8acb22e8d4f5b7e578d580e7905765665e01d15627114f02174c059b6bf79115b0257de15046d083e78705102ee4660db4fdb450beb2b82ce6a9d0b0d110a0600fa3737cf528f7d5abeda73a0c8174aa5390e9e9eb220111f3d539c01e7a751054ad541aefac5032086f700bbbdb7ed2ee4b5146eb86fffe1be6a047c74e86693e4317fb0ea044124fbc6f6ec2ca773f4f369d5ce2b8d6ffb3c0e15b6d7362114cbb84b2e67cf8afdea5dad66b49e069010aa735a81077cd3a4022db5c1e6632eafbbdf0e73ad6db218c715c7d79b25a6f1b2cfd207a08330a8c20fe01e59ecf02e55058002ae384309d94408b667af4a5116a9b9a8b0f4ec630a0ccedd75678d4d746294fce2e9e1fe3c509ee9fecf578b23c1b572c4736e120fae6a4842d52e98043e50ff0b5cc0c23113cfcce4100aee0624a3d862385fd029d91acf196adcab340aa764a2a72d120f906365513b4dc8c7ed533db5b6764be21553b0df97ee2640a145103b790b8b4e1e5146ae94508562f03c3a5f5999a2b5e9b1ea49224e448d1fc45968e22d7b82059efefadf00ac2714cf29e5b5f0f33209e10d02a903b346ccac053b4f3b1e4a5b8bb432996307e490b77bd7bd1c857b02952f0fef951f7230467fef9561e20b8e3a828070a5d44f8b0c01ab5a27e5cf5d2026dade03d8558fe750fe1055ea3f0550c0f4e4117bbbce03a463ac17939d3fc79679f33445df8db9d6e04d3d95b067a7aedb72f81b4b72698efb2d739c90 +Y = 17e764aca64f1eaa5fdf91fdcee50a867f2e4b68e71fa98de77b3b9dc3adaf1955ef825a2069305c4caabd98f97fdd2608030d2cd79e255ed4b6eb229b30b4c4ac007f31b833a0c476f86973f673d4a7f9ea2c78cee3fefd83efb4b6cea78f530972d632ef0996303e1dd5ae562ada2c6cf1299c785930ab236421cb3ae11b7cc02cde163b79dde3acadb4c61fbc9cff0379489887ac9f04254a10d49510174d1d85d526dd3d1cf0a7f5ba459d9f3c3fc914fe82324444f07dec8cdab382d2e604266755e3928c08bee3a8fc5b5936da2af544dd49552c79136e21412162846e5508603a96a24e7d3d9e39c4ec7097b6123d931b93a5cb2e747b4e27c8fc2864a3e5bad760f908c17eab9167f20f49f044c5109c2fe16d2fa1b0e58aa02eee6a18d370ce5817e2d0a9e00c2226caa4b1a2185b5325caee356165942965a1fb31bda428ae7b2a0b0fa0b9d32562a55485195aecdd47edb94e6ea6ed442003e2b3391c12739ee484b940d28dd7980796fda985a25a2a1e01e07c97b5d3d578c0b717d84163d9d11947b9ebc81ed5e247b3b97829f35c3338c98d3840540fec2f1dc7d9b75122dfc2a43ec033cc7c107a920a9c9f0e2d791541433860424d75782494c2fc40f695f8faf4087911e9bf0f6bdda6ba64eae407ac7941abc7c452971919f1c9825b1d5992b29cd7a208ff4f3b276c44b7cc826b8108f108790b99eae1ba27ea348cac252bfd30e7f01ffba8cd149fc874c77e15b55982eef9e3b3d332395f0e3b61b4ab0fba4f72bf339136a921151370a15b53a188751e2ec8086c0c +M = 0554aa229b562bfca31821c123be6864aca002bcf8a0b25de641ff11974aac7654fd2aeba4454905bf58a8049bd1d11f04b71b847fe10d5e2fc8d03978ba8113b6050fc8441fc5fca82257f3124fffa777d4dc8cdd04bb50e6f193fb7cf6886109f084155e13f0fae7b92a6073f7a7bca8f11ba16cce94c3489b4080cfdd8f75db79bd04d69b50ee9f384bc6fa375c3e00dae02a74084f25e75451180e2cced2f44d74624105f3a7e8e966308cbf49a36b1abee1d358e26f48f3e32c3951c5af099d84e39c3be54dfcefd565fe75921f713f275d1a5d6dff4ae5b4c08799ec5b0eb09f8afa1bfdf0705fab8ac41027d51381b76ad281ca80564a1aa959a90b6f7ce59e1231711858a05c965589f386b741f2a8ca739a30bdda4c732c3b4046eb0fbd3c1f2a35dc6bea6c90d44061490c5efd512b8b384bb6e18356275739fcb48a27a0361eca92b7ce7784902daac1d80579376de51ccf688fe8c8a66188bab44ae56ab7c5edf4c894d92014e23d9e7ff99b7cff830a87b3a70d3cd7b47551bf0d67aed463a11609eb3d36ea95ab503b2e1a1dbcc0ebe86a6b7c921e04bcb324ca19c7144b8868831192a5e214ab1e7a1339761435382ddc4fec389052301e06e111f1bac7d96cd6d8adf6443b1cc108c36e66d3d9c92b43e71067663ebd54910ac729c7933d4f6cbe43014d72e76ec9e11e92d2ad6c21b06478ad6f151776ada82d97364e0b32b733dbf6e7fc1f6ef519156f4f541e92cf2d58fadcce23bdfe02ae44edcdb89e4f66b109f8134a79a89ffc15b731b859dd17d1a6f4efe6b576 +I = 041aa96a8291600e4e0e2609071e62697bb39fb83ede38365fba3833f8f69c3b222f846a3bf14f960b1cc8409583f0ec09280974ee823c4af5240d311d72b7d3e1e1892788ad52e2931436655b094f16d10393fbbc2d37a7ead92e10475e3d140ecb1e0f735664e00990210ae870d403e439c7ec53481ffcbdd6e59ff7de95184a59c76db15203e8e2e51f59af131f9a144a7bcc2dc1d9be044a551725037b45b2f451586b5361fbec172d04c3c60f20eac3082ba1ff48bd82f7008b8ef1c95205058d7bdaf166b4009edbaa0770ed6d4149fe2ed90c566eecb14235baf67d94ce506c6e9fed33c6344827d0c4312eef16f252382c0dfcda1cbf5c43733f472fa6c3db6d7a10cfc61c4fdea166fd8cb5734e08859613d3256e01e866af6f88240e0159f7eaa9df609f7d865301f137fd0f88bec38ac63aa4d124de1405c1b740a5cebb7231fea79e4544f70dc109e60a0e11334a59211b3eefc22e0fe76d60576fb3147a1e47381ab99749f8b89dcad53f5f9368182023f1271e3448cd6e0214180c18cf24a864f31b5fd44428502204087957b486922fc006acf1b5171e933e8c278b24211aefbcccc156b25975681907287909c87b42da712069c2323a208437ac875651a4760ffca96e7527ec8f7e459831ce255e5cbcbb083b77783d4d6c1921a8b1f036e0fb85ac71bb1fe9c6827ceb2d3465944e420f9618d3b6a836d10fb518cae3e3ed18cb22a2a8ff5b2d110b8a6adbf7e94e9ae309f0cb9165066ca9a89b03364482c93e06a81fc2277935d3c916641d2da0fd49e6fd732aa93012 + +X = 104331c5843b2318bd5e13390fe63b70715539ae5c5850b125e34e447cd7bb4e5c98b0e64fbe6a0ec351ac6a7fc8af9b0586a044622b437d26a6824cf12cd82be09b8d1c678c3e04734c77644311822a63be547b04140b2b2ccf10c1469d85e807d003e8107a40d18554eb3d61d48e6d3c48d58cf7d4baf95acaaa63ee08f161654e9430f1d31a832a41ec8beebfd08d04fa2eb0081dfd5c5e666dc54d23ed56ef7c2b0773ec664d31c9c3d7bb3e7ac6d443137309eb30a1ebe5a93aecefcfda0c4926f2e3d30048d111f12c773ea1f305fc970291ce17d9fbe221faa4e857f94a575f79969f49a18fc9e667cadea0c205c7f120672cd8cd29e0c7f4fa2240a27d6aabce5d338658927d7a831a4f255dcefdf672da890c9f4b59302d082af4f90c5b1344378aaad43e9a40806f0e13965f719fe8468a4970e87c20a11eb7de85e22144c7cc468d3b59c03e8de69901310d49063a5d1a85207568d9b878bb8b86db2433fd6e1fb0d455cd35a06a4bc72bc88482fff7205f05482739bb73c8b2d20b8fae7199edc9e8f074b56b197ba0f8416364d3cfdeffa83cd5029555b14c59c77a23e59fd2c27d48fa1f3053ef2c9b074d7f8de5d24d58b87a410671d80f948f5d55689705275216e3a2ef90043b5da2cf68be1f2f2f679228637a09f37b8407adfdc45918cb35ac1cac20f9ce6d4f116510c8ef0b339fa4ff846756912d4f6f0b74f1abe38acb4e3aba8cb1dee9680931e72312bb0e534ac325023e5840d72d8cc3f0cb25b00a37a686fa0569a42aeb4544c1363eba016e04eedd37377642 +Y = 184f086b472409c2f43724f385f14e775a3b9b068d30cc6f2be9e7fe11d09cd70f89d851e81eb8fb162a300760ecbf3f15239db2aee051b8006f651ae5bae840d8f796028c899962f7734c05440aa9fd7a4fa530625caef958e9a7cfad8b8d2e10ad25b4074dc568792161eb25bbe0a17530275e85430a4272f1592dd251319fec8546ab116a78637796c336e1cb180113e9a579869a7271954ed90b5f922fede100f729eb60f53a24e58ae866d945466dbca1a9ab24c532c559caeee898ce0019bc1a86684cadc551f64b8e4af4579530a8c79278f5263a9b9715e43381b53efc48c05ba20724ca7b00bcee16f12b6a0b7f515495e3c46460aad68dc1e750987bc96b6a4950a3f16a82abfab23d6f7a2b3f7d6445eb851fa2e092fa2f13db910c6e6bedf8f9551ec00c38637da0c6d3fd98d1ed50f91ff58e941b7b7dae97a4cd83f41912bb8835041939bd161c8fc20c0e700b37e2954fe5bf2019f19076e5f736c4b773c158aa4446ff57ea455010fcede84a838754df090df7b5f94680020d44e9ab0818f8309797a6303c4490da60973601e5d74cb00d95c0b80f9b0d33a585afa9a5347fa93e46137612cab58008acdb66b71b81435a6cddc8f8e05c1c8f92a5f60d189a937197ea4cb67ba6520c6213d8918c73dfdbe4910e75534e96187b90921f78c8174e63b300f38724b5d9eb2a7a9435f257480bf04028d4f4a9f826ba17484c229a14811c2a8bb4c27a11a8eec702c4161d6eff145fd9af9c82e49f7e2db5372556807cabb4ff6f1495d0d35066b056a455469566ad9b0c876c +M = 0bc45a2679a8f923d2658b0d17ba730b77a557be7a883beb08b1dbdb493db1fc30ed3bc40a346df162500341d8a124c70506ee03404d1cf484a84ab1bf55a3a9fb71eb601778c37926b0972acb13f8e349d16c0fc70c54c32c8292fc9c0f939c050e1d51b7bb64c6af0eb755e3b91c9746519db115b9c8ec26a5715784dd37de3620b33deb8ac266af9d0904da26622b16b7d0f31cf47c40ddcc4ad0910196fed64aa717f0cf539d7f8bac2a0326869b4e00118765686e8143bfadb8c08ef1460d7caeb41af0c0ba799681ce9374078ecf5f8639f7a69c5da77cc0ae4094baec5aa4a147ada2dd90ad2541f47f9430da165be5e9f2a40382924784a19c94f084ea6836378a94736bc14b3e078c0404a1bdc7cae0c9be835067ec3e4305f6efe4197f9af2ef32be902a2fe94d8daacc8946645c141a3a986c08472ef466360566a62de41022f3f04705cc2d84abb8401f03262acdccff268e22a4163ae29aac66d18f4792a10caa81ee85827aaa77c78b658af10b4373f52efe17faf51e76c13914972d7ac63e3c4c4dbe9b32f7e32a95dd70441d95aa5d8c74cb7fb73c56e29061de9d879ce167d155756c0270af856a17f4fdfc98804ab9824a075627121a9f89edfcbb571a7598687620b7b3af098b9823a85a687453f9abb89d7579a87f5005c7a23031a09d20863531b55604d24460e39b9a68c3675c737d63e225b1e09c7e7d3ae75ced22a6e3645a856159a6200cbe076a899f55248727c60fd867ca23d7d1740aaadabe8809fce49cb7e4145744127c9f8f9aaeda29c25e5e583a73b9 +I = 075530c8bc1af3c313b613ad837d5a88b1b59fde8df0051ad9d2ad5dd0bf9548b9a68b1f3c1326941f614af645ad30d30dfbad9ce25ceb24538baca754974505a639b8d408afdc83316b24cda489a98f15a765f032ab1bbbfa474d783b267bdb195c2287c7baa52b682e96299872c509e0d21e934a8b702dcf6fd44a7940237dffc2c70dbd82120dc10aeba9095c069100e0ea97a55c5a7aba44b156600e55034b0565247a4b4c0b77607088b5b5af73f3c0876568370153905330a6ce09ba80119acafe93b43080077b9d0a7fab3fda974b9eb983ff26cf05ffbd138507163d29982398fc3efea654f9ab46f05bb7aa0fd2c3b2e8ed99739a0ea03307fa878dd131ef7606d1b80c131c7ebaee945bfd15db5bfe6e4ccf3d7aec96197e33a7761328424bee80de45d21550a9d4a24e430ef3ae41e9d3cbccf58c861c1b9ace36642ea10127693f645ccdfe9a335fdf050c60aa603bb8a3a43bbeac8cdfed1ec81d593199ec379cd9bb5c8d21b6808d914c92c2a09c6467b36831f3c766fcd99f02410070973270e5309f5632b02a51f79c7215dc7d3c76cd85d1c8b94d820bd9c998a46e59bea51260c4df0385049a6d1474626735c44241fb47a43f62996057b22562240f422a13d6ae117907619428214b82ff9e96ac49798b66954a6a05e405d565b3ebcbdc7129439c36ce848dd77f856e4eea099e463948c3229e36e6d9b7bd02f0d7c0b7a9358f1d67ff7ffe290d172c88bf58f5e4eeb91a76c2a12b5e0006b88c71c25889840323f4f071d654d0c9b3876ebdac4e69bce0417a859142 + +X = 1931e71e87e297305081d1648cdc5291a95b2d5829cb9a17ef6e0e38316cb158fbb85bf8e8646e8a572219f4f93d5580173ac650d003e1ccde9fddcdc10eaf0d63789659b614eb86663d8a98be26221d75dc1d2479417c7b28be152a9335ba7b1574683103b3f462a31b7cfe464623f4b47283528bd99545958975745faa94da4888e68b5418e912ca992b0badffdbd50bd6076eef97d23aa780e7ada2e37ca4ccf1ef1457eac83da7ed00880affbc2521c4a668451c3bc77a669a49c16d5bd4047f5c3ed123d0c29c48c677e41bde2e8014c428370407fab76c2460ce34e8c09dc54c9faaf9dafe9ee07bfeecf8aa3a17835d1e045144fc36a4a7c6b16e96e3ed50bf7b10c9dff945ec879daf94e28ce99a350e77bc20bb0d25e4e2ca4ae0f111a52c3168a6d2b8d3431942fb50f864a891e4bd7d6bc1f6c82a7ffe2b7cec37c90ca357f76c471bae9c0a8dff529440040bfe68f0106a08d7a99f13a315c98bb619e29f9a74259d460f67e9b01a0481ed36c75721d675932f00643d5b2dd4241900f0e681ce72d675e8fd8466b5a2512d76a428ddffc24e18b9c3638dadbe7a7beecb37403584411a930c4ba98be2cd159df76986d32a5c00fc743f0a7cf8cc5b5382b49c4e97727d091e9219f7249559b7eaa70ab43821a6138b3ea8ce99ae12a8f5177aea5619c839c3cbe93e3bba444c33df46e4e0790f089b7b80c5f981ce8de4927e2166e161d5b49d20b637390e434f003f1992325eef6f27fef3d8cf6bb90f75880fe853be3ddefc99aed10407462f3866d15cc277a4dcd283ffaea4 +Y = 005099fc7592c452085175bf36b6a3393583645f85808df40b455986a7116aa8e2bc8da62f5730b362e28e1ef7560b8702e08c5d00b8228c0da2e47ae3cb1b1372e6917ba43d9e310c0ef3599de7552aaa1862ee0abc3aca58d051708016651404ac4fccfb94e320845af8961987961f9c78efd339b3db10b7fff0387f8e6d483e86642c8af2771daaab460e65a749fc05f191ad873205ed59f4eea1a1522ea7707413e55939f7ef265cc95ab728c5570f796227d0599d5e6bae542430c9ef6f059934ef2d54aaafd8090dab36d593f2de11657b09f7c34c910a89d818e9628f8939934e67227ac2e9d2ec9cf2646fde15d4081ff1b49b4f344b8376f91dfdb00b0223d6f83e40dca44d64258d8ace417ef239234d6bf7c34746dda3e278ab7603757882a7a330002b92087539fa214c99c67103cf0f1089d591fcfb3764b7813fa013a7408a7c07703e06250cf4a2440cca4b2f2a74287e2f880e022028891fdd8ce3114d743800e179dc4c5d522ca94d36f910e101ce6aaa87bdae0000a6c5196677115dec99f9e5c15aa1ca758fd4def3f1c7a7b88dc2644beaebb99dec60bf39f67d7e97c73f2c9f82f4b42f4b6e037822cfcb8aeef95948cf1a2298e371a89bcb10b2af258156b07d145458db49a40636691fd6eb86c6de768cf18cdf350d42256d1a245a103dcc276b5ac95295d27a39eb88262de52708f7ec6372a62e05774f47ae05e4e7bfcc2c798b418a6d084e04fb2dc15c72a8314a67115ab54f29760cb8ec3dcd50c7e21c8a4441fe3ab4e009941c074c35de88012d95e20a94 +M = 15a8f5a76d6b703129b2c43413b983092ceb3891afcd52316178a13508ea284a7d119b3cea216abb717079f970332de419e89491ccad0ec9f0030f86b88b7c8f5e3cd834d378ac3b690cb7ccb586a9512fb37bab4c57037dc52b16d33b42d8fd16f5ce8a4dbcbffe7bf946d66f34a6d9b8157341a89dc7550b419ec1f90c8b6733a854af90871d7b3aac862ffa42184f19e9b729e94a59a30fff4ef0f8b9e9e036c8e7d8bc7cf95e9184711791364d7a187d7cac03d4ee62c94e25898fd9cd9a1965be597d8245b0e0a31ce2d309ed2b7cccbad42e9435c4a7f325e8541b4ec99e762a09605ee6ca2877f56a1d955ed00a0b8e41377508a70e5c6ce0df41c9bb1cd2dfe45d9e8d1f622f64361e56808a18219097c3477e5d59f997e03aae1970140c436486188e82530e5308b21c9c7ca2a1453c781fd1cc82e8ab00d6bf215babde38e1e2cf9319ab7c1a162a4241361846904bc698331cd7855d85671030f5434e31d99de7e34436c34b90c0009c6961f27f10e1414eb617ff4f482cff8c2a17577bb835df558361648f3a980d7425edf1fab0b3b4b56a67413cb147bc4bacea11d409212e1169e4750ced1375f9a40c5957c3f460126bedc3b70a02ec761eaf061dfcb1d571a67a02b380876a4a928b9cffd61277d5af258d8a0712ae93690146066ecfe99746485a938ef2c3b1371bf1c85e34a78e6c6cdd3bfff4b7bf311a29d3af5b12cfd3e2b109786a09dbf3057112ff9255231f104d96ef7a88cbac983de8efd43e42f1e887a0cff4e569cc3f7d97b1b3a22edc8890337e9894a97f +I = 09d9767c533d4ab9df4fda8fc070db127ac99bbd0cc321b7e4165116f987cee70a525b2fc8001bbe7e0762afa629040e075f70b4cfb87cd3f05a906b93101e58afacc45edd77739a49ca6e3845ad6c03410b39426f8a20fd516394cbd9aef2ab0552a70050510e937ce0b22ab0542a479846f21a00abb214139d89afae6619b7bf01e507f2dc41a74b70e2f7f5dbe97b11d9db0d6af7b629303a6b852df72669fecda0ec9204b04daff24e39def49028bf5cd825f1feb08a76a5a0854ca441a7114df15cd770993144268fe4c4723b1d45de4d68ccf39d2ca3fcbb3fef7b63e84b8cf7b0730837df9ca3a238f0d4994f011a43405c32cf8661021e130fff55c3a99238cbd2d986ac671bdffdf28e75a85bbaa6c06bf0ea604979662d17d91aec10f79ba94a78e7f326d957c400825c1bda471a27f2882d8fdeefe698b05ffe4b509a57d1b125b46a8ec46038fd8914911340beffc42628911bd1e5e73a88d54ed16e8570c18e5ad60e9537115763914070c5ab82a21364e0e98e0e655774e52e00ec721773319e6014ebfb9e71126d6d54a3766f62893f60f0323f53c44c4712d30251e3e7a390bbbab18b723bdeef5f01bc1be37a2573e9c9bee9ddfb8a21462b7f2225e44ae408704160831fca2349edfbc075bd418efd508bdd1994d07df817736893aeb80c8f520304ff8450878e4898105a24fbeb243a0f05a58e1fc9dc7991f5a44cf126071602f8f4e9598ee516698997c5a097a52e369454d8db4785a71a5a792cc0f041f0c8bf853eb0acf860733e54618186233e8088a75670ae2c + +X = 15eb46ed735ff303b36e558402fa885db728c03773c7597482bf4b7f261546f96775f810bdb35132a7fb7a16007dd88c184d6176f2a1bc65a80d24e9452ace0ce915719bb1304cf70b184650c5575074648c27149e5fb7bcb9c0506365545c30028370f748d619d2e1beb395a856ba6c0c8fd1d80be0bc475391a51cb7a3c0375207b817aa4267babcdb2b26750dc26e0891ddd9f7e6c64d187aa3d46544e4540bbae31ddb8903eabde46ab86ec0d6518800d3001147ef4c565b4aa59bb4109810b3d5f46b8966f12a91f44a8358444a6a8f68ac2f7adf23f558605d8e53006f531058b6b9ab4a1018de92e9f76652d617c470ed61e8d9c0ab6eda35df6da66d67a134fc337c8e0bd5577be89189aa293353bf634d26c95d278dc883de710ab30a53821f7c263c75aff40670a8be272a499bfa9ae4b0ec35fa7677fb1af5f1bb0bc26aaae278f98e511ab7ceeec27eb000a2889c6aac53fe92868a3a12147e93b8804968867875f4bb35f644c7b2f553ce93db853cc3af077b9362b27d9e528401d85e91196e3c572d319c2ae9ea2a9b59c1d2a097aaaf8c446639317eb05fda62b5742a5c4517fbee85f4c068e3a57a18da4db35910df451ee8950223952463a6e3958f1517244b5cf67806b774afeb2178aefddb62f6cd4f7d69d6f1385744176e065796465591ac74172c7a4310ebaf7debcae083b60b770a225e7a9b68b80d2b0ff0adbcd9606d85a011ac44ed97086a65dfeb2cb14da6adcd80d0b95f3f55917fcc0750da8fccf1094307e4111565e7cddf3fd02b064432a4f690acd083 +Y = 0e9e33784ec47645ad088c5324d639c2c4a672bd810abfa26d289d6bb68e83e46d42d521aa2de3bf337572cdd4cf9b5b00927a963bba763467df7d67194ede1fe706a6fdabee1663d3b13d383f377d1eef4e930f0e37b054801db48e804b8c1813ec809f34bb25fc72464b9b1a3c121ad6438236a6bfcee0373c2b386e2ac442592a449d1b5d58ab1b058c9450c6c49d0529fed0552afceaa6346c75a3969c9c5a6df5f00c1656d6d89e99d15b6cf8d8e9da5ec67c15d9a43a39f9f9c0c3bf5810f88cde8a8dc120f756a8b1d76f2e5e3dd131ebbf08daaae4488f3a1b37b2c9d9dafd1ca23425c9e9cba5651771101d07ab2fe79677eb506a42b644609c7a44aa4ba921197398dc007f4de58f651d51fac3b198376915fbed65da58340807c908a227c0f39df2c29b1652dbeb1ee72dbe5e887e6d0daff1c84e1ae6521ad118077e997708f8acb6e3378b58f0c7a99410eb50e83f1ed287c7d8931149324d017edf5f6e20e9cefdd188a8092059fd871da84bb56001269f693986288118025207d2ec8f9fb8e5dab0158de8d7fa1605bd1e793bc432fada6cc2e37fa87687ac4f7920c5459f602c2a4390d8f11810090d4ae30ee0d9f1779cfb24fb852769d6eeea35ba21adb04addbdab839794bf118551ad5ac50f549ee19c4cd12955a7410f20e3b127ee4735eef94365e3e7da8e6753ab0bec81072834493db5493e80deb89d0f0c360f99d471d6191bab41a38b12356f08339ef44d954e199d182153dc1db96d536b3573500e46271479c45ece9024745d80005926b6824de16e3bc3bd +M = 16e4373b05f44fcaefcd6fe1b9ab991f17ba3ae9de053fc944d4679cc7bcdbb9e2c1bc0ea6f4e69185adcd1b0119aeb613b8ebcf03be4099467ca01e7dfc97f6268f67e220efcb2a41c62329b49105001e26f348f46cf0df3cfc1736a30cc2d8126963cade2b822617151d72409481e18692f4631e83ebd1bca193c886e400291f8d687495c9e08b2e85fcba0cf62f3b01e421e7a91dee6bb0478ff2c858f2be543bc1288bbfa5a18261264a1381f17eb1af2d0c12a24d314d77f10cb21fc626002400b85cafc2543369cefebaed0446b4ca3f4c550c883fe13da9b6006a3930c934c689e97b2cd7a46c98e0fdbd2327077739ac2575bbf047b3f694e5438f5adfbc8a460248caeb366c6507cf672dac5572b4c6bf7a570742047e6298a678a313d92f029f11b7ffd1a948e65bc964ce0c25db73edd8eda2d314797bb97e74876d395935c8961ee37ddbc03d6ea6a3da12647fbf5bacae3520468a9327f2ee73664eb06e342a33a71ca3a244d5bf99d709cccb498f24a2a322d912dbdfad64fd17789d7ba371fa0406911cf17834324255d2de935fda75b4a79f9f9a9b56b7346db8141be12b85b7dc59f787fe63cc0912a7583488ce1ab3d9117bd102ed8d58d7b5bedc2f6804f9153ca562def4574cef2d8e539e15222e1afcc708a0469ad008a3423c5bd304a94f93c0cae03a01d2a63efd4311a73abab6aad3b9842465f4fec5e8f43824cd709753c936f947fa31095c4a4c1ece4aa74f71df950de7dbe551d731b6a986ec7494e2ad6dc042b68427a6c5d9be6f2d98c803ed591a4ac68e +I = 0be74ec2cd6dd88912083e4c26dee5305d4898bf177ef186c9181cd8c6249cf8975d33254f8cd3c562674de68e312ce711a5824117e10a736f0805d2a81607d1e383a44171e081eb0620d807447af386571a4e38dbc62f218e08772c62a163d604f47b1d48c5846c02021674c1688fd509c9b919e5a5aae278d564177420c71448ad2f6acc0024d1f280fd24d87d6a4307b90c0c27123019f84a1d2f15d5e64c6bafb8244a91db99f8fb84a6fdd0e94d96261022f86d5b7ad0e345e2db44edf900bd84092bb23b511cb0673e92fbbe807f2cde51d9383e1db30ebad582f7a9c06fcd3e050650ed11c0abdb5534e2796e0b313d38a149ba00d62611eae3e33d981e2a4cc7a4eb390d318ed9fba0152b0255b7a244a504a5ace750e81f7a515b020edd558d1258ea8c38f3186c8e7b0a5d5cb4dc9ae899a4b1e7ec2b5bde6e38555d4a15f9e7e898b3e2dd287a8740f01b0c4aeff06117ff90c01c0656e17d6dba9839a0628028a8a78df7573670aa48135118d6b49e841dcb65f45671cb9ac46803fa5633052e5571fb9fb108bf469fba215b7d0184ad62f25b50177da419a1d53caf1b380535886a2b41b4f8bc43f04913f4ea5e99d39d25c3fd6cd39aaeb108bc1d15b548a95de36982ea39005235d79ba18d48b81e96b9550c0dba9750360502f245fbddc3d445faec5eae2882322ca15582ae7eacf549a980a4816749c1c89eda388b4610f62cad53b4d2114e0d400fc512d349fbada7716ba9b686323ba4ffa8ed674ab8c11355aec4a060cd9526f45760b766275196c582d5c627649585 + +X = 0e698fa3c92a37a3e15aec9f2101e81a57d4e783c5782af1b526c61e48f5a04addc116fc881746cfc1890d6825993a070fe0db17d4b6a11345e5d5d7bfd35f50524d69863026e2b467c506829de04e3d1c2deaeb85fd1a0f2b77ae626c1701450a0f8a67f5d6ac2dc09f212311cde14ef799cda4a28fee2e1e3b9202428ee0d3e09c9542e7d67783bb25c83a6d772468082367b2a0a8c252deadf25c8d57f68354c34464a7a96fac012907a952f5b056b67adfecb92ec7b12aba6c185405ef12181de34e924c83923d51ccb6ba5746e71d41cb895101d691779e21f5e376a05d73e6f3e2c1122ad69c009d1fd7b416c00bd153eb5600fadb0136aae373f3349ceec4d78d9e4482d5a3e4e597b6cdb16ba9c00a8ed50b612c1a9ef529dd3d03400626fa808598b46b8830a1947239cbed5aec0337a54d70f7263e88421fe0005c1c2da3d3fe16d4184b28eda9ddef08c40a7186b705a242ddeaaf916e89ead091bbc9567598f93b7710b2dde32264faa3ceb0539e252247b20b5b5a4d2f8478cc088cda55865bca2daf0350e0e09659819c1fe0357f8671946b74a2aa1ba6d7e8a0136b236905329c1cf1eec49d90533514646c6f6ac5777723e307efbd5a40e2194c920c8fa56f611b001510f04a820e547f016c13fb50c3cab8309aec9106d9179025aee10c5bfcd4903e213dd70043f542d29d2d7cadde0db02caa7a7402ffc361a092c709a14617c1818b6dc9e44b19976bb0e191d763a0f8db1c538228e37cba484f78a14f93cea949a68b53ae4d3bd35d3e14c207628d95c3c1ea0a8d42 +Y = 063987084fad687d791709755e5fa3039654d947b2511433c0a0922d5e671940c27a4f48da1ba790de7b66338a4442840030c16cf5f44aaf382beb9a1da306066635ce27e4c74623c58f7284bb037239221400d81a029a09d50b9c7909c2df3c19fe3c9d5a1b179d732f01ff5ef25b3f45b4b74488200008a7e3eafd72c1aedfa8d318554bad4e676061735f8fa63cb309b5d75bb79476e3182c2de602bbff340e4fbc80c0c4a0e183d9985de39bc9f3850f5bd70c44c31471b4d15c295fc185179cfb794a68321af5ff24d42995f92c99bd6011d4668124da6d1aca765459f1abbf358500ab423ca47879794582969a1865dd13c4df39f63ad03f5ffda8815fe8b453323d97469842c5cbe6598afd37f1948b0dd5a68cc081cb4cc43fd1624f139a3cab648ece683e0638ad4760dbbba8b73b8939dcb28bafa0c6578a21595614e8c6017ba79a44ce1e0f149f0ee68309a5a93fb169300e1339d0d4dc4e92adf0760a8a4d071bed2ff12f954153be5d85e3caba957ce8d2be1f80c74875245301c4a3895524edd4b44fa31fe5499f5077b7b896aa1c29628d03b9806d565671c798dc39d9f7e3b75a37ec37ecda105e0498368447a1f32c4d7ef6602fc412f44453f1050d59013cd4285a6c3d59cf097cf863214a098406e10031d21bd28a180bd831f414519de1ca3bff040dee69ea97091d77637eaed04af1f2b34cdc7ae5b586cf76a73505f7e81e5ff572cf2dab040aa026327f46d048f3006382bd0dae662806a352990f852a02f03534a5edab40608dbdf65381f663b6abcc99da000f +M = 0f77c1d15fca6956040317b5c395a8956963d03269a5721402f19e3198a035cdb64bc9688fb8ef516ded8fb700583cce13700b0f6afe213a211bf53043f7539415943d3183859ba6550e54527e9bcb3d5ca75d6b2c4ab01acd20627e9adbb2fc0d3805f2197961e796a09717cbfb797a9ac84b36a6dbd1bfd1737bdf98b67899ce60bb3daac3fe01ff0020f14070bcb509bf0775c1173df180981361f55de0f7945e86df75cf831b575f7cda4f4a44307290af5960c2e8a536e556f01d60cdb006bfaca7f31343acf80bd8abe7873e25926e89d811301b3fdea114796ece22e14b914f4bd0844f5838e315c6d98a9ce60a4de39419ee4efe68ce52e1e4fcd3cf5bfaad67af6184ff7e42752d6ae7b54bba1fe99dc10330ce49b5cb83427cbde202556d03f1d093e5869f0c327bbfb3321e9d096e1b613cf703642ad953007bd2ca701800513d4234af4cbd6ceda69ced13c11ab9166b364003a22e82eab847b037ebdfda007c406d25a849e731fa9c11da58bc01b9fa05d869ac211d194f91fa01ed606147e82e882ed00ec0140cfe4c869f9774884657043b7a4c8188cd70b84e2a2c73a04cbbc8ecd3b4719ebdc8e216dddbdfa35e83a6cd788244e2379b36e0248d6670312a660452b9e25b087401044acf6e220a93121e715613b2d9315d18dcb02b3f68791c2b48673512991684a78ce2840a7336a08e4b68bf6bb7b5f4a32c2a24b512d74c581ec6265bbbcad60cb6f0db123c5b034971b2d905c11e632f724007621f18a82f1add142685d5835343ba6dd05c8795c61df920b731d5d4 +I = 15e21fe7fad2eeeb673b64015906d0bd594851bba970476e3cf3fbbb73f925ec5f86492462da6755b863fe2c7368131c0148a3afe3e3c4a4eee64c86aa1f99d3c53a232e60102b270f26dbdd625d6e78ec3da3c1e027bf47bf957ae7393c35751007138ad53af83d1b22b4646739a7bd5af922a209b4a42b2ad6299f4a97f6c7d808317580d6bd8dba7544c42c2de4ff0b150db49b4c55131963e9a553e91415c5bc347f41b49545805c58da5972ec40df5d6812caf9f797e2ecdf75b1642c2e10fd396bde57a5f5f5b61c61fb5ae1c7bf21c45ff12275787ba476ba5fe897d500d1315d8cf832eac34456cce72b12350b31624eab6d2432176d92a103e66a9030e574f422b4bd8af3e48837ddc79b1a22cf5c7a0a038892e7b23592411822ce0087b7e60cf96e8feff77948096b71ab2fe52686b8e3685ca852eb9897e695b1dcf0bad6672b3cd258c431804f618dda071315e6459f017c65e03dd76d57ea3484d6c1c58b832479cc22f005de400f6e6841187e1e6cde85c34541a0f5bb410007c52f491f71e28f2f8eab658058d895a7de180c2e55ae91ca619cd453bfab5401688cf40d70e564d91542d93e74bc44028270195befe177b8440093405c9bf13ccde1a3681cad772314b3902faa0105d411974ccfa5fea14393fc2eab056e2304a562be68d9eb68c0c0fef5bd5679fe3538a9321e7a62381c7e8c0e9d5cec227b3cdadff83aedcbd47cf017cdc5471f16e4f78b98df3956be0a770fcf22ece2b86bdd4023cba5d9f347d3f4f98c2c34fa6b377dda0e6ce492cfc4e8bf872473 + +X = 0aab495c5f9d963349e725ff9fff41745e8666ea1f2e4743c06cc0054715d4eecb15517bc3f0745bece769d75fedad020093382b78abb586ecfe7457a22034019f841805735dfb1d77e201d1eacb47062c385a48e2588713ec34062eecc48a84011e0d1f9d435f48c42d2231bd40c718bcfda2c3ade1e743916796630563ab02dd2f0ce6bb85ebeba9889959618871580ccb7195fd9b6835bd82ea7e0bf4dbd8bed30d481338a71f2d7b0d5c4ec00d9942cc88f861df0a57d4e84028afbbef3a19bfd6f393a9370018b3451e8331b8ec8d6b2c821069e4dbd97c5b8b00f3b37266fad1ccd3e2061fbb8c142e7d498c32181e824f9d9c7763aecf548c9268574258b76a65cc86335c8011b013976097d5e564c8092eb3b7d6699d0b42410d737c0260bcd12fbf8b1f18416db46275c06a926045fd79510078e29cd9bee20d08c0606bdd561205dbd9af755c8864af1ca60fafaf20ab27ba0e2bedb08cbc3e1d1685f3cd32c1807b1f5be60568f29b5a987f15b5868d3d44ac708d2d39ca135ab210bc05eb2c84a0abf8f702a881e7c095da4298f2cce182bce2e8a2926929fd1e4252c72c590699d41484409b6696390f0261a1e11bd5daa788061d15d0d08761b1b6aeb2bae5e94903cf8e9076d5d3b681d4521d8208b5889240881acda801a502f9a8873ec15998b25fba7a19e9f7edeb6aff149dd57bd037bc0d15ad3b20fb08c39303697ac2a03e9f4c9f7ce34f8706728b10bc92f6d1e8c3cd8dca0b7b624254ebcd2d325cbefdbd1029cd4f61cf51fcd1b739df0353438492aa3f7fa265 +Y = 0108b87e6090ecc8ec0ac05b7267887abe3052c4fe65d2ae5b1793322ee1278ce4c373c55db9ef8a3bc98c77c1aa3cdc145ee821a8f8488bc2a30d1b1c2daf18b35446ce6ed1ceccd714919e0c48892e8497079863c7a16c447285375f4f60f20fa52fd2a724bf398eaf03cf3d377fd4d23ca3be64020a42fe25532fa189ba6da538f217cd4775329ca25439e9ca7da50c80da6d256ae2f1267530de1101dc21a8f69616bacfc7b6d6f3228d73ff45dcf521c36c2cf92a285ea8fb1aa405fa3809ec793af97d2c89895c48c8214623364e337c7ac367c5fe5820f762f512bf016590c36de5f9e81d16f24079b21e4184006bd2f64165019b8e07d67d914179f8397a66b0e52c1ca670147e32c07d986392294e6c28b5ad2984c1ad3cc668b2e907faf5721d686091a3a709944996af295b4d126f7f1c0f28d8069ef222c2d8b5a1f531c4712ed077974114e5e08e41af07fc2f7a360b4ecc48522658f5ac96e6ef8d04258df443adeb818ebb738e1d70981c77beb8472f60890cd1bf7e5918cd0ff0d2e028b99869e4cda2eb4a9c848c78b45a5964b5e5d2523247458e193f6c5c49aca46d8349681c618db6b5b6b0a5196cffd4a52c834b1125148589312c85ccbc7b58c7651da63823028ede5817a45fac9969acd83d8d172fd6533701720407ddf44829ab4c16bfddb47fca41255f7f18e01180345fa570694302fe823e64beb20052ce5ad46042821ae12489eceb15446055bef8c359d1c58553db9d39386df394def0c7bd7938c95863f104e57da49540f420ceb513eccbf9b93c91235c +M = 12b189b71e4f1c3cbc2a3ef355293511faf56de9277d94bbfcb4e8a42f69a8fe7a2a53d786d7208b73f0b00c34f1e2b41895f8464ac40ebd203fe4d88fe869e2e5d0a1f27be0474ad323a9ac79f7b9070c285d557803c047aae6c956eb3047bd0347c0c824cbe232cc0f81cae78453e27003c4d06288cc4884ea76737b8fdf4b5e9fe140cf0b58d946c8ba790597c4d4119f5eefb6163b01a9759bab32b8a8d86708156d2d2280669d31810163544e2490ff4cd4837893998cdc9425c66f1d2414d4e494f035f451c891cb4a6224be7d66029c5bfd789fe3b5a3dfcd164c1af6d2b5921876a598b61fee84f5f9098c0a07be760e11ef8292db29122f2534c81c97249c037a026c6c209084da81d3c27ffc310ce16a12c9fb206c72fe48b88a3205a039fd6879ad2a3f9aff87d737f910d5b89fb6f8ee4e0920b8a90b45074ba18a9411b1d76d661093aba803a1b83cfd07a6e6fe44a9182e484d7f6a8c089459a665083805c62c846157477a1a1fdfeb89422f6b43361304d32149d2cf52ae4f0f1c4090636c6ce1706433a06e7ce6308d925e5817742fc07a6ed8768502409267e38832ad10091c90e9e4edda0bb394052353f5401bed184eecf1c6e56fca6c99a3b9735d3d422e025f4aad9e2f89913ec5f23b841579dec41d5dfb0ea43e921127350dfde6fe2d94e4b0170dc5db3f7d5fdeaeb774a34667044a38f05ddfdfb51b24e8296a6662e3743755eff1a54607798e24f277bf3896993923eec75087dd1f4dd38e3c24b494cb4fa06b297a0ba928c7d8144c9f2e6e185b7ef53ba2b8 +I = 18135044309a93ae00329b8356af3b08439bf7a616054279dc68b976ec925cd78769254788caabd967957bc5cd39e7c60f6cd09163ba59f5c7bf16657b0fc67f74bcfcc40095c3b17ea819d1cc62208f4a883aac74445db69035dfc4f2afd487056c66828c40d0d74b29f1a854b777fe238ba0a3f8a131843f56e59827baba9d2b93dad55f68110f9b5b386de50692f7153b244aadb5e88f6ff0978e79dcf40341f0498487d1fdf0a74313094d03b3b5a440cf543e73654f7e7cae4e32887f3d0ac34532ab382bc3109b444b51f7a1b90e449c644709f66fe3903edd298847d415d0d7f119f3b48d6c729c3b2b397f5a00b78cc9d246dff46ad9fae801bc46a24389351fbd4618743ef418ad9a3b53468d77fa1f5a3630817f5a77e2d814b5ba11de6e15474392766c534d056ff4cec2d6f9a42f4c924315eee99fd244f223cfe750bec6a08aa2e00858c280c6c114b6034436b388a684cd0090cc64d378adc6dbe6caf62d77efe7d82ce03ef43c333ffd3ddf0287e35c0c9bd2df165bf3e7d605fdab950139c15b671c6a9634d4f914299eb06ae35c534ad98d8f0b4698dd587685717d85444f7cdbf2de7720d56ab7015c910b954798ffeda71b5ecee0d64b47d4d88d9cefca510b71d01f79678a966dd9926d54e3cd58fc7290c78cee15af029798e302f171a5e164501e2bfa8fd08b81a83598304b7993466c4dc6b49cf706e0cd23140babb8c098a22ff5a585ea0d7cc379320af252cda75b01c911275a5291db038c9f3dc382527e56820f26ba8517594157d183d4e0db5f8f1793ccb4 + diff --git a/src/tests/data/bls12_381/fp2_arith.vec b/src/tests/data/bls12_381/fp2_arith.vec new file mode 100644 index 00000000000..2b8f0335a2f --- /dev/null +++ b/src/tests/data/bls12_381/fp2_arith.vec @@ -0,0 +1,227 @@ +# Arithmetic tests for the field Fp2, X and Y inputs and expected results +# A = X+Y, S = X-Y, M = X*Y, I = 1/X + +X = 022e434c1e96ebe73ada5d7a0a64c76e21d4ed2132f3435caa0c6caf277d24c717fc23e5f4791037c2d89cbdaf0c04eb17e36f8f991ed1280f913a4bd8f59ecf1db75bf3aa5f50f642db58ff16d3c0f96cd8ea559810bbec4472756a7db88684 +Y = 1769e4644d31217296c1413e375d1ed7a896e54a10badb4c4609972568ce1cb138fd7bd518d6ba7a45b627492976d8f20b8e5d971c5de9657254e0b0ce25b4e866864713585ab64f8a30a1a5eee77159ac4475969e2292adb7017d826268b993 +A = 199827b06bc80d59d19b9eb841c1e645ca6bd26b43ae1ea8f01603d4904b417850f99fbb0d4fcab2088ec406d882dddd0970bb3c7bfcd3f336ca734663cfa6e01fc657820f34f48665db28040f0a3c2efa715fed84df4e9a4174f2ece021956c +S = 04c570d20ae5b10eef34c3f21653556dddb5535c15bd7acfcb33a82ab55ffe39fdaaa80f8cf655bd372175748594d6a40c5511f87cc0e7c29d3c599b0acfe9e6b73114e052049aa6b8aab75927ec4f9fc09474bef9ee293e8d70f7e81b4fccf1 +M = 129adeb55c04b6dd2c85b9dec7836bc839c2c5542c320798c7928dcfb718c57bdb2712c3defb748eedcef2076fea60550bcc7d245774324d17d08964fb50f95dc6649ada5d09b504f659e97822351bf4d634b5152a4703751f5ebff94412f430 +I = 08aa4ac3d6e55f17fc125ed68f7f95b2ede033fbf6e47865e560114e84ac875d7fc8178c2c8f46142c5a276a372d38bb11eacb34f62293db7b5c153db5757c527d4e9be0970edd78d565bddda7372612f66757c28f007f9f7c5c68bb32d29956 + +X = 059b4417ad2c786d2c5aa189111abbcea6ef0ffd40ca8b9ef1204babaca954c842b3302045148be2d11ee555b8130a3d01dc2b0ad3b19c9370410f061179377aa76c0f4565651159cd8cb22352598f855d263b4707f73b1a971d0ff3f1d3e600 +Y = 098a7c90e603658f4126ea41955641393caecac95c54e4a58436b713a7e120156a8e2b513351d364ded07ebdb68115360b14b228e408a2d06122454e5d94813e252d89dd172f8f50a149dbbb276471cf1dc87bb3b0e38180fb966f2eaaae5f53 +A = 0f25c0a8932fddfc6d818bcaa670fd07e39ddac69d1f7044755702bf548a74ddad415b7178665f47afef64136e941f730cf0dd33b7ba3f63d16354546f0db8b8cc9999227c94a0aa6ed68dde79be01547aeeb6fab8dabc9b92b37f229c824553 +S = 1611d97100a8f978364f5efdbf10276cceb790b8d7fab9b8d41a6738fb792ad6f6d104cdc316b87dac4d669801919fb210c88acc2928e05d5a3a716df7306313e6b5d0ed41ba94c89373a90921a613da5e09bf920867b9995585a0c547253158 +M = 0992d7a0d1f36471f781c82815212f567058e9ef00d9e917ec0648c7b084aca53df8a291054f5bf3670b418e679c604e11c2e0c5acc7fc7df83c9832574ec51a70de7c25fa7dd4554b5eca2130239908179a8443304a828678858dec98b0d18c +I = 047d602d2cba740a0ee62b7df46a4230283e1fa9bc961ee6553c73365eb4bbf153c4d9ef2931b5db5fb9cd77083c694612096c26552bf8e11c32ee9b7d2664f8e54eca7f30671fdae4b05c9e7491e096a3cc407f0942e255d4ff02e094b8736d + +X = 16ca25085a97f1721f324330e08887ea3723c848ba3038aee7c0526cc2ce94a0949ede93094dc61b7384b7ecefd442860e053e87beb267823b2c8416024f82d701be2317b5ee26457aa21c3b1db4ff0c8ae4c818303e3600440150920a5ff8e7 +Y = 13b374d2aa3a91a748b4baad835d0b2b1bb5867837c24269bb5b12322f17c121da7c538bf45eec84e325a1ac718666bd14d6338f51e8c83a16b287f9ac243325cb04d1060d4a56f83e9353002233da8cf997e9cba65278decfb49c0af1f30000 +A = 107c87f0cb529c7f1ccb56282099e63dee62033bfe6d68593bea91fdfb355f9e506f32204c58b2a09cab5999615afe9808da602cd71b492206c364596b280925684ba898cfb36a7e52049c9a4937e37565d0b1e5253caedf59b6ec9cfc534e3c +S = 0316b035b05d5fcad67d88835d2b7cbf1b6e41d0826df6452c65403a93b6d37eba228b0714eed996905f16407e4ddbc913301ce2a64985e26f95a3d29976fc889b309d969c28e20ca33f9bdbf2321aa3aff8de4b3b3fbd212e4bb487186ca392 +M = 14064eaae2393d0e67dff4df86a785c81034853963d815fa8cb7e0bffb80bcb3ca27f548a979bb3e91647aec712ffc14101abd68412fb2545686b02de0170529627b783354c99eca10371d0a69a9523a5751f7f65c139f2b2e596a2a6a72e880 +I = 06a29ab3019fe19573d22c64d2ec83a708be252be410d3d316430253825cf54423ca86da648420fd8d635b7e7ce66768180c7f990dd8fc7b2fe4bf7cc75c62a40e8b8dae7e28d239033ea0720be6744784f176428efd0a124a92b482d6f6c94e + +X = 082c22d518690a642da1abb325faa1629aa982652adfec4a2f44d7e00fc60096847fbf037fa3371d862e864b61378515117b640a9e55afd6beca5e63db31f8efc8ca3ae126df1240adda029af0d43363e995a51f9ed7dfcd45b1fef746750f87 +Y = 1900b799564760e7c49082ca597db7bb0e29c84b1cd4e9ad7229903019f36bf4ae391eeceb85c2fdfe63c1ae19137f8e0d013bd6b1613c6ede87a42dfb5378b44ba81b9b6613c8a67475a05ae64d5ae0fbfada2c1d30b5017250ee96a816e60d +A = 072bc884353084b1a71686c73c2cac46445bff2b542fc3383a3d956f33087667140cddf1b9d4fa1bca9347f97a4b59f8047b8df7163705ab52365adb9339c4ccaffb0af7996dc827bb1ed054e0709820c6e47f4d0ab494cefe03ed8dee8c4ae9 +S = 092c7d25fba19016b42cd09f0fc8967ef0f7059f0190155c244c1a50ec838ac5f4f2a0154571741f41c9c49d4823b032047a2833ecf47367e042ba35dfde803b7d221f45c0cb499a396462400a86d882ed9acaf381a72acbd36110609e5e297a +M = 01ad5e495edc9c68ee5112be57668f86ac4ea345b97241892cdd41e58869ce347c06eb06133773515cba6d583b55434d093e54d8dd48816da280b06a1bb521e2da2f2cf3de4354bce813a4755235191abfdbf1da94d580d806621ca74aa9af7e +I = 0d0cd6ce0c4beea34b9aff159d6c07ebb2cb966699904f3f3db4e0b4f22c14e390a77bb87b5573407052dd24b01074dd09afab03a949efbed7cc1481876f40e8cca7bc8cfc3e6558dc411a7c36d8c6d9a15623a7f9e42430ee17c755592215c7 + +X = 01a7747322556174d0ee7202bbe4da8a93358a848f2df74d2e3b0bceca5fe51fb86b14ac6f6ecfa3993718d420aef53016b9bf4f52749be0a9652784a02cd170da23ab5886d6c2be625b5292c8dbec603bdd5f2fad145595c8eed1cb35447ba1 +Y = 03f2d9c1f5164f095fdc22cbaaf1e08a1372a2484bf467f05b46cb98ec2ea858c09e98c29a11bd7e77096fd5a26543cd171aa62a63c1eb02eef175a754e87c89b41cba9731f2437de0211b13d733c06c990c23fc810e4258f633358676aeb9a9 +A = 059a4e35176bb07e30ca94ce66d6bb14a6a82cccdb225f3d8981d767b68e8d787909ad6f09808d22104088a9c31438fd13d3538f7cb6a0494d3af575b1c9a12329c91a6ac543f37cdb4b9b05a95eb6a8b63d832d7cce97ef05230751abf38a9f +S = 17b5ac9b66bef905bc2df6ed543ea6d7e43a33c136bea21c3a2512d6d4e232eb16787be886b11224dc2ca8fe7e495c0e19a02b0f28329778058f59938e9001be8a7e3c46486991ffe96b0a1fe8592217c17d3b31dd5a133c8cba9c44be956ca3 +M = 0e5fb2d13cd4cf8ac54993a8f4d368392f6f9b4fc12e609742f453e24df950c230b92f14939d18d49d98ed77015addbb06a882b236a9be6a22e81aeeae8692bc4ba5860208ba1a8827533f39f02f9ed4bb2f19fc431b48571c368e32b61a9005 +I = 02343249d4a81a3bf45d740e91d3c8df7bcb05beb9af2a3d86cc2461af84e818448296df679cb63e1324efcbfdd0b43f0e031cb66e9d4e4e7425a604954161c01e0c634c423bcbd77e0ee462053b0f59cfd88ec3976b08adff221d6c3b7bdf58 + +X = 09f9e05027ab9d0b90ef6b968a0dcc69030318b2d2a4ae8bbb5ca384ae6afd18ed7cb00b396cdf69a4570cceabb6debb02b57206e760340d312bf8f4ebb5a5dd03a301c6d75e441bea7e608136b31aa31306bc08db0f9ea6ff32f5d1ce43725d +Y = 0bb4cfb4409d01800f61694a2d9526f4cbfb9e51b5cded8b4ce57061bb8ec27edce37a9b407e173e50464062379314d905a93d02d7fafd2c499410d32ea1c03f5cefdb826a6f37fbfee9035d5a03f3ba9ab12e7f323ebb925e55d2ad52963184 +A = 15aeb00468489e8ba050d4e0b7a2f35dcefeb70488729c17084213e669f9bf97ca602aa679eaf6a7f49d4d30e349f394085eaf09bf5b31397ac009c81a57661c6092dd4941cd7c17e96763de90b70e5dadb7ea880d4e5a395d88c87f20d9a3e1 +S = 18462286208e8225cca9aa029fc4524b9b7ec5e6105bd3bfd5a805c3e98d30be2f45356eaa42c82b0e0fcc6c7423748d170d46ee48e51d7b32b38fd8005f92750b2a71c960741edf52c62fc4d3601d0c97018d885a24e3145adc23247baceb84 +M = 0f3a5745e1a39d5e4674e0cb0a7efd2bfdb9484b3a4d5e29662b18d8d94e7d32073eca212060a1f0a0335c7dca88027f0a330ab72a0e71030fa915edd5ed94645da8823980bebaad8e484f5fa376ca9fa2e91ef956bc554f6d1abf717aa2ecde +I = 177defd237f6e1fb2e40ff96aed530f1266993aba27fd1be3d74e7e007fc0a8fe4c1704ab52efd3c556d7c8d612bfc66138461375f5d18c95a723740c2d09462e96434ecbe7599641ca9b42bc4f6a9dbcadb3cfc1429531ee2a45cd99db162e5 + +X = 1330fa7c0a7a004ad1a1370203d5180cd9a224c7f398a50c1380315b5aba9716cf992ad661de80823753b26774294aec138e608f81c6fbf5254482170ea631b1526e6b487bf3ebd5542212ea79c876c0dbd560b914174b56c8ea2a27385699d2 +Y = 0db7f9f8c1253388217d6d8e99582ee9368f5f79358b507f7076931153d67defaa83d5c4fae57a07470623fa5a17d67411a1a59215467e72f628ff70015014c43aa962c327aca009040b2996cc3d7df80cee381b6c86453ae95eba616f4d44bc +A = 06e7e28a921f4d38a802fcda59e19a1eabba38bc359ee2cc1cc5f1cbb7e01ee25b71009cab6ffa89c45ad661ce4176b50b2ef4375d8d93cdd051d9d0ccaa999e28a08286b01b791ef0fc69e04f54fe94ca1798d5cf499091f849e488a7a433e3 +S = 057900834954ccc2b023c9736a7ce923a312c54ebe0d548ca3099e4a06e419272515551166f9067af04d8e6d1a11747801ecbafd6c807d822f1b82a70d561ced17c5088554474bcc5016e953ad8af8c8cee7289da791061bdf8b6fc5c9095516 +M = 17517719a926039f3ba672645e1f71d7561794bca7231e82acdfd2efb2b4e107d35de9c5689ecc4c1c9b0c660acfb21c033408caf1320249fa5dc09181a8d8334d6eabd0f37d2438e04704daef1f6c2f1a28cf8bbdc1e49f5e53bbb36f924eaa +I = 02b70691536b64a298f400887cb1298ce950ba458fe8cd94a5f4bfcb51f3cd91f1d1078d53ba93cc29df8b262f74fc1b0b1bf0fbeb082dbec39b1cac550f77110a910d8b141cefe8d7468b7649574499ef17cd54d3705dd9714cf96da27b1412 + +X = 1944a14c5a579fb0441037a0b1ab7a66bf36282f1b28e631757c43f4c37e8ab474e15404b795647f20a58020327c4c61043804fd2f35e6daef801ce58cd6c09f5e5014f98d994a587db50945ecc48db487a2b5f3f42b16a82cb7be12846e5b06 +Y = 03b407a16e4cd6f76fc03b87013de1e498de1e7c5cfe0e3324d7adfeb50216d2b96e15b1cf5e3f29b1619c570567568f07fa857b89bc0d5d515b93b1e20c2dcfa38ab0738f5142f390188de2190594870404806420837965f4282120c53ba752 +A = 02f797038f24900d68b4cb716f9daf73f39cfb2684a1e1a533231f5281cfab630fa369b7d59fa3a918081c7737e3f8450c328a78b8f1f43840dbb0976ee2ee6f01dac56d1cea8d4c0dcd972805ca223b8ba7365814ae900e20dfdf3349aa0258 +S = 159099aaec0ac8b8d44ffc19b06d9882265809b2be2ad7fe50a495f60e7c73e1bb733e52e83725556f43e3c92d14f5d2163e916bdef9c017e94030e9ee163fa71f3cb00af1cd1a2454cd4e04ca6fef51a24a358e84fb9d41f28e9cf1bf325e5f +M = 02d4b2739685a97ca42e5e3bb9c9ae297ad2748cb2397c4cf9a2a77f5dc1cb3fef1dd8beb493e5dab7b7a64306b88105042f52d299d46e1d45d8b08658505737677860b8e21af273623ba3d29deb27a3aa3fc1d35516f0ec35aae386ce425076 +I = 159c2433721b5fe2b15cfb3b97baa732dcc70b767d4e10a5412975e9f60a9a628e8189dc87049d5bdb7d3c57d872bee516561099ff132b206dbc94a72177bdf8ef3e434619d1202fca614932709d3abda9387c5a96db074574dc325ebd7214b5 + +X = 013ba1c05bbb5db47e0251a30d1fd51e6b5aaa42b16faae2bdf603337427d871965d7bed38fadf7ca5b15482a5a394440835d319fb00191fce0b9e1449ba2815f5a32ef56cfc8398d0a1ffcc007ba70707fa2f97512ba11990bd65e565936c9d +Y = 028c300d0398a73adfdcfad17c9b69ce9a230b891c880ef6a3b45d66fd4750662fc2f42cb3d617c795b28353be828c2e19f2f52d4c267dc146b31aa6dd8e5b3f317b96fa063de493d28789e63d95607b8745fce8dd5be4b293dc83efb43f2e79 +A = 03c7d1cd5f5404ef5ddf4c7489bb3eed057db5cbcdf7b9d961aa609a716f28d7c6207019ecd0f7443b63d7d6642620720827b65d0da6b046c9a31104e3fcd67dc2a77a6a7fb5556d3bf8b7114760115e70942c817d3385cc6a9ae9d519d2f06b +S = 18b0839d91a29d13e940fe87d3d0182735aeea3e886caeab8172786d6d917e2f854687bf3678c7b4c9fdd12ee720b2c10843efd6e85981f8d2742b23af7779ae289ee3805a43b1c4654b4886b9973caf9f6032ad2523bc66b6dfe1f5b153e8cf +M = 0de74b0fe04d177ed1d734720f5b5fb4337597990116fb9551283ec35eba46a33216db9572011dc8bc463d289aa3b2ff012023410f869c2d8b1230ab802c4b43df6136279c4afb571bb3b28fa367810837811f7f1b6d485fc6dbf6e81a6c865c +I = 13d704b067f91c2c7455eb0ef4904380457df40d82b0f5717e05db60ad69f9f4ebdf0a12c349519c0edeadaeb1910a5c11b0d51c1e998351ab1925678e2909d944b8bf1e42bf9224fba27e351ffba512f3f4dadcc9715424c5a5a9dfc2b0cded + +X = 0ccf918ee97aa5d5359f558a9c46e70a7f9a3c8d3f6d46eeba57e4a5259747e4e98160b43d682b6e6ae9ec4a3a6d8adc05dcb6e085e6b565271a69e9d694e45955da375a680fc567a1089c26b526a4548e80678bd735e06b4e27a04dc22209cd +Y = 16539722377c90fd27c107e8996793f5f84326bc7c1e432d34bb176b9636cf92c942312cc7d7f86b4e064cc9d1a70b6a11acac25c95ff114a3659cafb5cfb1ea61c614e59f63efa08c944feace95bf0169f647ca26c82f70a09357bd775d5ec4 +A = 092216c6e77750381244b5bcf262ce29136617c4c806775c87e2296fc51d2153941791e253ec23d9fef139140c14eb9b178963064f46a679ca8006998c649643b7a04c400773b5082d9cec1183bc6355f876af55fdfe0fdbeebaf80b397f6891 +S = 107d0c56eb7dfb7258f9f558462affebebce6155b6d41680eccd9fda86116e763eeb2f8626e43302d6e29f8068c62a1d0e311ca4f606aaeaced074f06410df46588b6df9bc30e8867ba51edcdd41db7743361fc061c1b0fa679348904ac455b4 +M = 170173759c17fb61d354cdc67f44cc63dfe2dbc53a39eac37d189bd869fa8c49e45e472e1e257dcd8b5d3504c7eb0ad606b3e853ddd1dd518a8f79de629e2fc1d6aee9593eb74b85d4463428e18645cbacd768b5e24341d9a6a5367be1266e27 +I = 0d331653143a1b491441f2f2eecc4c9e12526b4b14e2b1bfd3d8cbad68e623d548873966a7340ef6870113f17002d7681435de261002c8b83d51ef086f720966936cb86c945bd0aa41606c2aa3697f0085cecfbd0791bfcc2fbb7cc31a5e361d + +X = 0d796e3d5a25f5e8ce7f0a1697c275ba91094efff0c8f72e7c4577378a554c810e89c6e99ca006350b0ff2b15ef3c1760757b2eef51d83fb0e8be7fde2b28b37f3219421d3c601463073937086d5cff85bf22c15d59eb1430aac598a039fe532 +Y = 01cc4928494e316fe0038829c5b10c5bdba0d425bfc78fc86dc74cd5f56998403966b5cf47a62c49865c4eb4c287a11613408d95cdba2d2e11b7a8707714df6197e30cd8f595dac38a9edb7cd57b5ce7aafdf7ae58e22d69d8945ecaf525478d +A = 0f45b765a3742758ae8292405d7382166caa2325b09086f6ea0cc40d7fbee4c147f07cb8e446327e916c4166217b628c00972e9a8957ca8ed527e8b8167bbdc2268d5575d5d6c94a53e19c4c65a036bbe84423c57d2cdead2941b854f8c58214 +S = 0bad251510d7c478ee7b81ecd211695eb5687ada310167660e7e2a6194ebb440d523111a54f9d9eb84b3a3fc9c6c20600e18374360e33d6747efe743aee958adbfb5d2cdd1b539420d058a94a80b6934cfa034662e1083d8ec16fabf0e7a4850 +M = 00ad5787a7bc158e94987ea3893ab48741ea3e46ba4c184cfe4476cf465b0cd5b641bbe75e61c644f0ec5614834a4db302af0ce7df50b912d27361599184e705f5962c714557429faf7260c00dc677b2c55724d90208014d47bd0cd27668ceaa +I = 0ded22c492f3faaf9faabdbc0ccff5780e96ad4c61a9af9f2fc8b4693ecba88ef05bbd5a43ed364b701d2e18fb0f552509455dadc8a026482030f5f9b0b2053b88ae949d36e2ef542a260d6f3508f1eac8bd1227716cca8b72604978517e0d37 + +X = 0778c5a68238952cdd1aeec781d872e2acffb64d474714c73fcf70b5fc5cef47afe8b3591e126bed5dfb574419e29a640d40efc2c8eddc3aa36d2e75e4169c1b3e6b8289affd2b4b34000052210ae2f73f8af15ec3f38a646914d77ce96f37c5 +Y = 0cedade6e6e2f66a47e335635d3a2e7b091fe63703150e413e4e929f2931be27d5c772e3a0a1d10c484e9a5e0e7ffec306c6354c48dceda42461b2b5c35224f4f23d350a082284ee026183c763557ba26a9aaba1ea1a79964184db8a5b7a18e6 +A = 1466738d691b8b9724fe242adf12a15db61f9c844a5c23087e1e0355258ead6f85b0263cbeb43cf9a649f1a2286299271407250f11cac9dec7cee12ba768c11030a8b793b81fb0393661841984605e99aa259d00ae0e03faaa99b30744e950ab +S = 148c29a9d4d5855ce053611a67e9f13f08571b9b37b7194568b1b0b7c9dc2743f8cd40742ec49ae0cfabbce60b62464c067aba768010ee967f0b7bc020c477264c2e4d7fa7daa65d319e7c8abdb56754d4f045bcd9d910ce278ffbf28df51edf +M = 0583d747c0ff898cde7ee56f9c0dc3e267670154a9f6184a21a4f89d3c8ead49a963eeb22f9b413587259fb6dabab40118193f65fcb8f28c274e09e9da3656f057a3613ad44a9c683de958a83772706d8dfafa36607ee76a357d769cd16c1031 +I = 12b7b80acbca0a142b92accde80b02589d6b56efbdbf56b86deaf799177f7fd7707f1e98d8607510d63918d8d098dfde0bf5a599c0d08b34d1ce38942bf7b0bc8708ea04d56c0026f7cd6f30fa4e8124ade9632c2e57819a51821eab47bfb5c2 + +X = 15691d5507d513e7b65a3320b7d63a29559b85b9ccff1020c83abe6d498aad7b5f9e8975d82436409b53fc12838e190c0ddeffbeed8169cbdb1d4239734d05b2456932af49c0af5b5f2c2e04800419187be3c1227ee82b66466364cc5a5c512e +Y = 13e096302ea7df056ca6a8ad800ceb476d06c8738d09de6a9857e8b3d64b1c556adf9bbf493d3125693ef7ca5b427a3e14864ea3a706e5863da2698ede8f2c1180b2632cf77131ea96a4f5b8ca1545f02d981230b46cc86eff821be95abfe376 +A = 0f48a19afcfd0c52d7e53417f49778995e2b02a86683dbcbf961d4802924d3acabd22536700d67664a93f3dcded0e89f08643c785b0868b7cda404120e9084ec61a44a574dacce868ea0511c536868e48acfd3548200f3d58be680b5b51c89f9 +S = 01888724d92d34e249b38a7337c94ee1e894bd463ff531b62fe2d5b9733f9125f4beedb68ee7051b32150448284b9ece1359c3057ffa6adfe8968060d8098678292e1b0745d490302fb80aecac9fc94c6cf7aef07bcf62f700e048e2ff9c1863 +M = 04a57fbf2834cded05d9347032b7c3f7b58ec568884bde394505a2755f5b650940e4a09446aca2aa806383a5eb60f6bc16d2ae7594100884ddc506858dde4e75798dd07a6651ea35eb7a8080ae29703b3c7286f35a4882861e4f7a5807238f80 +I = 0926ba4ba44e0ba69a3ce4b704b1e7e8d032a0e4c7ee160126d89ca32dc74a8fac3ec3ed2a12336002515640edf2c4f713a7b9921b73af7b041acb55f3c46ff1d49a17c96493439e00b2615913a63b6389881433d0e82274970add89d84a8cce + +X = 050b960ddedb11627bd7880e8ad131a8a6819f0de39dfdab61690ac75fb2a4eef5bdce26ce8339f565a40241947578b906900df8c405a96a46b8d94dbe10eeb919d845ce30f5c1101f65e15425df31b7850e3894a185e582a952dedd872db215 +Y = 09c62b1e3950aa33a42c2072de880fd277170e8e8d8fee1df6ea0dbe8a8c6a08597be778ab513fba806fd3e2ce993f1a000f9b5e09f930ea6420f3345c69d616bb122f675101164f1d429a61bbe21dcef2875cbca81d3932115b7c9c652971f4 +A = 0ed1c12c182bbb962003a8816959417b1d98ad9c712debc958531885ea3f0ef74f39b59f79d479afe613d624630eb7d3069fa956cdfeda54aad9cc821a7ac4cfd4ea753581f6d75f3ca87bb5e1c14f867795955149a31eb4baae5b79ec572409 +S = 15467cd9df0a4dc922c70f51ef94cead93e1dc044993224cd1afcfa9cbd7310abaede6acd485fa3a9f332e5ec5dbe44a0680729aba0c787fe297e61961a718a25ec61666dff4aac1022346f269fd13e89286dbd7f968ac5097f7624122044021 +M = 10c3ce94ee9c6c559e5b9fc18eeab339a3bbb9fd8aed350f6ac36313db0df2964ebe6f3e77fe63f272240ed94e85118905eb7fccb156d3af133034374befd0aa8ca12b5d4c3b2d2eeacc0479d84010ecb5fc0cae84704346c300b2825e9c9d26 +I = 01eb7ff3a6b5c215068c0ba4965b7d011d0f591b8295f2a30cf200f72304779b4eabaa23c765e39f8299e5a796917aab1632cc2b5b68f2d219a1bbaf39d865f754b18b9ae92fffd24d22a090205d5802591e91064b5fb3a03e1781b057fd3fc8 + +X = 172b36ef7fa82fe0dcd2a820ddc56acf98485a585ea2afe82c4eee9fb8ba09fd80f09c5a3ac1722ddd4b534a38b533480e3c6e442a55d75d19fd0db066d6fe89acb9adbc0f56f4012fe7ecd75422b92f5a8c97205bf618638d735d1de91df261 +Y = 0d9c88dd929b759bb9d060e672f2cd2e3abaa27319295792b26c6b2c78409de14111b48447cc8a0b6bd49c8b915be07109407b617d803e8a08567acbcfebcededd9036204b1cbbdd09a5f202975edae0766f0bfcdb193983cc414e7e0517079e +A = 0ac6ade2d8c3bee24b8761510d6c8b266e8bb1468446f4bb778a872b3a49b1baa35650dfd139fc398f20efd5ca11690e177ce9a5a7d615e72253887c36c2cd688a49e3dc5a73afde398dded9eb81940fd0fba31d370f51e759b4ab9bee34f9ff +S = 098eae11ed0cba452302473a6ad29da15d8db7e54579585579e2837340796c1c3fdee7d5f2f4e8227176b6bea75952d704fbf2e2acd598d311a692e496eb2faacf29779bc43a38242641fad4bcc3de4ee41d8b2380dcdedfc1320e9fe406eac3 +M = 19650cf85fd0ab4e2f1b5e0ad69fe784111f693ac3f46a150b5e9d89c40852cc2499ef7cc35af93ffbadb81a91e7613818f4227911580160ad6126843f7d2b6956d96e945cd56133c1c99359c0469b3b17265445e55370d22d3b316effabcdcf +I = 11868e731d9f79570316057fd87140d14c24ee0576cca5c05618a30a9d30c63d203d74aa2dadc83402cfd0694b129a5500b52f185fe6e839680aac747813e24f8b1801543d76fc6028e53f4d3271170f096655b9bb9e05ff93a5679f7fcced54 + +X = 06ca71786e4601caa277292b98f0f33c816e8538aaec1d40807631ec82b689fdca9822b89c4805421557b4a5fee7ff78041d205b4df55bc9bb273f87c9857776081e96c28d39a16a943403314285add87ba5b87e48326bab91c43596b692b01c +Y = 1399a168cc4ddc78cfd990e17e9ca774d4534ac4680864b5fd62fd58f84d86488f641f34c7b290bc4ee8e24591d54f3f13a09b3bd759263725285058f7e1eb259b4632bce45f43017f07045d6252f7f26dfb40ea04bdfc1e1f8ec8ffb65923e9 +A = 006300f70113f7a927351256d441edd9f14a84781f6f6f3716a85ca484531a223b5041eeb2a695feaa4196eb90bda40c17bdbb97254e8200e04f8fe0c167629ba364c97f7198e46c133b078ea4d8a5cae9a0f9684cf067c9b152fe966cebd405 +S = 0d31e1f9db780bec1db940005d9ff89f119285f93668cb49ea4407348119f9d959e0038285e97485806dd2606d125ae40a7d9709b01c1c2ce11a96e514ef3927d14faf8a9c5f71287c5dd174d6e3ac0a2c567792f4c86f8d2c346c97003936de +M = 08d453b087df8a13617f0f25741c599ff4ced15c2e305d80816c82560af49976291879ddb3b5720b26b50e9a0e126cd910eecb651646afd3ac580e0ec038db884b9bd5d9e3803883680144807d7c0570bc714659a8097e3b71e79fbbce54ae20 +I = 0f5f01105281eccf42652ef8117924f9f158e8999a0151d7ba3b64cf383c1d62b917ef0fcf5658e8c789bd5bc4002cd40443f12cd7d1f636ff0c96808282bc7741a3f63ddec493baece7d0cac59126b6be47d6532d7192dc41502e51fa24d5b6 + +X = 036d80b38503e3793b99910b166d61eb73e4519a66a293bf6c775ecd204cf08d4c0a5b798d0ea3781d500f5356b471ea07d61bd34b00cf4a2bcf30c7db2eaaaae7d9c6b156bff33a430c681af4a47ae76afe4d86d5cd339a2f72bae8040d9247 +Y = 0d8a9798832d9fd44dfcf4b85d96c51edcaa23f78fb74ad7a37731eddb90149cf1752918a83637559f24464c464b1c4305a476e6fe8b29a948a90fe1499023153088b85d6039b1aab5402ecde37259e2f522bb95e9af665c2cb02d485cdb986a +A = 10f8184c0831834d899685c37404270a508e7591f659de970fee90bafbdd052a3d7f84923544dacdbc74559f9cff8e2d0d7a92ba498bf8f3747840a924becdc018627f0eb6f9a4e4f84c96e8d816d4ca6021091cbf7c99f65c22e83060e92ab1 +S = 0fe3fb053b562a3f38b84408fc2249a3fbb17927ca705ba73030ff803b6dd2147941325f962c6c22382ac907106900520231a4ec4c75a5a0e32620e6919e8795b7510e53f686418f8dcc394d1132210475db91f0ec1dcd3e02c28d9fa731f9dd +M = 0caa09b01e9ec01aed6bf70478131d2a59f64a1e2fa2fd4533374c8cfe130559b9dc4f6c35961828a60b7a942cda0c1a0221cd0d346198706a4136c6911ceb2ca3f45f95b0a57aa148cc500befdc3497b07082f308c36385b1e07b5b4a52e53a +I = 01f026529fd274fcf900c2f88564a70b3671226e5d274e0d2d2b678745a6da2b88a871985eceb7b66ea7a19cc20b4b680d740640ea3a6607a62a4bd22fcff95b97b27f810fdacdd3235e861fc56247f5a007dae4aad2808b2d59776dace0e694 + +X = 0b47427573ce4ea360d617dfadedee0d11e6cdd0c6f5d837d3533684fba4939b2e286573cdf81053ba555e3e86641e8f0ed95ad9bc951cbf850eca5ccae9cca553b6c8ed64d373be8558b18db33c1fc626fff719abeb2588b8d0aee8992ea286 +Y = 0541a2190bf1f1216987b5db36a53a0d12e4a23b254c955622fd78f6b4b2f0b5fa12ec1965cbe3ae0746123f8a888c10077cc5cc71e2d424c3a87e693cbba36ada7dc8f6bc80d1444d5ac8d4953d4175d7de169b2236328ad811c5cb8f4a3f85 +A = 1088e48e7fc03fc4ca5dcdbae493281a24cb700bec426d8df650af7bb0578451283b518d33c3f401c19b707e10ecaa9f165620a62e77f0e448b748c607a570102e3491e421544502d2b37a624879613bfede0db4ce21581390e274b42878e20b +S = 0605a05c67dc5d81f74e62047748b3ffff022b95a1a942e1b055bd8e46f1a2e53415795a682c2ca5b30f4bfefbdb927f075c950d4ab2489ac1664bf38e2e293a7938fff6a852a27a37fde8b91dfede504f21e07e89b4f2fde0bee91d09e46301 +M = 13c8b35386f23c7575715ec7280326f96ec2d1e51b8d3d31fbbdd53bd8eabdfd6924b3527441fe867799ad6f1762d3e40d2409a68f16857723a29da739b2ada35e8edda903d25086a5baa3d4a194cef1cac6d3eee859f13ae6bf4c0649630d2e +I = 10d1392a84c67647f76fa1f58439d1a4e43bc1648c81316b47aaa64a7f688b85031382da6eef7b8f5fa133e1817a6bdf1128ced23fc1d4cf998d7f252eb675abb51085e785da0c008e47b1853ecad53d78c206ab2d2addc0703bb336e32e7867 + +X = 15f341b3dedf9707a1aaa6a1c013fc3d393bbc4884f850db36a97fa6b9582d7ea08911ceac1e31391ffa78d73df9c74117e8ec0a59e89a520d866dee9a6d45f21e346e0a5998431706d51deb1e5857cddb3fcc7704cb15ce9103510eec1544bc +Y = 12a6019b3cb7ccfb4cd8c94e5bf1cbd1cd1946e479987f454a025a32411b3eee72fb29a8e6d27942e2538da0962518f61383bb0c061fea84e0ab645eb8a59cd43cfbfbc38e124a691ed975de032d8dc8bb373051e368d2f92cbe525fcdf687a1 +A = 0e983164e2177d68a367c839d8ba1b37a1ddb7a80b0bbd61197b073803c27648f4d83b78e19caa7c484f0677d41f358c116b952c26889e3ca3162a970fc735eef6b91e48f4257ac0be7dc1282ad4ef7277cafcca36dfe8c803c2a36eba0c21b2 +S = 034d4018a227ca0c54d1dd536422306b6c2275640b5fd195eca72574783cee902d8de825c54bb7f63da6eb36a7d4ae4b046530fe53c8afcd2cdb098fe1c7a91de1387246cb85f8ade7fba80d1b2aca0520089c25216242d56444feaf1e1ebd1b +M = 1796d8cb5b094a43ebbc02f2db6399271dd1313fc101bd83113e26b7299a20b311913c0163eff1cb46534e8f4d14fe45139a7de3765315110fc839ca6e309c34cdf733d90ba40537574671d81bf7765c040f70fd21f69d047e35aea34f14eadf +I = 0730f27a25d67b80a663c15f9138c82246b721033b5e20aeae7d7f31ce2751657878ce380e3f1d64bb11c738030cf1bc1097d4e3bd64efc16ec7675d8bcc1ff6180694a983da8ae67f76d0763e49a07daf63822aaee1fc22dd6f778a657b444b + +X = 13e51ccb9ebadfd2f6b5dd51fd68f96262638ba8de09c8c8b59b39732548d5009001f5a23132290d195068a7ddafc1860420d9513cab4d6e67d1d2fb671ab500982894fc2b630842cca457cb7252d933fae2385f891cad1789a06e507c53299c +Y = 121e7673e6cd3b9b63c95f3f5f14e16f20f654eb2e2a399226948ccb51a92f81898844628061a8bde58169934167dcc618d0f9e0b860bc2dba5718c5a1b0d0ad93ce50b4c2cca70be59791fbf58e68c4d20422582473a83b2625c855218757af +A = 0c0281554c0834d40f6394db19322dfa1ee2950f18aeef9b74fef39d80410e5dfade3a06003fd1cb44d2d23b1f17f3a102f0c147bb8c2301d70d440ac57fd8d6c77f9a2bfaaa9c8f4b0b172671304bd4ae3a5ab8fc3c5552f5c736a59ddad6a0 +S = 01c6a657b7eda43792ec7e129e5417f3416d36bdafdf8f368f06aca7d39fa57f0679b13fb0d0804f33ceff149c47e4c00550f15abdca77daf89661ec08b5912a68d18fcc5c1b73f64e3d987073756693478a160615fd04dc1d79a5fb5acb7c98 +M = 03232e8cf74b961bef42c6da0b6a1a29edb40f6103f56eb98af092bbbc4034f0ca8c0a963d5cef077fe1d431b161c7c10d5bc0043d51f48fa4483ba6c0b46f5386a23681bfcb73b7964c1ba277982b1614061f5ef6af6c7763f7e9d038593bad +I = 09d0ee415ab87d753ab2cddfbd13f0e3a64097379954f380ce4dfae20da63b1670a64d93d7246a1b9a1780e7ad346319164bb70020c337140a4c76819bfd6006d77130d59ce0eea0bac7ea765786fb3114090a2d33a87c8a0845975259da272d + +X = 10472d5396427c50c4e490b82604b2864bc4bb3ae8b221674188d13a0b0f22efe2cb23c40385165934d452ab2489a3f702be2245f8c5fa89917ce9639072db8edea21715d766c486e58cafe735eaa1b2c8aab8fdb5e5fd68732056a07c28d075 +Y = 17ae8187001ea14beef182b24c1a77cdb9b44fc01ea7636b5cf58ddbf0b9997c0f6c716c56c68903828569ffb92ae52200095e5022dd293435cc8a1970e1130154f9ee4378435c6c233e595708eef348522b9ae6372ee80dfcbb40f8eed83317 +A = 0df49cf05ce1370268ba6bb42ed37d7ca101bf7613d47213374d8c750517c647d38b9531a8f79f5cfd5abcaaddb4de6e02c780961ba323bdc749737d0153ee90339c05594faa20f308cb093e3ed994fb1ad653e3ed14e5766fdb97996b01038c +S = 1299bdb6cfa3c19f210eb5bc1d35e78ff687b6ffbd8fd0bb4bc415ff11067f97f20ab2565e128d556c4de8ab6b5e698002b4c3f5d5e8d1555bb05f4a1f91c88d89a828d25f23681ac24e56902cfbae6a767f1e177eb7155a766515a78d509d5e +M = 0d980824e592c09312fd2c1d085c4bbb9dad141f2aea5a7d9c66c127e58147e0136247fad70058e92e91468f2f1621261540650adba33be376c0e928383b4c3db1195580be2cc272c86138ba97f7d7dcf5b09b43179867deb7d7da7fd0a6be2e +I = 18437caae0d4de753e0536a6eea7657af96e0b74b30ad289cdc4d4bc4276d215778669428a151cb52f73613720e9073f13f1d241176d10fbed115b597da7b8cc0408ed4401748f7e71d216a45252099c0faf1d204e424b6656bdba8bd31c175f + +X = 142079e1b5d0d3fb9f78bef0152be2f6924243303127bb07d153e551aeb2722e9f3306b8f56cb5b18b3341e25a58aa0b0ffb45bf2e458d8d9b04e7056d31e2f5827cc38d46b3d4d19c4acfca61c21e1dcbe212de0ba4f97b408d54430f8cc6ef +Y = 1412a04e47bd25e2d37bef5967f0330be06a0e373d2fa6c4c1a225be5011d486c2e3de8ee4d3bacf529141596ba6e05a029d57e6392bc1465f8f9195e2caef836cf707eb2e67c131d155e670b20530ea7d5b668495bd5db782fe38a89d7ac103 +A = 0e320845c40e134427d9069339d0692b0e3505e27ad24f0d2bc5386f08135091436ae54928ec708123c5833bc5ffdfba12989da567714ed3fa94789b4ffcd278ef73cb78751b96036da0b63b13c74f08493d7962a1625732c38b8cebad0787f2 +S = 000dd9936e13ae18cbfccf96ad3bafeab1d834f8f3f814430fb1bf935ea09da7dc4f282a1098fae238a20088eeb1c9b10d5dedd8f519cc473b75556f8a66f3721585bba2184c139fcaf4e959afbced334e86ac5975e79bc3bd8f1b9a721205ec +M = 0ad127baee810f9ca36416de539f387711a710266b1d22f80167ac9869400cb9dd82c3ea8a61b83dc49dc727adf828b807dc39b4385791887593449a3c2fb08c60fe511adf3651e0d08a8d0ddb5536ad4d820ee319d8b955a2337a018957bf5c +I = 1844f2fd9c743d1d49979c2322ed2ef02b00afaea892334fff75a56107a589d90acc0c1dbfb742c8787bf305ea3af4ae09877604886ca42f033938864365ef184de2d17634f99f544a3de94ec6dedd40812a8ad42f02bf3a2fd9d6d11a269041 + +X = 0e36773898348fb9722d33c12b2fbb93c8b54e687a107b056845da21ef5e01094e0778f9a4d6facbc31c6b4bd5d3f070020fb9e9fb9f5cff2129c21f566ff1cfa055935eca29bbe3ec4b682c028b5c4074b0d445809c5115f5c62b7319bdff7c +Y = 10a9a0be26cf18a1ad6f2a7eb6def1caf930d3cc473dba3ae6fdab4692cbced0944c5ef7df21400050572145e2c17d7a1912bcc6789b7bd5d5c1fda4d7bc0ec33d1befbbe74e8f9a011b33f95cb0d30ae05529f7dd3909d9645a8642b249d700 +A = 04df060c8583c1c0d480b6899ec300875d6ed6afcdc92280e812b2c78b78d9b5c3a7d7f2d2a43acc59748c91b895c33f012164c63abaf23aabd0180deae053bb78fa3795bdf338be8635c984688b39273659fe3eac815aefa021b1b5cc082bd1 +S = 178de864aae55db20fd9b0f8b79c76a033fbc6212657d389e879017c5343285cd8671a007709bacb2cc44a05f3121da102fe0f0dbc83c7c396836c30c1ff8fe3c7b0ef27d6603f09526106d39c8b7f59b307aa4c54b7473c4b6aa5306773d327 +M = 05e0d35794243931383765400a915e975ba026cf887f6558d78a00f98279beaa14577ce17f16ada4e0af412dad3ace5d103fecbec63dfee45b733405f7ad40dc565889a83929b207ed2111c31fcb0a5c3d56e338f7fef504af5312a00f2d869f +I = 07f3ef6790b788b7b127adf0237baa711e06a9afd259681659cbddb4946bb5fb43880a49f55c3bd883eb0d4c125ebf8c0abad1f645f5872c2583cf18e0d2b704592d9d884ffca2f48073d9c6496b735541ac3b6d99038ecfeb7505161c796c95 + +X = 0bde710ee1eda011d1ee6020d48c13a763b855c3595f81b86d4a7a15ecd3444cba9c2e2307b252c689ca6417a93384bf03bdd295381b59152e2eb84844eebadd8ff3efef09fb2b2cd4b8a88c733e10564d56a59a462e64de3df50073044351ca +Y = 01556affdd2ea5a0d89b0415b9e4f70476d173c2a000ade7af155ef85ce31a2b71a4c5995e1868aa15fb15b87ae031d30efbae2c8b97c0f4280cb4613371e646288e88460f971722396c49cda51c5bb8080ab8bb94d10333cbe58bcf8351298b +A = 0d33dc0ebf1c45b2aa8964368e710aabda89c985f9602fa01c5fd90e49b65e782c40f3bc65cabb709fc579d02413b69212b980c1c3b31a09563b6ca97860a123b88278351992424f0e24f25a185a6c0e55615e55daff681209da8c4287947b55 +S = 0a89060f04befa70f9535c0b1aa71ca2ece6e200b95ed3d0be351b1d8ff02a2148f76889a999ea1c73cf4e5f2e5352ec0ec33652e6037ebb513dab9d54c8816ecbdcb32dede926ca027d315fc4d2aac263f7ecdd62b161aa2c0e74a380f1d2ea +M = 0a55a695213a885af981062aa7e17af5040f142b3fba07b818938bc334ab7c5d1430ac761c32f3cdae84d94333a1bb2d04f565f06651026036f56173114b59dd1347313eada8a3ebc0f51cfd3ea28bde074bd9ba6cf098aca6f1b0020a8e58b1 +I = 0602fcd734f2e3f7943ce1db6aac27eff5050005c7af5fe3f15cc7a6d795138de37b54999e806959d30c800ba14b41d50d31b8e1bd6b5a55ed0fe4441a219872532afa8086508451bda092698fb83660ab074b5236a6b642a46d10346dd617c3 + +X = 0ef9a7e18313a08f94a9cfcb4d647e70ed5bb44a852372656c89710adf60c2605553cfd5916a56ecc2555ba13d291c3d14e81b4c864c80e5f11562636c7637b54e0af12021e3bf5b2380cbd36833636e22d34bbde81bfcc81930c130762b6d1d +Y = 0aa7c7185e0f810746f7520e302349801d655777548b2320475863dc3b76ca64736d824bbfddbc7073f362f4cd40b7490046e5772f9a49f29cd1b9ad04caca7c8e2bcc254e02de3790d7782174ece625497b531adef15029cefb7322d923116d +A = 19a16ef9e1232196dba121d97d87c7f10ac10bc1d9ae9585b3e1d4e71ad78cc4c8c152215148135d3648be960a69d386152f00c3b5e6cad88de71c1071410231dc36bd456fe69d92b45843f4dd2049936c4e9ed8c70d4cf1e82c34534f4e7e8a +S = 0451e0c925041f884db27dbd1d4134f0cff65cd330984f4525310d2ea3e9f7fbe1e64d89d18c9a7c4e61f8ac6fe864f414a135d556b236f35443a8b667ab6d38bfdf24fad3e0e12392a953b1f3467d48d957f8a3092aac9e4a354e0d9d085bb0 +M = 0e28a3dff61f311405ea49b282ef37d12a4f2af77aa8597e4f7f2be2f8d49e04166626ed04940b339b1e248f9da10a1906eccbb3891cc36ecc1d37597eaa35931a996ac08d491f39f454c882ab19385ebcdabec62756315b3531757cf4202640 +I = 0687e85601875000b1b32099796e342568bb114808fa3fa866583745aa8278655728af88ed9c662e8bc1886b7f5e4d58036b5cfef386147e763519d7694694801e19ee0aa829fc5c38a9b955d2d7f2ace69f1f2d81ffe4413cc396d9b8e9c429 + +X = 18393d81c305732a0bf76ae320210aa8cee00b4d39ec6374bf924e7987b52a25126566e7247c31a257ae364a0d8dd2e418cf3d3e64692c377f7cc0446e964b53ef7fe0a79fb2ad5df348ffdfc181591758bec2b5735c522ca1d2b41995e6b698 +Y = 13fe2729f237c26fe7279484ed78a954335596ea9d8f685042e0cdb2e6711162b98c3a8eb5ca5b895eae568d03fbacb51546f007d5e809402bfd844391ef037546b00a319112b57e05a41a74bd43fd9fdfe9bef952b9d048242ba85958f1a4c6 +A = 123652c17bbd4effa80357b1ca4e07259dbe56b2e3f6b9059b42498b77754563ad45a17728f28d2bfc5d8cd71189d4ee14151b5c00d14edd605e9cd1bd39a1f1d1b89f543d40501c91bc47b38814609319fc81b014c222750bff5c72eed8b0b3 +S = 043b1657d0cdb0ba24cfd65e32a861549b8a74629c5cfb247cb180c6a14418c258d92c586eb1d618f8ffdfbd0992262f03884d368e8122f7537f3c00dca747dea8cfd6760e9ff7dfeda4e56b043d5b7778d503bc20a281e47da70bc03cf511d2 +M = 174380f5670b4ecb63c58eb9ed3d61098eeec1a7be9e0794166b3602538f591b8992dfc36714acdedc8dd5ae571ea8800874558946cb9e7bfe8b3e3d83ed4dc8a78c9bfae65977613b1c08ab71bb2f9c6c7a4500ac7dd4881c46f583e9181841 +I = 11cc37d5fc934a9c8039da3454190a412c49bffc4f7022e008388c99c2829816b551a87ce7b8a9f2f56ec05a7c504c78089ade55dd5f88a32e06a7a6e5acecdb3f465e995180da363e8d5487db7edf4fbc1b6c9924fa664ab4ae77deac4f775f + +X = 102ea0d2a01a493944811ed7be5cc6ec2504b628b99d6fbc971fb7755155b2bef7ca3911da010844f4378af51b5db5ad0590e71e2e3ba12b05403e9f8e585f09842b87c6980bd85e20a259b3418d97958977dbdf22cc1e679c4c1d2c8cec4c19 +Y = 09025b788f29bf1ec19bffa56e87cc6bd8ca4881ed6f8c17cd7647b4daf80aee1f155af0954800b44b0b1222baa5912f154235c274c3abf87fd946a0a7460a08a7cbf464a7ebbec32f31d494c89752e29b92ad53dcadc6381a773db52a76c540 +A = 1930fc4b2f440858061d1e7d2ce49357fdcefeaaa70cfbd46495ff2a2c4dbdad16df94026f4908f93f429d17d60346dc00d20af6697f668939fddd89f252bc3ac78030a64c728461e8a35ba71373f454065e89344e25e49ffcc45ae1b76366ae +S = 072c455a10f08a1a82e51f324fd4fa804c3a6da6cc2de3a4c9a96fc0765da7d0d8b4de2144b90790a92c78d260b8247e0a4fc345f2f7dbccd0829fb52a5e01d840d6dee6e3a52c5a58a157bf6fa73ad70c912e89f772582f3bd3df7762753184 +M = 06a54bf083c8590b813e17384c42cb5f7134cec2ba5305e432a51d35ea53828a22cd1071b83a037f39bb3a019a6299a800afc658809f3328d14aafb0131d9758ec548bfc9e4f4c17517e5ff68d53e32849b2e2c0d0fc19c408f0b2818cc426eb +I = 11a28870e7477b3d3de64cea734edfb4998888964188ed9d9bd60744226184e66ce12bcc2dd4a8b0f419ec6778b74ec50f352e523c7c899caa6e9220dfaa7769b5c64e54cd93caab56a92ef4ee4a1acd98af1736d10696e1cad905fe0ba73813 + +X = 00bc305a4be9485b3d956cc7936b233f1a6ffe2ece749f8494ac461518d53227fe54a499ec96c870f6e6e3a261645c520fc789934e8ff6c2656008a583a252fd9deb6bf71d2391347c60f316e4b31293d6203fb3899e1260b0455f6ad51dc3f6 +Y = 192bc31919b229982355ccb8940aed87ff8ba2707bd7779fd1ccc782dd6a066909b1cb114c01ad1c0d91d6dad3fb23a902a8ebe5aca35882d3a9c4b77888dd17b9579a459ab2d884d915bd402a06ee070055fcf0b71c9a83a56feccaee0e5b4c +A = 19e7f373659b71f360eb3980277610c719fba09f4a4c172466790d97f63f389108066fab3898758d0478ba7d355f7ffb12707578fb334f453909cd5cfc2b30155743063cb7d669b95576b0570eba009ad6763ca440baace455b54c35c32c1f42 +S = 01917f2b6bb7055d655b47c542abe28e7f5ba74346223aa42a105133321c21e3134ed98751e91b54a3540cc78d68e3540d1e9dada1ec9e3f91b643ee0b1975e5e493d1b18270b8afa34b35d6baac248cd5ca42c2d28177dd0ad5729fe70f68aa +M = 094142b83f59ee540869ffaf0db6444290a4b57147ca46ea53ed98404dbd0faf50031519a9a282ad821c248440ae41960a88696210f661a9b46e5d10b52498a06bea8ccac33eec91871426a46853e7ede15627d1ba74085630843d426568952f +I = 0f364e9ab072f41e285610238e4fcda77c68d5027f7a520083768c619e55091a903a99e69be6b306e9eeddd18a33860015481eb631440d485c042cfe7443014fc3c0f116ce2a5f0d5907e5b5521838467edeadd291d90edcf953a94aeb1235eb + +X = 16d27bf524ca1f01f155cd3008981a0f2e0574c339d8935730cfec654571a37c4ba259cd74a25a560ea24ddd0d04c05213789b64905a683d4ea05abf101bf01ceb627c02f16bf26c414ce67caa32a9decb1094d6c9668f2573fea2e762fdba1f +Y = 1753fe666f0e3c8ea7fe964cbb7fc42d148ecddf9e9cc7d13736d439bddff6ec108e9336f012bb8c8d2bce21484eb32f05f138deed6105b5d8b74d144adb2ee3c7a8a8e876896690f5e53479296552fb01e175e278af1bedfc8cc6a61a8a71e8 +A = 142568715a5874f64e38bbc680cc3164de1cf71de4f0486900d5edfe0ca0a4443d84ed05b36115e2e1cf1bfe5553c8d61969d4437dbb6df32757a7d35af71f00b30b24eb67f558fd37321af5d397fcd9ccf20ab94215ab13708b698d7d882c07 +S = 197f8f78ef3bc90d9472de99906402b97dedf2688ec0de4560c9eacc7e42a2b459bfc69535e39ec93b757fbbc4b5b7ce0d876285a2f9628775e90daac540c13923b9d31a7ae28bdb4b67b20380cd56e3c92f1ef450b773377771dc4148734837 +M = 0ba3774814c178f4ef9f16628c5c6dea0dbbe7c05af918796a7884cd8e03ce05cec3b7d5438320c5ce81539969d61fc6009622f8417a139d9d3320bf76b8f57273629b252ca33e549a9048175c8861e944e2ee87da12f4a4e5a0028339a12c1a +I = 0424fd2b93df9e4a119382aa04b98c57882e5140c5710ed3a388b5d619faf224f6afa4d9514a8f91fcc653b1cb337998192b5aa08b5b9fe3c54cda13104c1657f9fa66e35dd814436ec0a3f0782474f08af429c96348fbe5e3d4f47cb8562029 + +X = 16e36f91b09c6ed26aff0038ec5455d4ee0e20ffe6fd2ec18a4679007b0790e2b0e255940078366674fbcd0bb615e8ef172fbf47d4d9af423d36b08d8d7dff2e9a34e61845268c6e239ccacc660fcc50de4ea03944ab0f851e4e6c93abb52918 +Y = 13a34162c4e42f920cbade3466aaede2a4f7db5196eaff99556ae6f3296861da24af8790b5700437cbebef0da62c47c60aeab2191033f2c1856ff41385413690b94b7d7cdc06d8b958742daaf1d0d4c5cc8fef61651d48bc1c978e486de18739 +A = 10859f0a3c00b7ca2c9e36b70fb396e02e8eb0cc8a631b9b78808d52adbefc98b6e5dd2604943a9e86e8bc195c42860a08195f76ab8dbb69778afceacf7388e7ef0918102da8526814e025d6612faaf28c328f9bf874584180e6fadc199705a6 +S = 03402e2eebb83f405e44220485a967f2491645ae50122f2834db920d519f2f088c32ce034b08322ea90fddfe0fe9a1290c450d2ec4a5bc80b7c6bc7a083cc89de0e9689b691fb3b4cb289d21743ef78b11beb0d7df8dc6c901b6de4b3dd3a1df +M = 08427516f7fef8c1ef8518632ae0febdab06e6ae3a237f993571d1d02602834a1539f281b18e7e2b4510321b08625203194b1373b8ecafc0cc48ef5750b441ff643fe3bd4d78f79530eea2e966e524d2c06edeee698017588b83894ca89e137d +I = 10a50e570e2aac9474cc2f5cf7bf63529da2c53d2bbc80fb60e5184a81e850381ad7dc4560e9ad8717a5b439311286be05b9ae57b6a5bc5efd953fb15cf7117e48150878c9a85ddcff8e749f20ec2391eae458cac8f3f2dc147dbcc87ceb669e + +X = 12e6a3f045193a2e5d9651098d2a2df4b17de7a7302009548d1b50dd7fb7de83256bfd5eabaa384216b5a5c783b467e31617d483db40e8314b704d356584b4bfe59200142ea3a6aa4157d53b1d08fdf9bd461f3a217d982c6a5f3fe86b540ea5 +Y = 07f78a9689907ed981020264d2c6d1a52284f40044ce65bf6aa094d063f744f07ced89e1a396746ce85ccf1ff18b75c4039d6ac0754d39c03c514210c88a086922c2852a721a8bb0c7d8d4141e5bab627f1450e8195c866e942919f459776f7c +A = 00dd1c9c9529d26d937cabb81ca552c26f8b902281695c54908b130cecfe2d4f83ad87419decacaf451374e7754032fc19b53f44508e21f187c18f462e0ebd290854853ea0be325b0930a94f3b64a95c3c5a70223ada1e9afe8859dcc4cb7e21 +S = 0aef1959bb88bb54dc944ea4ba635c4f8ef8f3a6eb51a395227abc0d1bc09992a87e737d0813c3d52e58d6a79228f21f127a69c365f3ae710f1f0b249cfaac56c2cf7ae9bc891af9797f0126fead52973e31ce52082111bdd63625f411dc9f29 +M = 188ba0448bfba1f9795b3dd59a881c80e2322d5bb70d47a15f2abe8c863f599bffa206e468e12adc4d60e75d0448d67c04f743e1acdcd51be3ec6a75a41a0a7b843ee7284a41e8f32350428c869faf72a5997e40583ddd505a7d8df038b9fd65 +I = 17f008b5a67ed22f3db8cb651e8415e54dbd424b422c9ad774b56a10969a4f766bb3ea599b6f53c085446254c15e9018113655b736e01fae916b753d73dd655716719085f5cb0ffac8db71e86cfe578b4e5121a32f7b1e8efd02cca9afa685fe + +X = 188a8e6deecdaa6d722d2a02fa581e477e9478d177e84011c4c8ee8859a19737d72b360fb11c7ba962907fe8852809ac0870633292d367964591b25b7cee3e34dc851d6bb7ca2a9cb4f60ce899a0f7688bd606240bd055254db82009d916a6f8 +Y = 0009ce6917dfc91321c8d9ef2949546f04075b31906cedc9e4cbcc1f750654511071a1529c8bb88b9602b7b7544daa0c028636bf7df5b0cba915a09ddb0ddd83b1a0555931cf835efe50771fd1e16c6eae07ba7ce67cf292409dad7beca2af22 +A = 18945cd706ad738093f603f223a172b6829bd40308552ddba994baa7cea7eb88e79cd7624da83434f893379fd975b3b80af699f210c91861eea752f957fc1bb88e2572c4e999adfbb34684086b8263d739ddc0a0f24d47b78e55cd85c5b9561a +S = 1880c004d6ede15a50645013d10ec9d87a8d1d9fe77b5247dffd2268e49b42e6c6b994bd1490c31dcc8dc83130da5fa005ea2c7314ddb6ca9c7c11bda1e060b12ae4c81285faa73db6a595c8c7bf8af9ddce4ba7255362930d1a728dec73f7d6 +M = 0180fa2ef149b1fb33991b346069d2725c8ffcddaf0a16ba0573d719f8bf960e2165b88666e4e6860d0dc1610f2b25880729628340d0a7cb18c2814169580ee8f32da1bc31da8059b3e5dc8d8a66f95167e1d9d89438f3459aaff13aaea0fb83 +I = 03cf5b19299211f151c6360f388df05d8aa1dcd0c4e162ae6ccb36be1c5ea06c3698568f3f60726a7149301badd9ff8a18c57cd3e5ac012c008f3d64a18a4d5f1028298929d04c410ab666271a7db710e763b2e285f9b893f41c7d375fa925a9 + diff --git a/src/tests/data/bls12_381/g1_deser.vec b/src/tests/data/bls12_381/g1_deser.vec new file mode 100644 index 00000000000..89d433cbeb4 --- /dev/null +++ b/src/tests/data/bls12_381/g1_deser.vec @@ -0,0 +1,119 @@ +# Deserialization tests for BLS12-381 G1 compressed points + +[Valid] +# The identity element +P = c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# The standard generator +P = 97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb + +P = ae8d62b64cd649a5076bb5cb7ebfb3a3ae09b5eedb038fc08812d607a05161078c798000815cafd7d71786b678a50573 + +P = ad498b076e0dcd5d1b6451c29c6fc37419a4db06ccf60605bb77d3121c38b6426abfca48268042106a8f79bba68109bc + +P = ab84757896bedd84f054c7989421edf4ea8aa35ced5de0e6fc1ed875865d5f56b78eceb0209ed720dafb2b0071f6fe81 + +P = 8fcab2a0b6407d07da4778676906fbfe335c49672315b5c4bc0dbe44735c91f4422e975dda84c2e314e32700137acb47 + +P = 844f67474ef1ac4bdf265b836b4602a6a7056c1d31439987ce19ecda19a59beec1bd552935d2711e72fb9c346c68aae4 + +P = a2f5e3c5ecfaffcf7add350928dd24e085a5bf78d1df6445da6607dc16aaef50f685348d441501cf7ea73ab91c2a28e0 + +P = b20e688e802f2dbde016e860a5a19f9773f8bf4ab39c03fb363cce77c36b2e73fcdebfac6a9419a0d002eda8124e7b54 + +P = 92d314d33cf469385cbc1759c65a5d0c7a3dfa46f8b1f06994993a528dfcc9cb28c4ff1422e69b0d0e9ed31d97e675c8 + +P = b14cd886b248e91ef85b5350c1c809c6513c615878210123b1a1125903eec4e9f4813c283dfd2f7f0eb3dd31f766bde3 + +P = 94c11f276fb3f1cfa0206d43cc85e38cb020444c2cdd2c1f5533c0dea539c62016ff743364c6901350dbf7d2a4f52934 + +P = 87691beb1c46490df957e7a8cd24d7b1d273544edf58e48dd55622c26fb85c1ca1d66de38cca7f19809adb2d12aa24f9 + +P = 86757c080bcbf7c3d537aa5ff24a38062aa4674f029f3dba6c08ee80045b05e3c1f3e810e97d46a856a47e29fcd4e6bb + +P = 89018fa22cff1a33b8b2f184cc674be42682d06f5460539aa72c23b9c25d366ce76322b8a189cb06fd03ffdbf3cd5b93 + +P = b7e3fabe08ae64c16184de606e6f43b0aeebd3f54a2d41b71dcf2e6785f89b40c73e8884dd297ed30d2be5ba646c77bb + +P = 970d9b21cdc94d2e76c1fac3dcd0e3db2809f2cd04cf50bae859c957f26f70d508c633244356eaebe1af768f89777f6c + +P = 882a03209cca882a26bcc2ab6395470a92d8ab3b48e5f247d657824f4294058765fd150e54ecdfcf9a798be8afbfdfd3 + +P = 93ee2d3e94966af8cd1fe18214be579697def7517837aba20b48c1a9f7605fb01b6a10412b97ff9d3453f7c87fe131cd + +P = b41b71457d68a7ed6c687333265e61828555d66a39cef65df47a135f8b1742bf7926166e8d83eae0bc809a71fc544aa9 + +P = ab5393ae9274780511a745606221b84ece0f8c343a89a3b64b050136503df8cbd4f7bb275ae3c0289302301d7567d3bd + +P = aa7ee4cb730d635e3587ca45d8fb79d29f5e40cb855bd4969bdc8ada680c908e9623f96ee59d03fb08fb29841384241d + +P = 93db753cd199c10a77f6d106c042417b87bcec1e0f50ff568393475962e54f504e09b944c092b2a72cbfb81d31e3dd2b + +P = aff3f0d320dc4d674806c2a459b8f04d2edc859e5ce46e61d4be1a9378ea6beb8b7da98184817bc091ef33506c0f93ab + +P = aff22934cab418e31f5f8054f451f788bf1f4b514d3688435178f0743b088c1c54f2e1d973790217d12e133ba151c1fd + +P = 9348203fb75ffec8c8ef525d8dce5b81059e602a71e1e408c7845952b96ef2545416ef7d99709872122cc564d61eabbf + +P = 8cb392cabd063144f0651624e533a6e8be4335b9814aebfcb900f7786b705d785eb7f49a18a854e8caaa0355fe494189 + +P = 8d940d1cbda12d4d391e34f919efe46a5c7978e08001725ea76abab0ef6ec84ec69bb694e9db16073c2e1e0fceaf6b44 + +P = b4f0cea032cbef90cb2daf0402222fa5cf2e3457122f37cfca6514ea94047f38865046e5c3e242f3b8839ba5be2be8b8 + +P = 83486508c299648aa6fc281b81b6a3fd3c9ff8e731e1133f37e49cc467804481fb71493c2e9b2ea1adbc663711fd67d4 + +P = 8f872f5e319ce2bdb1a00f92f61cdfc4abf484ebd7a22b5c86a1bf473640ade18e457fd84945651daac126c2df0cf3aa + +P = ae6d15ff099ed2b7a067d30b764c35525530c72ad3d855187861c66e9bb37853589ff3ddfe073dfd4b35b3aaa563244c + +[Invalid] +# All zeros (compressed flag not set) +P = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Valid point but without the compressed encoding flag +P = 11aadd08a4231d4e5020a1176840c6db1b2c653d160376f3434a7d4c4b7c98af09fdb6ea3141cdf382768156e4bf0a21 + +# Infinity flag without the compressed flag +P = 400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Infinity with the sign flag also set +P = e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Infinity with a nonzero low byte +P = c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +# Infinity with a nonzero x +P = c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Non-canonical x (x == p) +P = 9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab + +# Non-canonical x (x == p + 3) +P = 9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaae + +# All bits set +P = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + +# x^3 + 4 is not a square +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007 + +P = a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007 + +# On the curve but not in the prime order subgroup +P = a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 + +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 + +P = a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005 + +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 + +P = a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008 + +# Truncated encoding of a valid point +P = 91aadd08a4231d4e5020a1176840c6db1b2c653d160376f3434a7d4c4b7c98af09fdb6ea3141cdf382768156e4bf0a + +# Extended encoding of a valid point +P = 91aadd08a4231d4e5020a1176840c6db1b2c653d160376f3434a7d4c4b7c98af09fdb6ea3141cdf382768156e4bf0a2100 + diff --git a/src/tests/data/bls12_381/g1_mul.vec b/src/tests/data/bls12_381/g1_mul.vec new file mode 100644 index 00000000000..194a80f205c --- /dev/null +++ b/src/tests/data/bls12_381/g1_mul.vec @@ -0,0 +1,402 @@ +# Scalar multiplication tests for BLS12-381 G1 + +P = 98d2f065c17b3fb30ab7858a163df3e2b94e176ffefea24fe117d0718c37ea7f9d8aa198bbdbd690e666ec6895229d0b +K = 2283c8934887393803c3ffbd0aa0f8cf5b69ac125300c64f350b45a3dfa11293 +Z = b04803d953a36b0fe8b068030399caf02033aeaac19e35b89a1eef2968240f06d591f1251786353041526b508a92179e + +P = b97fe7f1d9d1275dc2ebef44ce2d9408447357b76d0e5f9508086483df7b458a138c360f945db814b04ac85f672bc4db +K = 5f8dbe3d991ed1280f913a4bd8f59ecf1db75bf3aa5f50f642db58ff16d3c0fa +Z = 92828c558864c6bcf58288943296f772856e472f899d861c49f9eaccdd31b0eaef9e3df90a94cedbbab2e7cbba1111fd + +P = 806a6cb20ea6f7675ba53717997d1ab6c6dd9c001357e3f9d0aa40b475b80378cc21fe9abba8d38d3f9d1e22bc97a395 +K = 56223acb9e2292adb7017d826268b99311721a631e96ebe73ada5d7a0a64c76f +Z = 91d133f53214095e4857f6929c7eb3b1eba3e63f77e409c7e2c8f974099814ec13ae26c3f2126491f5bdebc3ee9b8db3 + +P = a905b9e9340277bf428b5dc1ef2947d5dbcd1c52fca9aaeb40325a6243cb0b67b55b5a88aafb47435be4ff40fd6f7ecf +K = 4df9698a70071afa956c203f3f00825016b5fd95168cb6e3874a98f81bde26a7 +Z = 9586796afb0bc4fb6a7746d48b57f37e0706d9967dc95b4cc085c09760ac79ccefcb74c6173bcbf1bb989e98396cbdac + +P = ae0f0b0ed73bd07a1c7408b9c93b5cb49de462f9c0f6553da965c544709f1704e9a7135b8a90d1daab7bc5175f1f3d05 +K = 5da791934d31217296c1413e375d1ed7a896e54a10badb4c4609972568ce1cb2 +Z = 972604028080926de20cd72520adc8dd0edd822584b1fe1cfb831dd40e6607b5cd833b8e51931dc249b1dbe17d3cb265 + +P = b2a32ed0f6ad0013b8dd5a2e8ba26c76e4a9ff611ca7414a2c0e619390a40939ca0d8d6400fd0777e40e0f772e45a732 +K = 2159981045148be2d11ee555b8130a3d0ee15851d3b19c9370410f061179377b +Z = ac6a64205fbda7657f6a2747b6cdf6a52a59734c87539379b308ae47031e52d0537083722e7cd7fc368b57a1de5cfe3f + +P = a0e6194b394bf07eae5be7db5b36120759cc21715707f5dcc4c019fd446e99d06886a7db670319b385e5f6ec0430527d +K = 1296c4ee172f8f50a149dbbb276471cf1dc87bb3b0e38180fb966f2eaaae5f54 +Z = a9f42605203be7378afff2ce1147d435bf21d2b21c96aa3cf7ad3e7c3a3294d907ef22639dde1eca5487e99e21f86ea2 + +P = 93875d63018b3312308c42a178b5c8d9b7fe413d06766afc2658498d496f1599a55e1b4ab78fd22cecef157c589eef33 +K = 2629f243e603658f4126ea41955641393caecac95c54e4a58436b713a7e12016 +Z = b4a7c1650d1bc37f359ae172ff2b51e80bb6b94d490c2a200d3ef6504afdf7bb624664447c452a85e58d6d4ec1b97b68 + +P = 9273f9fa2aa64554b53ce9a013b2247024726a62fc3a5f1c60c105e7bf5ae84a048d620ee9db8701501dd27dd20ddb1b +K = 4a4f6f49094dc61b7384b7ecefd442867029f439beb267823b2c8416024f82d8 +Z = a4027fc7444c1f73abc5695eca359c6fb31a37b5bae3e9a4cd0681f1944892bb1e9cd081eb8d51acc717e7cd353569d9 + +P = abdc81a62a62f3d04e8bcc0f7f624eec13cff8b50e3cee5a0c7d5fef5e2ef7fbbe6a6eb662228f4be08c86e3319d5b7e +K = 4c932e608cb9bf2edc22d08804ef4346519c95e72fda51c21971ea6a1eae8e61 +Z = a802bf3d641539d030eaeaa89dd35828bf3cde35027e1266d08317b28a21a67555231a3505c7df7bd3fa9577a699a3d1 + +P = 976182e109c03c4ce6dffd9aa226118ac3bdcdf294f7525838d4a6e9f5015405542a9b318cb09873818ffb903ac9fa7d +K = 59eb113c13994ecb081b1941859b6994449a7c33df392b59bb178a33a99d5c2d +Z = b5b5e5d45d6784e1cadf0187a65f3a24de6cbaffcf2e5a117d1fd19bed287142f2782fd101ce9654a721599534f09486 + +P = 89ef5cd5b2f0f8ac3272e227b138ef65a055d09423e67c2dd5aaeab09d3988024348aed511aee5c6c56daea33a1f466b +K = 0ddac33c37c24269bb5b12322f17c121da7c538bf45eec84e325a1ac718666be +Z = b1adb9c4eee85d3998ca4101a49762d6a7ea48661649d5f2529442cde6a9870b77ed788510cb566cda53aa583600bbf7 + +P = 91bef322f511328a05265dc26838f2db680305835d01e503e71d5ab614ea28c4b3f31e7b5bc27b4cabcf07693f3154b5 +K = 6e072f38ec6c1cd28b0fd00a56d249494753b7a5b0835aa951151a005d48586b +Z = b1299fae81040847b7e21199457afb296b839c0825c32642acf3bce64b583f895a11c8fe67d6e7ff10984707bbd0d68d + +P = 806e2ab8931a2d2cffc234a477820f369a173310cf286f67548b0834ef246e59c5e34fbb73257790dd80a5f92db47d9c +K = 423fdf817fa3371d862e864b613785158bdb20549e55afd6beca5e63db31f8f0 +Z = a9e0af73a239528eec4c52d54d7a7cc96de0725a85b4731fdf41ac5f0efac8ddc3113001d97f9388c16e199643eccef8 + +P = 8bda59ff519a02fe973353fe98405a1dde73189460c1c86c8f42d7fbd6ac207d1ded99116ccf9ce594311788bb1975bb +K = 25d40dcd6613c8a67475a05ae64d5ae0fbfada2c1d30b5017250ee96a816e60e +Z = 8c56f4ac31749cfbf56b45edcc6b195e3b8e561e74009be8b396b1f03f64e9e3c9bc7d8eba7f29db44c1fa1a8f76541c + +P = 8187f6fbd720202441fb5458ccec4717b2e5f3e94ed30b3741b9a29ce0f36e4daa06e98db410affe0a01a2522eb769f6 +K = 6402de64564760e7c49082ca597db7bb0e29c84b1cd4e9ad7229903019f36bf5 +Z = a4fba41792e8c3d8f9735443c2ea5d71cf21cef7b4b5a67294ca9bda01af2e944fafad7f6aefa2bb5ddeded077758f70 + +P = a60ff03b3419d186a7611fffc7c14db26e095b186311bf107f9fae5c1cfea0c9744a337463b315faea3c588db5b8fd6d +K = 5c358a566f6ecfa3993718d420aef530b5cdfa7b52749be0a9652784a02cd171 +Z = 8e3eb8629f8cdf6cc740c6523b6b6a9deb25ea7235f29db8ab096f1ef63bbb4f5b58bda7e15180ced745939256f412f3 + +P = b2123828ee210fedcc788e65a1cea739a05b939c913488a599a9d08915a0fd382afabb3dd2d1a3bf014eebe23c4b91d2 +K = 017ddfa48b312a92ec9f2ebdae8f6d0523659864701b12d809036202da9d7767 +Z = 8bb3b44e0e83c4f48845642f8ac969b85848cdbe32969524b9a4eecf2d218dc4a2acbed7b485eb7f9cb52320176c2bb6 + +P = b49707caf4b4a067e04eb5628b8489555101d5c75ed2e5fdee02afca786275a793f796684c9ff787a8a1300178284744 +K = 5a0e5d4b31f2437de0211b13d733c06c990c23fc810e4258f633358676aeb9aa +Z = 98ad67a47711a8f73230f7bb50f2ca6387530b56a61a3858151c111b32ba393d3f0c248e583abedc332935f98b2c9b34 + +P = 84fb87d5c56d4f90c58914434a07d693aa578964f980945aa07c59ad14868a767b1b2c4e5c82ab1bc7446cf39316b066 +K = 0fcb6704f5164f095fdc22cbaaf1e08a1372a2484bf467f05b46cb98ec2ea859 +Z = ac6195b69480b02e5ca583b2ebb7c01331df9b36298721a4ec022868bc456215f07263b3960ace2289a146e2a306a7bf + +P = a7e986cc675b68b5fd9e6f37ce7df991473bddd2c0dc423507b6bac6d8f4109376cd5769c6e4a83dc6d863c35e19c324 +K = 27e7814227ab9d0b90ef6b968a0dcc69030318b2d2a4ae8bbb5ca384ae6afd19 +Z = b7d1f90c82db99979941090f231b9918e49f658558b50fb60eaa9b6d61f377cdcff9699e7e84adffc5517963e3aa8cf2 + +P = 8cbe1eb73f23b6f25fe55443d74058ed5f44250a1be9f837a6c5c0362c0a51ce61fdb4e0cc38cb07680a9dd0d71cbf44 +K = 6e71bd4d407e173e50464062379314d92d49e812d7fafd2c499410d32ea1c040 +Z = b58fda0f206bec0ee8b89f99516a543018b0f6ec098d09e4f811a53c503249dc2bd2e284af08a6b6af35c9a36a301966 + +P = b97921bcf7ccd8f086287f4b45911b09c4e797fb9e1e7f4f47e160575c899f2212fa1f5f141f14ca1a710eef85e6bbbf +K = 293735a47bf3ebd5542212ea79c876c0dbd560b914174b56c8ea2a27385699d3 +Z = a74d8ec531c46be1888a1663258d1cd343c6510540aef1f8204485e8520012a2a82e4fd1b1143279b6483541e64b4b98 + +P = b165bba397e725ce47e3f8dfdf2e643a222074e1b1b1254359ab6ec7a13c9b3c8a49da9f7daea534452030b90f7fa51d +K = 4cc3e9f00a7a004ad1a1370203d5180cd9a224c7f398a50c1380315b5aba9717 +Z = a70d7fe9ba9534ba00f0c57d1012b081b6262cb07bc8e9c5a3f9878f385558aa83482e2900b6a162e539376e8f03be02 + +P = 8ec5b162954434d723496649b4f13a9ca64cd71ad27ebda0638b8978e519935b55cdaa9dcf577f62ed392f644bb1db46 +K = 5541eae2fae57a07470623fa5a17d6748d0d2c9415467e72f628ff70015014c5 +Z = 820b6c2bbdaf609da1b88f1bbeed1268ea5cb2c30d3f37b560d70b542ad71616bea9c00ac92c2d6848695e134f6f1bca + +P = b44031db6df32bec21d178c2b714b804a0c911afc15118c806f041a4e2a89bf4507483c1ca8c831b40024c159453589b +K = 2f280a7c8d994a587db50945ecc48db487a2b5f3f42b16a82cb7be12846e5b07 +Z = 815f3800a824490a8f68fdead9fd4c0d3fae8074d58a3c1a1ffc47ec9bdc7ba4dce5ae23f59213198cb0fa6d3afc1f8f + +P = 824e070e98706c6d67c5d347ce7192db55c96fb7014e58c47475eae97cb5fdf90f80c03565d9bc8a78c96ec62a8bb053 +K = 651285305a579fb0441037a0b1ab7a66bf36282f1b28e631757c43f4c37e8ab5 +Z = a162c52882f28299c0d058e3ba628b233749874e57c99788913bcf712652a6b301c96ab67ecf12a830c1fe36112fdec0 + +P = b46da08a8d53d296f2e1155d2cd289ea1b3f97932a08f513a55ed6845885cd17515033b01d1b841ee946a4ba8e6d105e +K = 5cb70ad8cf5e3f29b1619c570567568f3fd42bdc89bc0d5d515b93b1e20c2dd0 +Z = 98d0f65758e54366a63f25bdca7a6cf504b2c1316d6eeae04bfde9a6f332dc0ee25eb681ea9c6c1dac8160fc8dfb221d + +P = 83e4ec403624598c352ca8b4768d37a5a5bfb2a82c72273322c13a2bf9c056274fb0fb8275c39df46e8cb32635bacdeb +K = 5bf4e39de74df1835cec4d13cadd2a1c389f5ef44e3df70181274bf9cc672ee3 +Z = 8c5a372da234bc9ff1ce9417c745251ead243b8d914840eb2b2ab3922805ac6ba94a79c3e7cdab3d5a553963a69c0d62 + +P = ac9776fc2576c138a1194337345c9ab35064e30bf66ae13b139fae3bd5c26d4753c7170a77cd30fca6c99fdc0f25efa7 +K = 20d74c67fb00191fce0b9e1449ba2815f5a32ef56cfc8398d0a1ffcc007ba708 +Z = 844893a8375163877b5fc5950099d77de6104d0a13c9ae3d6cf3087a43b57ba24261f44dfb7ed9b8a8d17dacd92541bd + +P = b84dd55289f53e6c2c21ccfce9f3fda556240b1d4e97530dc4009f3162e44013cece2251a4d950b45534c81895379d16 +K = 4b2ebdf638fadf7ca5b15482a5a39444fdeb6c741868ef3b9f399431b61f90f9 +Z = 8454226930e3addf64d228e0c3b2412395ae00b40e04b0e6e9025d6d54b97a4e37e13c6e9ba89688f0041ade1629075b + +P = b1badcbddc2155c78c317259bcac75ffd053af7c1b46d9cc2f8a7da024a9906b2fc00ff247105f015d0c574dbe07a4da +K = 18bdcb7d063de493d28789e63d95607b8745fce8dd5be4b293dc83efb43f2e7a +Z = 8575e206e649647f0887a0667fd1423ab6a2ced1dde3948ac331a873e81926c9cca37c55b3eac64f1d8610f8dff92bc1 + +P = 8295bab902f6560001b9b38fe1680fe249cbad11847c6633c5a7bbb210a4871c93a32b6be24cc4cec738a1cbc0a2134b +K = 0a30c0350398a73adfdcfad17c9b69ce9a230b891c880ef6a3b45d66fd475067 +Z = 87966637de7b89ad49035a118227d2e6688b4ef2ab3ac0040c2d0a6f4be60f70e6199f2b1cfbeee910dff32477915b0b + +P = a988a6289561c5ba1b7e4cb2d23aa79da9bfba9cc89cacfa6110185cf9386e4a28bbe8da76ca22e70af7edb0f90616ef +K = 333e463be97aa5d5359f558a9c46e70a7f9a3c8d3f6d46eeba57e4a5259747e5 +Z = 9061ac4e0e646b69c948d6d793eab7d0af924388622b4606e8c2457e7c1e2d7a45e3ece6f1713e9b5ddc7e2a305cb1b4 + +P = a1518588e57eec4beb436be34716691f4762d3ac88ae9e3e8b25bef59ecf4e78ac9c7675c91e2758c86939fc45882d1a +K = 34fb23e526c82f70a09357bd775d5ec4f2f0f433414e4eb912b00708abf59a50 +Z = b83877f09ac07e705504c430fd68ae71ea4d24d88cdcebd823a49234900df144b5b7cba5f4854526ab887a0e79fa7cab + +P = 903b1bb1e1ca838e271729cbfc22896df156a41b266c3b8b0f4f535271362eea643cc46cee6310acd9d1a29add0da8db +K = 2df9160ad59eb1430aac598a039fe532b29cb916377c90fd27c107e8996793f6 +Z = a87d108a546399d2bed6efd26b5e672fa50ed156ee9f8b9da59682e1ec87b622e66b167ab7e33c653993162147f6d5b2 + +P = 8457dc60c76d99f376e41d067212d4107e5a0dc27f804cd3f3ccf99f5c8c171e5d90b5a79d222f57e38e658c19d06400 +K = 4884a77ff0c8f72e7c4577378a554c810e89c6e99ca006350b0ff2b15ef3c177 +Z = 8bff69630e3ec207ed899acb67002479568f3ef4b7e8ec2ded87882cff6004ff3534eb5bc252cdcb90ba15db769ae45d + +P = 98abae56a2a794f19fc2d6e1b5e6460ea79f82c4400bbbfbd60d2de88c50fc1752cbf01523905b909439ae7d607a1c77 +K = 4d023656cdba2d2e11b7a8707714df6197e30cd8f595dac38a9edb7cd57b5ce8 +Z = a95f9f135dbf96021c0516577854c5ad11bcde8d18d16a8dd3384da0e60cce143c618db529f97cb4a039ee543f83afb1 + +P = 919efd98865f1d0b4f233b211eb69cb2df2c7a297172023eb9307f3afb091d57eb7d9346612af43ffb9ba97d3ff3759a +K = 1fc578afc3f38a646914d77ce96f37c50e624941494e316fe0038829c5b10c5c +Z = 88f5cdb5b7a4ccd573f07d64ea3eb44410798172a44ce057695baac28cafab0fec3d3fd428d117cadc7b7bc57f6d6d6c + +P = b6f31e8fcf63ff13b8d6a2c1dc948cc937afaeeaf7739dcb73862366a48c0e27f6a9208d2eec5acb81c4a399288ca285 +K = 567fdb26474714c73fcf70b5fc5cef47afe8b3591e126bed5dfb574419e29a65 +Z = a86ed3ad912fc283467b3a110e322cbfa6ad815ecb46c831f6f3ec61a024b52cdc864488fca62d90b75d406bfaeffd03 + +P = a261bfd6477cf001f2c88f0b9d5e1408b5e410acebcedca083c55cc2368307aaf0a6656b1320969f0bbb4e5a46a111c8 +K = 1b18d53048dceda42461b2b5c35224f4f23d350a082284ee026183c763557ba3 +Z = 843379ab04160d2a42e3ebdeaf723b898461bd97d526680dc4298439e1680a04b5e42b5fa32f9acaf410e5148480dc3c + +P = 9121ca4cc787d1adf980009d0101b2715049360646b89e412a7d509b04069ca3a81911c31cb9058cdf6561cfb29b32e8 +K = 3df1e0917ee82b66466364cc5a5c512e676d6f35e6e2f66a47e335635d3a2e7c +Z = 9777761b170a88f1d330f9f3fbe1cc6bfd2f50340a27fc1496de3d2527fabcfdff33bcac93f62aab5bb3a255bc99a543 + +P = af22a46fb7e2d40e7a2a255ec57f3d9ae55e1855448ba7df82904f9f927e696548ff94841b616ee47308a150bcee2982 +K = 2acdc2dcccff1020c83abe6d498aad7b5f9e8975d82436409b53fc12838e190d +Z = 9240b7bf056b4a53237b7059ef92e0b7858b335d640abee835c36a2feb04dd2a1c2fe18e4e13391bfccff0f6b782f5e2 + +P = 8fb9753bc14f2e55e0e03e3362b4df369883bab8e12a7a3faa23da91c002e5f265e5c2cfaacecedf4ca4278642165476 +K = 52193a8ca706e5863da2698ede8f2c1180b2632cf77131ea96a4f5b8ca1545f1 +Z = aa839354402c2429c95d2c9dd21825e13ca3c83470ecdb0bf4c16ca95fb90d6cac4dffad8a8b2e25e016ada6e71532c9 + +P = b5b004df7198f3f47f24aa43638786d77481cba251af5c24063e12f2b0ae4441c24b26f9ce6b4f4d2479d15986245785 +K = 42871c4aa185e582a952dedd872db2159f04b1872ea7df056ca6a8ad800ceb48 +Z = b91ff9bf1c3068391ece4c89f1c2659ef77bae8e79e39653083b7e7f449d385c4d8129fc9dcaf19740db222eadcd1bd8 + +P = 8b26940cef6682688de35ed17505430169220ee9fc21253344856ae9ce89a9cc26d3e2366ecb4be208fa7bb5968c6665 +K = 5340cf86e39dfdab61690ac75fb2a4eef5bdce26ce8339f565a40241947578ba +Z = 911c6315fede335b7641f3391b2385b43e01275e6b9a4a79ada21d152334eae8703eb233982ad7e4cd0afc69a933f3b4 + +P = 826c3ac1e8783034fd40b6407258b255f20b84d4263dc971612ef026e3d9f249dbeef218ece36055986dc5e14d301362 +K = 381876889d5df73362a2ddcbd78903b1e8bcec577243158e7b0c6435d820fa4b +Z = a7d93b9860ab048e3848adf6de186cdff6a4d0995095e5a86b4db63eff87f3f2dfc43db1ac7abb4535fdf283c787d39b + +P = aa0215caf754c399c78404a046eab1e3bdd2005109fb3813aa085d6e72c046ac379837a81579229c0d8cbc12640ad421 +K = 2718ac783950aa33a42c2072de880fd277170e8e8d8fee1df6ea0dbe8a8c6a09 +Z = 91eea24126cf17ca5becd6bce778773c0128f6204ad8535ddb9bd12c5843a44a92651872136987315c7a1ac3b88b8e94 + +P = a7392b8a03f419d690af0bdf1d0bdfb79ce7ff912b42f32713985a1e53696cf8cf62732c112c3525bfe792feb0328924 +K = 2d464b905bf618638d735d1de91df261f94e7b9f3964d013f6f0cb2bdb2df6fe +Z = b698630b67e8ca1a64db667636fb208ece3b88917cb38f0919a8289af1241211e9c29686d7290365aa0936273aba6939 + +P = 87c04d21c1c06219b8d860ce927f0d07d5779e9a44569ff017a7b40efcc9bc3c5e0f542d8537343a19ff2f502170aeef +K = 4c242d2c5ea2afe82c4eee9fb8ba09fd80f09c5a3ac1722ddd4b534a38b53349 +Z = b133f2fec67065863e7581b3b4ec39ef71a48e8855b153f2e18f53edf7b881e50eb275eac84aa2d6011bbe9c315a01ce + +P = a7b05229c832343a7021f7a0cd372d263d0d05360ade9c7b8a41c5417b94981e8179424a8277fb9f03ac0f411dea6299 +K = 2501ed847d803e8a08567acbcfebcededd9036204b1cbbdd09a5f202975edae1 +Z = a6dc7250f4314015116e6d7c5c4fae39f5669970408168c62e582ce9540f08adb1db9e819f243bf1dc6f1dddcef9ce28 + +P = b9dcb8be7c24cae637a298c9d40503f50e5a3d4a70e8825c4b430f406b0868d60ab1bddfed605f886c997ffba7512e52 +K = 3dd2dc3f48326bab91c43596b692b01c6ce446eb929b759bb9d060e672f2cd2f +Z = 92da2e87f4054236badeb4bd1840d3d178ad087e16e24c717ff9d30c2fd5f7bd374f749b762bb9ab017c506fff651337 + +P = 942fa712a4b4d2aa45cdc7361a87197561a32fc353fc8886144e874f7819c6a20d4f2beada4ce10da36bcb476b8f91a8 +K = 40b7429caaec1d40807631ec82b689fdca9822b89c4805421557b4a5fee7ff79 +Z = 9a009438323df16b0ca74cffde2afa0c15c791818c1b18dd58a6894474317aab17601bb23c465d94bf1c891be1d9b81b + +P = aed8190e9cbd6eefa5596ad829bcbf51f822e9ba082777e8cea46a8b5f699945b565b10bf01f012c1204b93720e1f230 +K = 4e826cecd759263725285058f7e1eb259b4632bce45f43017f07045d6252f7f3 +Z = adbc4d9bfbc4574c0d3124090a774d32a06f0ee635ad18c5b15bed3bdd29e8488e10ccf4b0b87e06b350608af9e12114 + +P = b8947906b780485f57813bcc38c80a1399b6825a4f081f2c480655562be2b7fc8da1b4b986b5970e0a48eaa1e775cc2d +K = 5794ac8f0d74fd5c52fa209614dc1bc59ccd0b44cc4ddc78cfd990e17e9ca775 +Z = a19b0dc18abbf624aa5bd6e97502f51b45f0c6a9b4910abd8fbb0cda5694903ed8daddaa277c2c6058ef5a34ca10a81d + +P = 8cd91ebb034ce0b34a224d483f6569b3566d6f729afe9d991f44b0174098f5ce8ea08250eefbe81dbf8bad140d5de5dc +K = 357f26c3d5cd339a2f72bae8040d9247f0a3e56db411e8f36a2dcd5954bc3756 +Z = 846f2c3fa49f1608dfaa5fa9d30369c1deb45d9d8ed623f2049b8e423429356f03f8a2e6135b0a5298abff370e968022 + +P = b20f0559571a0ceefba916f4c47864df578216140ac28b875f985ccf10a316a7913e4a75af617da89e995e8a1d205d93 +K = 39f228cd66a293bf6c775ecd204cf08d4c0a5b798d0ea3781d500f5356b471eb +Z = 8eb922256b843609aa0c5f879098aa1f9d048d2bf653e8b6113b3a1f745397ed7670f29e66f676f2c3b934aaa68dc72e + +P = ad24782e626737574e9ca7570258feebe3cc37f904706fb3a387510d1e1c09bfb3c345d3fa4a7552dacb2361d4e332dc +K = 18445c2e6039b1aab5402ecde37259e2f522bb95e9af665c2cb02d485cdb986b +Z = 821f2e9a3eef602bbd4441384b33760be2c7aecadd76fadae0f261dc4ccd3e3f41e6bb53b1186e75397982d8637ea220 + +P = b847a8c2f36c26a89fc29e4bf8aa7fc329c6f95527465f53f1b2072ae1f3dfeef588292b874cfdf53ad9c1f8c636ba75 +K = 29db647664d373be8558b18db33c1fc626fff719abeb2588b8d0aee8992ea287 +Z = a30902138ddd669001eabe95e929d536682b59c8c11730f43676895bfb57b35da38e1d1f73bd653655be3fd212c17805 + +P = 9621ba1055ffd8868290854f5f01ef208f9e7d6a5528d1152e40a788ca0879029d4f631c56a084f5a24b679b7bf540fb +K = 0f0a08de7e4982ad6a03fb8fa958ab181c722045e1869cd78cd43d965db27c48 +Z = 89751847c2ee9fbc88227dc2c926346b5282a3aaec3a3d16670e4323966375f3d2e780f4e6fe3d0eba55a5bf60e4ace6 + +P = 975ef7b2684bf667b973a70eb8adf52f56921bebba96ce3b37bc51d32ee33c215eb7654db0b0f0adf0d77e6894b0a078 +K = 2d1d09d773ce4ea360d617dfadedee0d11e6cdd0c6f5d837d3533684fba4939c +Z = a37c6982f309e30a0a1e68b944bd27dc2a7bcf43a80595eed400e94f737f2c31da476381e3621422b1854b9703b1706f + +P = a338689582659ecf584359362d494211510c6a66f731531f998fe4ea747a9607af97cf559f4a188fd727591d67406d61 +K = 383445ba1a9f535f826d9c4bcc2bb3563be62e6371e2d424c3a87e693cbba36b +Z = 8c35885c69192654f48cb65091c3f534254ca647938819303c12a70a36f531e2e0bcd78a64bd5fe6e1036ee43f7eaddf + +P = b36cf04a5334f1e8832c753908842fe86d106acb5e472d5ed9b8e107726baeb41646d8303f861bb4317d9d4f3be27628 +K = 0972511d254c955622fd78f6b4b2f0b5fa12ec1965cbe3ae0746123f8a888c11 +Z = 93d6b646c31ebbf98ba027fbae65064b2761cc8fafa4ac4725f66e547bfc102900df2c4af41751e954db6d8469817cbc + +P = b33fed56193b5376344221f48a8ffd0ddd2148b39782a01588e0c4f3540cd99a545315b4188f969de2cd2bebaf9e8672 +K = 5fa3b02a59e89a520d866dee9a6d45f21e346e0a5998431706d51deb1e5857ce +Z = a77df787e55a36d28dcdd9e24e70356b5eb1c105c6042da1f8fe1e8accf0ee40b3466d736548d2823d26c263c385c927 + +P = a5ffb61d6371683fa01431e25cc25866d906576e5c2f6c277b15983462d4e3ad0cfab993c6c10fbc3524d55dd99b2867 +K = 5d9b9828e368d2f92cbe525fcdf687a1af9a0d9bdedf9707a1aaa6a1c013fc3e +Z = b5b4beb6151fe94a09834a2f78ced4d7b13ce96fb550587bb2b3fc8f882ec39dbdb2936130dead8adbd7f74ddf502ba0 + +P = 80784f69fbc147ac53436a25fbc3e32f2b9423ad932cc9969598e8fcb606658bb001d83454cf28f60cc8d44084ea2435 +K = 668ca37279987f454a025a32411b3eee72fb29a8e6d27942e2538da0962518f7 +Z = a9b7af38ddc8a92a994eac683823ded3be3e6fd42563970e5a83d84a3f517985c1a01d9886d4181ad370eb3bade1204a + +P = 8d01c270a5ec9403365f6452eb7a1d0241d930e1194ae47caa0b14244cd1b40addbd2e6089fa6feb49a3f8c6514b0075 +K = 3131c5d4de09c8c8b59b39732548d5009001f5a23132290d195068a7ddafc187 +Z = b32fe01c53ae3ef0baf874718ae8b17fc0af4dc8865a0fd39f81e5b677013851bcb124b66642d4e9526a28fa3e4aecfa + +P = b4f7a74e835ec114a1427b84f496957d9745164779cdba1f05bfb0a82321e63cfc0fa388caf415fd3bddfd9b3574d40a +K = 6343e783b860bc2dba5718c5a1b0d0ad93ce50b4c2cca70be59791fbf58e68c5 +Z = 84be0c25fce8d1157a4c586f40c454f2ddfc6a55ad6eba54751b523da517f5b1e34c66fada58f971d4d131beb4cc1318 + +P = aa671f0d01815305201ed724dbfa9962e2cb6205d4759b1925bc97b261e2e5030207c35464bfc99d18128503953d69dc +K = 64555c7eb5e5fd68732056a07c28d07590f3b398e6cd3b9b63c95f3f5f14e170 +Z = a57acb16bdbf389390096bf79157935f52856b42653f90fa835bc98686ee205cbfa7053f75e8bd66ccc77799cece91fa + +P = 98cf7a2b61daf3a4abb9865a1424df09b931a47ae9304a5348eddceaf698c24c22d3043d510b51840c9b9aee52b28e08 +K = 25e25d9de8b221674188d13a0b0f22efe2cb23c40385165934d452ab2489a3f8 +Z = 96602efba14d91a938f08181a89e333c97ec80235a7e61cbd4a68e5ab7bc1a113b9101cd0a5ee6874ec288f28319bdd5 + +P = b9dbab178bd5a5557ffc941a74bc680dcc1d33f4fec9d089f85e8b9885f47c7678cea0465328af3a5856f588e708ef20 +K = 0025794122dd293435cc8a1970e1130154f9ee4378435c6c233e595708eef349 +Z = b1b49e387270a18c2d54c0e2615a3ddfe0a8ff0e631befa5378966fe6cae25c4f5e203b1e7bd0ab9f8b6956492e5d115 + +P = 942d2fd77c70814a74a4d05e0df07f58499b1919bf9ae9602e03a516696b378549896876aac39a7a37527bf4271a79a2 +K = 65f1096f0ba4f97b408d54430f8cc6efbd740c3a001ea14beef182b24c1a77ce +Z = b945d64eee40b9ee364507d5fd656d22a900bf94ee141ca512b22a3c2db32a597cf179a19d061b36ac5032ba9c2be075 + +P = 8b3b8eb49e48cfdca154f02d7e86cb02dfc2e1ed2564c8782796f9f154fcaab6284b32eccfac7a9b9bc47a79132986cc +K = 492121983127bb07d153e551aeb2722e9f3306b8f56cb5b18b3341e25a58aa0c +Z = b84f26cd8bd117c02e0f17394f616074dc8b04c6d260b1a8d65437c4927f75cf77878d5578eec1033175eab6e69d594f + +P = a6481645b6a693a3c14d1f8b5594d5f85177a6aa1fd0ce4ce0873a3ddcc9df6610b8393790be624358c98ecd627dee27 +K = 0a755f9b392bc1465f8f9195e2caef836cf707eb2e67c131d155e670b20530eb +Z = 866b3c658baf27dca92afb05e213b99473d6042b810ceba72534cd9d082064f7f474d6e7074a5cc76f5f151eea529355 + +P = 8de420bffdad89a82bdd2d6e6b8c1d0b7b352973f8ab08b8eb21509a12ccb71d57649aadd660804b24707146fa7e1e94 +K = 3a586a22809c5115f5c62b7319bdff7ca095027447bd25e2d37bef5967f0330c +Z = 8bbe17f99fef41e741ff3cf4c85ba0b1f4879f1e7226aff9a17b2aedde1c2dd76961e2d3b7cd825a81f1cdada6e52b0d + +P = b3ed36d1fd1578a14e793e632933f2b4a8e7fa095e38200fb9d68b199460e133ec4d3afde47292197ac99fc725280c59 +K = 645aa7347a107b056845da21ef5e01094e0778f9a4d6facbc31c6b4bd5d3f071 +Z = a821a5709cc166ece3f19844397b3feb83adccfc54d5f17b9056436b88c454f15bcc566ce62f30f64168909a72fb10eb + +P = ae70d90d17a88e1e9c2dd3d1768324e8a9a9b3a756103efa8f027e95edfeda583b33faeb8a890a1db7cb2951d7e5fa61 +K = 644af319789b7bd5d5c1fda4d7bc0ec33d1befbbe74e8f9a011b33f95cb0d30b +Z = 8b2f735563f866d7c80bed88435f09e755051d898bfcc1ba56fd8382c93a8153297d30e9c4779785849acc3ae11060c5 + +P = 8e307f1a6f88475425db0341e84619ea0e4feb45707b14430f4ccf745990a66cf1e15201fe306892b75a4d0d9e30fe1e +K = 4a262f7bdf21400050572145e2c17d7adc7c45f51e90cad66fc851fa5b0c3b89 +Z = 93559aaae3c6a8bd6fe23c1e7f54dc4a7dc812a487e5b83da9a9214913a306e2ba84977965c85411a0bdbf94292de1a8 + +P = b7765be2038ae11370c0e32e31353eea675f01010308d02d4688e89449e2c6c1d4050d164425e49d924b5829a4320150 +K = 47f9f7f709fb2b2cd4b8a88c733e10564d56a59a462e64de3df50073044351cb +Z = b89e451597f7503fa8113ff75e54804163ed298501aa9201b93a6f7bc42f71c57fd947d12cf6dda62be919a16fd8df7b + +P = b09e349f5028f8af490cc0065e824ff97665ea3d62154a9cacf091be8cf63291bcc2b936296b7c3c404ed6b6f9981111 +K = 31dc2ae1595f81b86d4a7a15ecd3444cba9c2e2307b252c689ca6417a93384c0 +Z = ae4e4b0344f1e4147cd675fff2257571fbb25175cf58995fe1cc0a7acf373b54d22c9be1d2a540c78747debf5ec4a827 + +P = 8a92fa5670e22337abad3c3b07bf0d4c144ef5d6c994a5d8166d51608831789142c81242ba0eeae75e9f4249d5f3c32d +K = 7084ba8b799f0349f1ca30fba62c608f29d214db59abc8865fc7b224d8af0572 +Z = 926564ee29a7f412c566133e38e8651982ac9f07ba04842617241b5e9084eb7eb3d87d968855c0dca64c92169513b799 + +P = b53582602bb535ceb639b63e10da227a34066abe31fd8933ceaa7ac3999bfaef649a1105a89b2f534aaaa30950f1c20c +K = 04055c5d94d10333cbe58bcf8351298bfd9c6a2779405442d3b800b1b7ef1960 +Z = 956ee8d842bee5e1c630a19b8f238334d07e4ce31b1494f70e4d12526d3d1c79c8f6e63a88ee275edd94d14aab14ae15 + +P = 91b9f1077c793646efae39341498190809b6984182ab66e7e32ff7ce32a2ef4b64bf42c3f4fcb685fff962810e1f03aa +K = 67c4cab55b8f0bd0ac66fe3d90a27d94a2e5fed124c8f4d93e031e9e0ac94822 +Z = b4fe7f39cac4d85fab000222af7fe644dfe27a0857b66689a500f0a1d78fdab132dabbdd1569516bc793b21c5aee9791 + +P = a4b689a666da755e1367815333361cbb9bcad11dda06658eab3efd81874400562b125938b2ce2b3a7274fcb4eeb27d1a +K = 0555abffdd2ea5a0d89b0415b9e4f70476d173c2a000ade7af155ef85ce31a2c +Z = aad4d2e2d230dab8ce3f980b32476d3ed2a2d57fa494dde4e245a67c36d7f2cc2cdfbb463228451ac72d9fe88d0a5b16 + +P = aaa4f34fce4bcad046479db781d860fb8663e0d5a08d708ba4ef81d7a8f901830595c35a55409976b49ee73b4747d961 +K = 2aa9e7ea916a56ecc2555ba13d291c3da740da62864c80e5f11562636c7637b6 +Z = 979a6dfc62f78f6817effdd72151212c6f21cf4589803b66aa787a44b95e0f52d30891a2f4648b4433ec757d44c67d74 + +P = a18af84acca44edf62e3a7682b23de10fc6b0255082ea219ccdbe63f2a217eef1d7b92c29c5269f35972d83f05835a8c +K = 4715e6124e02de3790d7782174ece625497b531adef15029cefb7322d923116e +Z = 85866d842e3df653e13dc19060ecce25d2a693699fa93f63f936d74e60c50642ea8d4195e060f4c469355b17d8445d15 + +P = 95a6ac1256f09ece0023e60d3480b13aa73e52f71155a8d4385fe58d3a42db986471dcf7475a04a8bb0cbed47780b6ab +K = 2a9f1c605e0f810746f7520e302349801d655777548b2320475863dc3b76ca65 +Z = 8fa663c2b2965b4b09d1618a697adc6acb12598e5ac85d13fa70458dadc5e8fc1ebc460bec41889a4ece73c04d5ff31c + +P = a61bcc93374a7b7d4b70123dce9c4d09c678ecec2e7373b39eb3f60aa05031f776fc68d32caecc15dfb07b27d979662c +K = 60e4f604c305732a0bf76ae320210aa8cee00b4d39ec6374bf924e7987b52a26 +Z = 87d67e4d8efd855597051f15bb9762d8b63f2dc5613d439e0d313ea4568b543e1720df3376ea0bfa8e4aef9bb5999402 + +P = ab3b52284295d4d2ad544aab89c6c9a60a64eb4e10c7fc51f488ddfc565a631c00e8dd1895fe16da26f84a354c7a87df +K = 5cc61d47b5ca5b895eae568d03fbacb5aa37803fd5e809402bfd844391ef0376 +Z = b8e08d9711ff7191adb8c553a0b4c9ff2212f2a2c623bc8542ad87a8170457a08cbd016e3605736696c8a7d182c408a3 + +P = 982bd3e16e9ed5c7dcb05fabbbc939a70244abae37188dea365bb9c05a7f11878057ff3063480494fdaa13c8fadaf42a +K = 4215c3e3980bd85e20a259b3418d97958977dbdf22cc1e679c4c1d2c8cec4c1a +Z = 84dd9f35ff5d3ca877f1209af1bac13f8c4d8af646dc79cfbaab750f00ce58a33d11c206bfc87ee6bcd2757e8f84f6bc + +P = 9746ded5cb127283802873a99b431c943caed665806e6d0851bcebbbfcd31209a9e9845ff92fc1cc64cfed906b47a42b +K = 4f627b381410d17144748b52b999c3cec05e0650a3234c6c65a43783ec756062 +Z = b2b7a488c5cbfc5c44f859c58ff7aba4c746fc920c7c839ad4120b3a476ee4630bf422db1365669c83b6bf8860ef9c00 + +P = b465073d85493f0b47ef291f42d9a07761a9ea564372397122ce3e6afd03b464496488ba6e0a198e49293c2fbd670651 +K = 5508d70b74c3abf87fd946a0a7460a08a7cbf464a7ebbec32f31d494c89752e3 +Z = 81a8165fc83c4c82df22260fe4930c2554a29be7d2dc169ed1527dd2c92179a1adf30aba14e419009293803f105e5cb4 + +P = b2bcf20a879607cba4e8654cfd206e3584eb990def469779c928c47d3ebca867e35363c040c9ba8c745de197003f41e0 +K = 6b101fd9899e1260b0455f6ad51dc3f64812dbc78f29bf1ec19bffa56e87cc6c +Z = a546506d291e4c67d8a9b4280dee124fbac0754296d341c5b30ada25c1347d4da82c9fe44dee195c212bac2c0cc10a2e + +P = b70d9ad391330756374f79c46024a19bad9683b6aa7692a7c414a8ac0564891734d873783d624474da29be573ab6ac24 +K = 0d37ff17ce749f8494ac461518d53227fe54a499ec96c870f6e6e3a261645c53 +Z = 91a037d5d6b52719b29207534097e2cc70e3d052c26e28f1b2ad73e598468e38f67376ddf523e06377b6815c49d8428c + +P = a947e274bded5f2c8ec75fc86da3b98c8d005de5b91aa328e06e382fd62b7f80af40a6f894b3939213a7db200f061503 +K = 0aa3af94aca35882d3a9c4b77888dd17b9579a459ab2d884d915bd402a06ee08 +Z = a3c9d770dc65a77a1bec3dfd8ffbdba008a07b851702e91461e075cace89e30e9d427d853455a1573806ce19827a9391 + +P = ad0b3e1bad43289b534e8b0a4a89b5717054f7fc9271a6922aa35083b754e60db37d635b7798dce94722218bca7b00ad +K = 65884a6bc9668f2573fea2e762fdba1fc95e18cb19b229982355ccb8940aed88 +Z = 89f2d7f13884a5dfc767e2805aba5aa379c4fec0616db11c1297d85dcab387bafb0458618beeeaf04c04885345a175d6 + +P = 951704f8487f96b93bdda6a9da7c7d947e0ff9f7f7d0d763a8b87052275f8535e786a1d3f2def3f7b2935908eb101f0e +K = 1702ba6139d8935730cfec654571a37c4ba259cd74a25a560ea24ddd0d04c053 +Z = b1153d9254a6234d7509ab52daa6bd966cfa5d8a8e0ff1505b76d0899f805b5b175a399903a0c56883978bdd4b4b1ea0 + +P = 909d700bb0d94743caf3e166799b317d4b6654fc323af7ae004c307ab190ae645b0a4c4b0ec5479051b2d405f3db69c5 +K = 6bad650bc2283fd91a9b341b49aff9b305b1d3e595f0884696209d86ddecc919 +Z = b2bbb2d5f5d646307bd7f956eb57f163cd03fe4762b5c496c8b6a901c118afa570d69a906c27ad105028c9a11c82f962 + +P = b3f6908a47cf753206d3ef94a4822c9596eb69bb207e1d720a992878cbf60491161f76644064c5fc383d116bcdbef122 +K = 00f0baf178af1bedfc8cc6a61a8a71e8f24589a5f3a45808d47dda7c4be2ea17 +Z = a3400b7777dda2707aee655e0d28b098bbf3d793696ae7903848e9bcf591dc9ac396dba0385c2f86c6c6df177387726f + +P = aa7fab19dcb5effff1a5d758bcb5a6cdaddb837e0398bb47d62ec225642bf4911653a8720aa4fd8884a711f44d9ef26c +K = 0a4766ef9e9cc7d13736d439bddff6ec108e9336f012bb8c8d2bce21484eb330 +Z = 8de58e81adb4faaf9b6c5511489c89752537abc511942b659eb42ee5e1038480fd2e9263b54f808a442999e3e7134dfc + diff --git a/src/tests/data/bls12_381/g2_deser.vec b/src/tests/data/bls12_381/g2_deser.vec new file mode 100644 index 00000000000..ed5556f6aba --- /dev/null +++ b/src/tests/data/bls12_381/g2_deser.vec @@ -0,0 +1,117 @@ +# Deserialization tests for BLS12-381 G2 compressed points + +[Valid] +# The identity element +P = c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# The standard generator +P = 93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 + +P = 836cf7da70984e70cd16d4b1c7bb1546c2bca921b1b5df6a8278a83cd8c57faa7c4f9b309a8fcf5d2b89b7c1b9b8473c0ae07bf3fa975dbfb89b7cf4a967b751f1edee6f10614819b4848c5793a33ddd355a4332ac61d27ff7b715961e7fa114 + +P = 989855405eb439dcc9cf7728f3f782b6830fb79e345ae01ef83045f572459d60fe6fe46defd1cd178e04ca61e3c91f150250187297529a82292bba095481a5665bfbef39a817c9478f7932dd75ac75d640c55b9a59b35b4511c41849b2657eae + +P = 987b75f1990aaa2b16ca64f4d1fcdde9a1d92a589c0c6d932845fb45c40c0c4f2e3899fd363b0332b5d365beea940ec8130818a9df55b6c7eeb6ede1349535dfc9f075f393d884526f666bc95dac1e2b873b67318e03b30824fc91943060d07e + +P = 961b236f9c4a8355913c77cd47190f280283a0c712b25e538b550a6278c921035fc05be9a6198a9ef418ef7ae2bcefb01606b330b501f0ecdf416719ec896fc76f261e63a1e197d9cb648fa01bbd4493f1f14a5716cf6c2d12655aaaaf2e3fce + +P = 82e2148f1f750b7cb2b32ef011706eeed6a089f654125327bdc6abf48ca0a96f7496deca22ad5fbfe46de969265bc401044a51f231ded8360698f6ace0b28ac4a8b8081d7c3ada62ac7152df1b36a64a7e8b98b646cc7a539fc276500f2a0a46 + +P = 9416efee77eb18677e3ef1b53c6fcc1b71e0f627b4ca05919069fcd1b0254518de08082398d9e07b86f5fc8737b844ae0319c02f42d138f075aaeb7e9f586757298132a5a39d2880d269b505a15d7026aee7c76c2ddf107d5b37c9668906b1f2 + +P = 9515008cfd967865784e450ff486bae180d1f482ccfd50c993e3fa7f97a9a4c4674be21050d9d58230534c6d249d819a014fa35af0a2883bfbe182893a1a3dc45577e303d7c3217c9828b525e8eb50d5fe6e3991814867b34e04c863ba9ce6f1 + +P = 8098956142700412c6a0d1ca239cc638a95f310f2848bc84b908f453c246a16d3160d156d70047e5f615c098818950340d7ae71c4b84cd9187b2811f715306637aa0b1236c8ef284b8bee25d98d6fa0c1f632ba4423e8e0d805dabb199630254 + +P = 92ff12baf5ee60b3c2da052b2f9a67d21aeca7da9b8728dfa03be8e3662316ca5fc18bb34506f28e30c9975ae9b80d4000ebf7c01aaed439f698ddb1a4ba1582420c035ca0af07ff2d4bc6062ca87e2905900b141beac326240dbcb55ecbcaf2 + +P = a683f9f5de4adf0cca987d3180987cc8e4c8f25047740c5574b947ac18e4d87dc34695a5bc9ede935b836e76c6844739102a6d10bc0b582ef7b9362c56b9022ca73c617ed03f8faffc612a4602c120c163377c813cf88b9cbd8062a4fa212a97 + +P = 8a62a388d3d26056d2a2536e958c3a9137585f51dd092fec276d0ecf5e243d4451100e270a824b763fa81d5b4858ca47100b6d9d2477612fd18148e4925b93c339a675c94ae2a1c2afba68cb581907287e5f94b623a272a51a5640df59bc1290 + +P = 8ce88683e74d5756e1ba03a400556fd4be86b58cf6a39d984c7b34836b74825aae07244c02667388f13659cd44b7aacc0ccfbcd6a060305517c63a4eeedb647cfe1c657409ea10919f45f2f634af73e7f181b91528a9259795c48bfac70e9f57 + +P = 8fb996cfc96e03081a2b517ea11ce71b6bee8b3059ceed3b40a6e713947ff5f0f6b5acafa76688ea08a86a5c7d704b1a03c0f7eef2133aabd37193e86d30ce5405b370577d24c6bae61f492d6896e408284c34f2fb14d61c2a6c5ea25bf80753 + +P = 8795d7678ca874adac3a145b977ae432dfc1d8d1d11be3f911b5e2afd2eb18ae5e33fcc2e0c7f0d524d782caa140fe121738a37d6e9d651015b09fbb210e34611b3feb85254651aa44422cb25d376cf3d752ea5483cd6ac4a2333538bad76e22 + +P = b12559752052c5986bd2bd8813ba90fa9db3bce62c725a68eefeb1261553c3f0d8950cc6472ca76961f360fb5be6e97a09b77f53ca9a4b06a440a5e00631918bf41f4faa282436a0793901db523e6e7b264edd33e8aee2172bf5e4d8f32191d5 + +P = a5d029375becd00f0e80b4484b0f478eaaa425c5d5b1a4bacb70157c954de2a77f6f84bd4cdcd7092d4c2460d0005af40deb577a36e6687be3b929490b3c17e00c6b75792170a3f8c416483f00210ec2196d65a3000cafe010b3284ddd80a035 + +P = 96173ac4b51501985f278dce2f267e6d85663416b91611be6566d6c7cdfe85ad25747d4b9f6b54194520afa7cc74eabd0c6c9bdb2f17328a366f298f1b79c526b15c039ca4f3a3e77755f75f3ad4164f1a264b31b2657315cc9a3e34f44bb530 + +P = 95c4a6085298170f4580fa8452a7a020fe84b0494ca1756eea5e20d522d3e50d9b03bb69f4751e52e8bd933c2a27301317dbfa01438883682938a69b90658d2916a0050f59ce1a1718fb554ff6f2cff0035c97b2a840d4502a6820c086499475 + +P = a28c271a8b017e74ab9aa053798e1e8f41031a38be6fbd4ab2a00fb63bf0a4183d923a4612ef6178c739528df33825ab03577ee14c3c8a490fd6fc4bb0a793a446f9125eea2033142d6739e6b9e7f4064686989dd8bf1e3c00a52fa2a08bd315 + +P = 8f56bab6796b5e442b6b5385a4f4f36d77d07f8524d4d87b337ee8d875f2ce1dd9f8d0f351c44596b95996fd01a464e4142e5b2b99901d0ffde5ec61718ac795bda601ffd9b419dcc8942f5e91c4d1cd24cb005ff75d7c4884128098ae336b1a + +P = a6a038a8b6306334ea89dcae97edd1ac90c9e78b0edb481c4f0b4ca6bf51e9f2e51de43729ece24cd9915ae98f7f1e6f08bafe98d9c979f5cc091f450f63ee4ac45facce224587662ea0e354bea698814650815a34b9246c2fa53d0596d41189 + +P = 94a50b1dc3e5ba223e9430a6bb9819c26cea37cb09ffdebaf982aa4360121664ca5ea75c470a81b1b6fadbabb6ad53b10283254add194cea2c962a62f40a61d18cadb3ce80c8f312db72561c8cb946608d60463f790dddce1577deeb66cbfa0c + +P = b2f325a278e71599a4f47d597bff879a2838636380797ee7d8508decf51466191a235d526c3ccb110227ce6c0831dc8a0df3cdcec49edbc3aee0650f1884e64f237e41481a43ebecb32581e6cc789a61aeb8ca1d612fc22bdd5f84f51bc5aca6 + +P = 807056d30354acc03739a9bb80acc899526aaea5288f29d99e3b7e9ab9ecd171727d8f6227dc30352358a9bc74839516025d9c4228f3ebf10e281095a20e460f57fc40addbce42dbee6d079dd6bf1aa4d39091af0c3b3e285aa74692d32475e1 + +P = a890a8f6dd74f15c9a80716420da5629ef820316481f42c734273bfa348fa06517d343f874848503187e5c7a83821ba70423a08dc6b084cea89acf5f8ed149d06ac374bfad7101ee6e54f1b5639b5d9e378b4df3982c3842ab6d89c3db8321e6 + +P = 9780b5a07d84e8871293eb6ec129cc6065f1ce6c20695a01c294f6dc19a8418f176dc5abcb60094aa8f33feeea1e9cf0053b25f5e8262befac2e128bfc5a2730e384f902ddf53e300df4467bf1b6257169f1c0e642b80c8847819de45733ea65 + +P = 976f8b4cb3f827b9851e9906a1aebe87cbdfc6d96ac6ef01944027d223feba5a0535c700b9dc4ee28dd5f436cde86d940f74300955874ae35056eaa92d3207fc4ac692cda7bd5700b5d3622f6accdacf455769e434879d2c5b604fbc42e07f06 + +P = b444bc392d970900915615d064f2f32bccb22c9e4b7c01b4e066ffd1402ab202ea9e34bcc41e9dcaf9bb2244b10a913d0eedde5cecb01a4e25bdd2defb69b60142e16b03862c38e1458f858ceeecdb95414817800bccc76c87108010e9192df0 + +P = 8fed4cd92ce89b3ad1b12e3370dc6784793f8129fcfba0be8d59bdab526364cc09b2f15ad99baadcf9bd03318e96447b15c607c05ca65825c75ad5576004d539d9ee2b3781ef87ab9f45ff799a69a7529acace8cb6ebb9292b92f1e74b0e2216 + +P = b1f7183fd0925947f044f6a1441cce31901a2fda1f651cc3123feec96b10fcec0e64571aa44fd36ad47efd4bf3428da012c568ed527383aa8c613536a200626a297cf48dfdffc37c576248897cf013149481622c98137c7a2168e7eb3f9fe781 + +[Invalid] +# All zeros (compressed flag not set) +P = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Valid point but without the compressed encoding flag +P = 0c3a937f9e4750a9f20d5198b2d88cc7759629ddeeca016c23135013afb690ba1ce665deac52435b993ed597f567b4d917bf6ee0fdb61b04f62e8b01d64c13ab74d3f3597911b8fa231781daec5228f9fdf67b7eb9aba9f259662817fa89dbce + +# Infinity flag without the compressed flag +P = 400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Infinity with the sign flag also set +P = e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Infinity with a nonzero low byte +P = c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +# Infinity with a nonzero x +P = c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +# Non-canonical x (c1 == p) +P = 9a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +# Non-canonical x (c0 == p) +P = 8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab + +# All bits set +P = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + +# x^3 + 4(u+1) is not a square +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 + +# On the curve but not in the prime order subgroup +P = a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 + +P = 800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003 + +P = a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 + +# Truncated encoding of a valid point +P = 8c3a937f9e4750a9f20d5198b2d88cc7759629ddeeca016c23135013afb690ba1ce665deac52435b993ed597f567b4d917bf6ee0fdb61b04f62e8b01d64c13ab74d3f3597911b8fa231781daec5228f9fdf67b7eb9aba9f259662817fa89db + +# Extended encoding of a valid point +P = 8c3a937f9e4750a9f20d5198b2d88cc7759629ddeeca016c23135013afb690ba1ce665deac52435b993ed597f567b4d917bf6ee0fdb61b04f62e8b01d64c13ab74d3f3597911b8fa231781daec5228f9fdf67b7eb9aba9f259662817fa89dbce00 + diff --git a/src/tests/data/bls12_381/g2_mul.vec b/src/tests/data/bls12_381/g2_mul.vec new file mode 100644 index 00000000000..2e7003707b7 --- /dev/null +++ b/src/tests/data/bls12_381/g2_mul.vec @@ -0,0 +1,202 @@ +# Scalar multiplication tests for BLS12-381 G2 + +P = af80cd458c3c3a179fe939b26bcbe856edd13b43e1d2c32735a98957128b0f4d9ea23fdb677a59d8434b7aef7126279f13d00ff3e6942f32031b95deb0c47e9d95506eed41249cf50b21fa14681330ada012c8ab07ff21973ae8510422ce3813 +K = 28edfe45d2160b2b5f34252e23382ced3514b1a746b169f54d2267ad4b4b09de +Z = 8985f9059770fe9061bdf1b7050153ddd4c4dec632fd1cdf62349a59b9a4ceab42ae5582a270a9debd6ea800df0ea67d0abd87e9ae693c438f0c2153122960391a7f3945f04fa293e92a3ce6f10d528c4b496685ed76c726fabdcb6c105be948 + +P = 98e35607f085a233ae213c938fe21bd6b743dddf8154fdef5c897d5d247a17b03cc4ac3ec28778f284ade1235314c813130c53f8edf511c9b49f50a019f231aed81253ef3ff7c50a93635d6a73917460bdd7772fb4d167df36eeaf8027f8fbbf +K = 2915e3f4b79e30f0dfb502d9bf4a3e1ed2ea81afa9c90e7022002842f384f532 +Z = 8f359d251304acda467e74248e55d7152b85d9f48b95f1d6973d0f71aee0cc0d43686a31e33c95fd053df7594c7476240d8981aa7925a7a8d3386c4768717a8cc35dd92a1d87b66c0c2a382241561b3f70e63834c59f4fd6dcb89b5e6545ec74 + +P = 973d570957bc6279549bcea04b58bc1260fa176c8ec68b156a2b436a4272b532369e808899a1fd11cb520189e04372ed13a601d4ec94ff8343f1563ebcde9ecfd8e420a12fe7b7810ba91638b02cfea2fd6e3f6324cbed8ab880ddfd81eb0908 +K = 337b546a426e27400296cc136a2b6a8d31b9e23fd12b418a07e910d6e675e9b1 +Z = ae273140c256cf1fcc17633e8c23a989f837340b3f8c5d7640c509c50b30a6caed949151b37dd000bc2d27b0604812bc04e9a4f409eb4fa61f22b0e69ac6cdc329ec965160bc84f47554825f73d2e4de8826f55e1b703d5873d8a41ff67e4133 + +P = a5564805a5eb85cfa99f446e075bd382d9a5408b76206b62cf1b48a091404d758325accda2c745272f9452539289a3990e15f2866ede58c656b98181af374a2f821e7c90c1bd488ce7b55c2bf48cdc4f3798cb3365e2bdb356f9570432ec244d +K = 045d47f37bfd6db6daa9ae7d99958218a0c7ae3bf353da919fcb6cfe5a7bf671 +Z = a5fd9830301327657df9846487193f0d7a205a79a2b9b4ff08d8469abd411a391aeb53e1cd9f21dd627690c581aec3b7123abdf51a805b4a312343e64e8493e8338b7c75221842643c044324003d8fbb4fd25cabd7bca41f3cfbde35d33fdac8 + +P = a33c051bbf6ca7c4340c87c5dd2fd506780813d4cfcff2ac161e6ebf06dee389bcef9468ec152577e3b88f6fd534af181485b977dd8bc98abcf4503a2ed4e603f31045861d7ecaa52226c2173bc1aa7127ad503b0080ccbdb2ca2f1f94d307ce +K = 32767de28ba2786fd2064d473bdcc80b947cdd78d0f4393d1570a1c17fa81e5e +Z = af8244a5d857418378f25cf0f9365f318d70f8d71dec9eb0c577564b9ef19240f2dd7c3405710a9cfa6a0cb8a32b5e0d02693aa11819711a9836502958dce8a94c1be3962094be908e4149489f93e033f3b24f29d2388bab4a1c23143ea7597a + +P = 85a0b28ddabbe88c8f96c8bc904c9c337b085734d0bf62544e0dd95849589f11f138d2c5da4ca9ce74cb23f578b7992105e987b0ab69ef8c189b77a06910456eea6376b5919be060f740331a605d2ad9856f89bf0a0b5a15d7465adac4445dcd +K = 0864671fc3407a3cd1c17f16ccf2cd5d86822c8cffd4ae94204678ce75720372 +Z = abeeb587859e5a7d7131327e4686da152aadca36a774900b15fcfba69865ae6d4d3b36465e72f8346189d9a768ad241d0d779ebeeb781430803033419c1e6e1778f6787bb4f25953fcea6f531aff86bf5a99af47e5583a4599a21c8dac16714c + +P = ab368ad36d240a615e4cef3afee37c9c6fa1a8ce543f62cc7b524575758e4c37206c3c4f0ab1266a4e7ee514624c63510773b496f53d9c7e2c7e84e96a5724d4f9be5302f20bbb59f379cd3cbb33d0760ff9276182796512789b38de2db6b3f3 +K = 737c77239ac970a82e41c05966f32eb882401ac41587100ee3a3a3da390c6151 +Z = a6a5f8bd4a4645784e1ea02d95984cf412b8923093215fd38e88d255f6f5fc683a8bf2f39637efcb9f17afc4fb43dfe517317fd4c3f2cb2531f67052e093dc0e9b0757445ce74fbc5a98729338b6187343c6e59b4ee48557d9ec23f2adc10eed + +P = 88efde8e83eef259a2caf20faa9d7f7acd25f67172bdf1afcccd1d673666a7ad72471729858c811e491d567ddca69d130bc6901307047a64e0e0a9328d054f145098071442ea5f2557b617251f8201fad7809055e7c70378781dab8b12f3458a +K = 3c22121851e21dafd201b56dedab9831b43e1921f73fa2c8a9295d69f41b93a2 +Z = 8b8d27c65d403eb6fbb8bbe01702f16b252de4e9b49bfb51b6d29b78d394f9fd2fc376bd17ed51b32c34bb4c8e9453c504f3dad1f4aa05ffc045fc88d53d1206dd1220db1c0855462d1a99f85797f0c799261084ffe64f0e618571b7104a802b + +P = 98fd91e9aafc4a85cf57e2d5bf3486349a6f2e18323126c189581291ef5db7600f9d0f1577f79b06073584ff3732923316684996c97c61cafc0c199e72a023e34e09c84014d36497c752d1f687499b00d234c6f8b6aabdd31ecebba97f45ecfe +K = 30346a6e9aa7c22a09beac2c13b217ba3032073255e7dbeb615ded8e6df54c45 +Z = 8bff0baf23a7475c22b16830f50872b8c2b4398917a24d6895fb786a2c02d86be71b65072f6ec77cac6a484e407e9e620207113a350e24e8e7aba2e58c060d0466649e831e4f3bd0bf7304e326ccca181e1c0f91cf0ad45053fce892af456cb4 + +P = b102ea15450ccfff5735e57387b7fccfd5d114f3fdbcbb42fd9f444826d64bd318bb363354eb16b3646a00de0dc4150200bac4794ec07cf5d6ad6aa04c325a302cc458a6fa2f888dc89ba19cdb474e0a2b5a9ad4b0f1cd226fd42fa3e168b334 +K = 4d97ac9f6e7d4a59da8bcf6a3a93d7016f6e58c3688c19ebd364dfb47149e71a +Z = b27709a6f1bb2e79d0e50ed8892b551b6f94d0ba07392261340f551020d18cf9110d77c64a0c737760d994488b994999109ca4056a74c2ce1a82a1dccd48f55b3909c33e4756b4eaeace6835ffc968a0e2860f92f69e27ea5e20bd22ca2cd44c + +P = a6be489eba96f6ed7e6ad711e9ad8365698d50294826978758328c5d390294322a7a253d39f6b89788e82a01d24507cf03439cd354204d44f4f9ffaec471275f8bbb9aee479d0fc31d20fdcf04620ec9e57a377dd64007e95821864e94e98374 +K = 321824f8df43a7077b43745d2fd32730f4ccfd6ccee33ea5230a985eb2444fc1 +Z = 9662c29f0578e3fe11f5939e14cede783b6dd391e6ab0b43da1bedd1d866c1e3c247e9ab9a41046de00a33836873b4be10fc52208edc90e96c028953f14ab27b1906c2f3641353d417ffb148eb541d6ada9091d57ad6ac97073bf854b424f6b6 + +P = aca1136d77820e5d0c628bb985df19c6f2c698ab388326b01c016e230c7b33f4b6f582712355241103906a776d94b189028e7f057b3cfef9ded390fcd57fbd9a417df7616f20c1a9512fcfb2a21cdf864cd37fb2203c2ca4af2023e53945cd0d +K = 6e76f041c5930674110d812db624f44078e01445507ed1c3c86cd01ab1778364 +Z = b3a8323b36ea0da3b66275ebd708e2389a8c382ff7b8fa0a340fc482b161b14b9996cafc00babd94893eaec77cde4f91089bc8ad8cf5fa34de8308803dbea626913ebb9a8ced18c9d0d5558fef051a73cbd11c40f49608b0f7e66eaa871f554c + +P = 842657caf348edb78d0072d16f6fecbd023fc755dad2df126e7fd067594e724c836dd753b69d7b86137a1950e1b9b56e0fb1e127f971d8a74e8772d6362144ac9ef7770f60f02bf69891638ff09195279bb0efe4cbad398f78d52dd20a7b9114 +K = 33b7d434fb8f1b58c90db1f1e75f2f1bbae383f8a727f460206a930ea332f08b +Z = 8f71691bb0e9c3b290db5abb305de8c2c943c6e6b847743918b456444dd020a3775bbd7f1ab3a794bad63e10efa8448b17138a070d958588d0a16a34d44a37f5ccf2e9fd163cea11ae0ce2316d6e5d50cfb80f2f32141270dc825d17d129b365 + +P = b666aa04cd89505205dd3a6c2497200f5e3eba196e26a5f5f48ac261a572d95b2fece4cb2c53c8d689228be77fe4604d162ec07d66e0295765fd172b4655a24344b558a438717411f171a0c630621d03b098616adbcc46f7d5d1621cf81a8ac6 +K = 0057fdfbedc243ee042d6fdcf43720c7f5df7cf616460dcd74ce3e8c20c86b64 +Z = a2428e115457269491dfb5895ef8dc72d103ea0b3fcd3842f946ea594860849c24c33177fa150b94bd997417480a83800f77e9ba591e64bd07889c29130b48468062eef65289ad99a5e45a68061270c3f42c4b9e68475541078755ad3169da93 + +P = 84ad087c2862061329367eb5284863bc5f26543ea207e221cb1ba4754ce3fe1c33081189022eedaab8c54b08955a7eff021b9f52bc45727c5ca4fc494e420d3115309c6f4ac3c589c146a31ffc97eef217ceeedcdcccb2e49a2ca8246be4dd15 +K = 1ce3a420463a745bf6965ee85904fc2f4492bb0c02cc3ca369c4478e96a7f5dc +Z = a798c80130c2061d7f0bc38ae07bf6d4f159471e8085d782fad8007181dc2556f16dc4363cc55e436936cc66f60d02b91783d83561775405c551c092a2eac3b97a8e3df0161eaa652461d374f114df7039db4808b1a096c3caa0db55a2c2f65b + +P = 94fb32a128159d820ba21bdb84246f12c2c1affa32a1b5f7f4a5a1a3ff1031effc0d1d17070b87a0dd577478d82f8b2a14ba0af3f3728d4182d2db9077bf7dfe29817ecde2a1f3d10c601d96a8d3f15a824f8028f661790c70da70825f182361 +K = 0721912fecbbdd444faed7905d79d52a8c5f4b4714fbe7b52e93e95825559953 +Z = 9962e7a1c2096c3a442bfdb8085098b2dcc3113fc0b146644519fd50f264a8a8cc2615cbc486edacfa3bfb96fd6225080e9c7c8b034ffd0c42d96a351ecdba100022f919e9fa3f5dcdd1216e11058c82418ea4d2dc195935f1c553faf59558ab + +P = 9722f83953c74b4bd56c6b813120cbab5f1bfcea3f48c35f0aff9072a40b3aaa1956a8ce3faf160cbdbcabda427d9465136ae5945f015ff9b20be4491b9c4b0f4997af97f6f596aa7764196e9ea90bfd8a316eb3741d9bb9fa801534c596133c +K = 1f5d7f2311c159ee24ea58d82dffaa028ce1f7d8ed047e4b2d30ad4b74cf6280 +Z = 809be9cb3d6e4c49e3ca4dd20a6ef83902b19390748aed43146bc93e5fb880382c3e43f2c241b69695e700925855c97905ab6a7f7568d76c8204770c18a79461a18293ceaab1a36653b622e8f0f373d0675715cb083906da09b00de5378a6762 + +P = 95249d2ee885b6d7f0237a28091c5be6b4c2aac31dd028ef4e5a0556dae8a6070183398f601e0673b74bb804f531370d102e363bd202f26b4e4030ee2fa475296cdee1e2c6796bf1f236acc82a64aea1fb37571935861ac8c58c61c6eaaf3e16 +K = 149032825449914a963dcc6f6e4e4d05d86042006080f1759e42a82184b5684e +Z = 99fb1f67eb87bc57a30c0bcf6d965609ff3a8122b95a39356c0423cfc0a65d8981b41aa60902094ddbb51a7d6848851a16f63a0b856998df444c98b02814c901926381779d058630a467c123f96fa02aa8c751fecdcc1879a7439520e09d32eb + +P = 8eee5c8ba99784f37dc879b7d47ee6622c0e73b26c7ad60b44b078dd5717ff655fcd4147bbefbc4319c6714fbc63941b183201e64fa922335b93687dcd7a21d1bcca8b9ff6fc566ce7cc34a7caccd86b36bfb7a4ed88291dc120207d4d8d5927 +K = 60905b64db898312974fd22f6a75cbc3e0eaf7d893fa659c82f93ab24be38ce7 +Z = a46930c27bc34bb36083e1f90ebebb4e8385363ef64414deccd6fa653f36745e631f9a8209ae89fe2962ce28a0b22b4f082c8f80aac8c1b5f61cd27bad21b3943f6f27616d1e5aa18e90b98f864e502099a64f07bf2764f43063fec43e54b6d5 + +P = 8fe0dc69a816a10fea9511c2f254bd2418822d94f0ad4a7991da738b3d566f0ec8e7f18ebb67951624198a1ffdb8373f03ca6290802263947f69682de1ea4b2284c4380ccb14aed299e41059e357760035ccc019d30c8fd8e660965cc9006cf5 +K = 6220de3f2ce9dab88392dbc2b9e14f5bb9a26fc0c982e63ac43633b0ee3ad697 +Z = 833995e0f9cbf3809ec838339781c3a9371c4ab183fbd0b345285390ca057836b71377243b9733a0c6ad1dd883a23b1e197b1aaf37d25f51511d9fed0df5c83cc8279332f30ae6ae0919931e4daea566ad36f1c539b4a3ca46c44545b78d59d3 + +P = b27a948907b8118ea55b5a3de8eaf32a329709d44ae136f1fe84cdea0fef4d3c3a36adbfec7a7106e637d2602d6e374201435568911fe460eb9c5d15c6c69e1236663975aa1185318cff225db12418022fd792782c9a98315d7311c9e7373e34 +K = 4c40b8d67ed51e7addfabee9f6037e6f90cad5e0d77c441997fae58fc850cf44 +Z = 974e3cc88671eca989eafe761f323212305f5991c96f0922cd1f2d4123d49a72e92c965e86ca93742cda4f7f60b25642067635e4997a73c893157af2106f9f9fd41056b6811ea3680e61f6f53f7e7424c697ea6e5e300ca300fdd76f7339599d + +P = 90815dfdfb3ab52af96cc8e31613cbbaa2271587d41f662da507807048a60b9ef2ae96128132efe9d70d72e75e8994ad0a6c66a1898c2556371dcb8149e0f2259b776ce4da3d13623468a32bc910363a4235033aa4b5dfe4002f055726ae6480 +K = 1d8fad9d614a7dab75e9b10ccee098c7ae19572b42ea48956b48aeb9781160b7 +Z = 956c8885a924d43b5f0b76d1318bfe48a5896ae6795dc6ac0131c206dc39bf1d12fca868ba92ae0a0d84e56ec34f74ca05f8fa2e546c1a8a32ffe2beb3d39e49f1807316c960a5c3ad6889740b41d0d1a90a277219a72cb80ed8e6802971c166 + +P = 807e3e5ac7c3895780ea80e11fda12495a20d392557439cba3dfc7ba403ecceff3ca423b237d5718e7c6c9e8ef4af52f05f13b2cd689e31b2145e931b944fc66980fcf4c41c10f51a7a79ae84bf88537630a21e9ebd5e007200a36565e9f1133 +K = 1ba3c26ad6f665b45a8d45a1c736b39b758d5657cc121da9cf4ca49916579081 +Z = 8b237f5b5a866cedaae637aa0dc7adf1ffafc9baa1b42b0739c8c0f097cb1e0bc618c99330aade8eed174375979f05e404f8ce6b5f87ede1354991c3b874c50168fe1d44072e0c156fe3c8498b37060d5e0ad3f6481b5b5b1eb0fcc2ea12e2dc + +P = b17fb065cf2ad6fc76a454dab039c75d07209a9ee0eeaea854e86b2f54d3de9f32d8bc180ada59c2f3b8cc5249fd086815c70aee7c3788006953ca98883b44a85834198a7ff87706df46af8aeb15bb5054daf68e0a33e64bb8019d74ed3ce5fd +K = 1ed4c7728a1eb09df6726e4d57a19e27a72496b54ed568257d42f923f9f1aa68 +Z = a0fa750d5ff2e67c0e4ee292f549cfe1cf4ca184d32779c4917f422f266b8be62f4e8178523e0094eaa8127983d9287610810af29a4c572560611a634667506e7406cea31a86dd90eabae02023488db7dc6dad1452ca3729963533a067e4c347 + +P = 9250780c71104a9088bce1811800a2604105002d6a55a280d815eeebd63bbc9ff7609f5b4f712513349c78b8e6b6d029121e33c8d0c49b9840e93a7410552d227c7eea334954cc8c5e4514f78a03bb3fee3bbf1090540ac9535b401409e55037 +K = 44e1e479d91750ca9fedd3f18a4cdc1b4cefdb80902e23df704821d84518144a +Z = ae24696f72d7330709772953db30d4d06fa361bb6e57cac3b523378da6c080d11a40a81758bc777d5d94930e22a0dff8188cd12d7cb03805706b8e2eda2029c931be4deedc1467ac3703f85922ff86e1357207edeeaae06dd4375f0fce53d0e6 + +P = aa0bee731a3b9123c718ab6b4cb12472424c0ddae75958fecdd144dbb2a49d2d2891a95b2f06793c0ca8fe554968365b0f5be9d3520cd587531afe77b4ec7661c92e89f33705ac08807961280f070069c5d6faf0e1d9fb47db4afc8bc8d23d8e +K = 054cdd7f0af2272e3b6ac4744234e321a8ef51f641c9d3eac8b59ab43cade657 +Z = 8a41b6f389439c19d4c255eb5d0a318a57f5d846f717b97b87dcb8d456380fca2fbfd5492f033ffe1747f4088d0571191939060ca033fb07c33e6639a7c72af27f250935e3b5f897514dc61be4d0b109d05d0289257d80fb2682063b4faeb8ed + +P = 827161d6f4d72f150d357d59b8bf2e73643692b6e7fc376bfe4680aa9e531a429d6f150cb8e1717c30185e4ff1cfcdb00f269f936135d3674751a077bef8f0f5b017d47638be5c494f7e2495e2febe3ee7d0d86691cf62408dcf3e901323f9dc +K = 0f2895105a643d0f54aab47855ce3b144ad1922e14eae1f51b4cc3fcf56597ef +Z = a782d87924c5a88fdc946f29daf3c92245fb9932f6d3612898850817a5f6a5fb648525869d0515c261d3ddfc0b2d4c850341bc8c451b33635a98d632450f78b751a7f1426612f969122350630471545adfa83373fd3c713a1e31eb20007481e4 + +P = b1eb238414e0c4d51b3b55f5dc4be180399ce8dcd3880e1449b42171818285a75f53f95d3c76b5e9f801ae9e1c949ddc061caf96aab54362f0a1c48640d9b69a24e2b06ce1fec1fdb5c39fa2d0be305cbc0f5f9e5f0d4dbcd13f0b904d2f166a +K = 2a219a116f70d5013f8429a6b30a6517a3137b91a871288de0712f9d7aaef80f +Z = 82c22a01e0272a6725607972fa017a0360409ea3cc6ea7a7823dbe5f01266071648976c97d6ff19aa4c86b69103411ae0c4e3caf65e84b9d634654516a62c5cbafa8794291e5c13097ffb2f25899af664ab021ad5873d8190aac1c2eca633052 + +P = 89e2eb2fb920ae249536a0ffb370c0decf6a387326be63844c25f4509ed2fe90a6c4a7afaf89be0ecb4f8226692ad91f170320fc98c38e787809d1eefbdcaeddc9530cd9ea811d2570fc595a31043daf3ffdc01fa777d8e3ae1418e46fbbb5cb +K = 45869795015962e5df3abc5cac4c66d0eec9c2eb39f9cd363136bb6d1292bcf4 +Z = b92997a193d199f56bccaaf8b2379ba7e69c795b8d979f40ec840df33295d56cffc500169d9dc5fc03a28d7550c66a45120ccae07023fd5f41e162ed50f6b2588606adeaeb00a9230c3aff661ef62aeb8e2ae4008b26ca25fba9117d93fa6eb4 + +P = 8cf8f94d846407533e6c74e24f1b6a64864b0fba166a8fd6e770deadfd36ba7bf5315e42bd504069aa3e06a24c118884076eb7305d525b0b85224fc1d2a18d3dec8c871764808d939e179b1c4ea9dc6107486c3bdaecbd4f7765148bdcff26fe +K = 547b6dd8d8e237c0a2fae1bd0da45129f73bcbb29c1d1677f1559fa1ff309d57 +Z = 86ba9e81145f45f130f58bfda93a80d6bdc22d8a63dd1b53a377fefce8c0ff886fb796a2c6e202c0c007269b1ddaa19b12ed6f4fab2d1c13753bcea5f3ceba8f419f09ec64b5603b8dc0145e06b34ac29ef279041d9a1c0b792f1d56ba503fea + +P = b918dedbe9a2691a9a60557cedf91128b3b6ebeb898250de190a2d8437d76648321beec6d0a25ef6c38ca578a8bb207d09464f1858f199b6497e075d752b5948fc711e0f71a0048dcbf1b4c803a1697f031c7818ddb298280a1f07c66bc943e7 +K = 3caec5fff82abc83aabc3474e4134013068992d5d1859bb93b901f98139774aa +Z = a1801ef28964fe682514791aaf91c423338a967a90b6b3d2904e3381f3c5e7b7d4b563d765f3f4df02e74c82846ee0ee19c65a4a7a0a10b5068f04eb68bc3f50d76e4dd21204962e724bc5324c526d028bf8637e6630aeeb3c8f348357ccb14d + +P = ae7225255b722d6faf6cee480535b942d7972a018f33d670b11ec9f83b9e77009509162bf5836e5a3bf5a840e895118f11c0ab2c653d883a37d7caf0e59e0ddb187ee1a120efa65b2a77efa94421abef9a4c801727bd36a13ddfa3da4d640597 +K = 310403abbb779471e2d4e469d20729db03dd0b67df0696d836cbe453c28caa17 +Z = b91f6820ea24301e8846d589c56adafa211b47469b6f0075159e105bccd13fa4b1c89749c561e1b01da6bdf2231f9be70f76944f26a937b4ee20fc2269fe5b355c2b90dc3544e6bfdb99c9d3252b30e568534aa4c512c482a9d6564f27e091a9 + +P = 86578ee64bf177befcfbd5a34e36186cee6ea2456fb01b104211a2a980e567efbddef0761ca2ee008f585757fdf2913e140ff0b46a7d21f1bd1686eb5eb25f6e1d5a1d0fe16cf378866a0cc378e2162113efc306a5f6d05d2e89e83fece9fe94 +K = 72e306b21b7d35493e8107fe22d5564d9d800c2fc19412a5d47cc59f451a47f2 +Z = 89779b2a3b4e532a5802e58229480d99f91d2bbcb498de8aede21b1bb5364b02c88604bd310444f560cc72e49cada82e121f8883151e3b4172e9c9c6602ded8c69f7bcef37d8001cdf3f54f51c2d1f8cdd2ef316efbfdbbb81a2f94aad628d8b + +P = b59b88fe2d935f0248fcedf663dcdc64099644560258680fd2c54e1d42c2b103aa06245968471d7b8f82d5f12ebbe1a50ac78142522ce627aedcfd0793b73d923f23516317cae83b259cfa9bb1e91184d231a3e312080e5934b63da0dd7b7d27 +K = 3ec6f5c43873f8f512803dc597525eff3177bdde302f0d154b115e40627c2810 +Z = 89d025290fdeb0c2e2b13c1e74cbf3c7cfe5b04e7dc1020170c1e235c57a2ad6ad3d5e9d3713f632029f4ab49ac99dff146a63714a98f4d9a9bfb8ad0d701c5afc2c6c8e56208479adf8f84b24b26f6ed780b778ffd3cccaabb0c8b3fd6ff7de + +P = b1f4a7c60a41eb0b381b5d9eee262cfa5bd33b98069dd81e68bd8107cdd87f92a06aeffdb6e30e7ef9df0babff13b275142a8a37ea143b1483f6c573cbc04db5f00e4f5d2bfc39a250f6c72b3450a27d38a31927f2f161b86c25645d230bb1f4 +K = 16d946ca539c745308e301335eef4420811ce29cfbc65673283d7a3e7ed05152 +Z = 91dac7cbca605bc4cb850594698f004efce99f23330e85fe6a95910e0839cee9b30e661b1fc65ac5932cf8f94d5f1ef910fdc9bc3db04179e76ed6f37cc48a548fa41e3db9bf702ec879a8d65f3218fe84154481d3e332df96465a4abe8e8aa1 + +P = b1981f98a3030a26db667c4a9b12a52c0b895cfbc0ed918f72d1d13e2ac409908f1beaa0abd83b702c7ebe51d1c35f8c06ec6ab12ad2ec373b773088c92d4ad4480e652614e5287a2fc280be0927e451e505d389814e66b943af59377463de87 +K = 2ff8d1818d23a3e724448af66ce3be829a88c808f7b670edf36e157faf58944e +Z = b910a6bba0c6ec270cdc792e17596a93e5e7e9a512b81a52149b2c04b8e39fb161b182e38afa405de8d1b456a3100a1e18cca6433beb40a78351e99956ba1ac04578728cf890207907d9d4dd740ddb223ab0dd36cdfbbfd9e0eafbccaf1d18f4 + +P = 9066b4be3d4465d9d593f666b76aac6a38c09ba1b7e42ffe90949c59d612c8683f5e15551e4dc04a3341a2bd47b55f8e15c8a3da9e5fcadec459910a695858f8650850e930ac56259876b192df2ce665f53c08050e7c1572277897126982db86 +K = 03b467c5c7780ef12c040f687e37e48565213f2c7d32c43074915addfdcbe1ed +Z = 862da244344f7bf9386f6aa7ee7cc3f495bd3ab50e75e67d48c8bf96292b50b331935d56778b909cfcd15ef7556ac0e90be136fd4b23bd926fb08dac1f18f59ec67ce53936591bfbbdf34d42d6ba88abb093b2b6da555b22481976d197e25208 + +P = 92212b75cdb1d2e1d19b2faaac3f5049cafc5d4dde65897255da210e740360c5840c5d54b36c5dfa04a320638490f51d007d96e54db0566bd06cea94c292d7367b7c66ab26f452cdcef63d4de12c6753bed7bd0e543a029ee31eeb1b835f6aee +K = 120d51bacc1ae78a84de50b48eb21fd7e64bd30551f14e2c1733a41a9221947b +Z = 96180cf58fadcbb68514a4ee39bc65266bdfff91775ff28154b017f2099102385b8c1f9a81cb7bd64c15f1f6cb5434ed02dd5b6f8073b3aca4d803ebfc07f7a62f93c8b454e0d05743528288dc635e40bab209570af763702a9b274461ecaf95 + +P = 8131fe0dc95476c4a3b2d322f088aa5296a21f0a44520efec3557be0f7e3b67fb18c8b85400e63133a0b2661fa074b380cf3c6c97fc6046b19bfcc649a5727fcd82738139187d24c5389361aa036fb9c6beedc7ab22642cf3c2d758f3f352c35 +K = 3bdc126305853e9478d1b6e708627d1bceb1a3376b0674da8482947f56fc3cc2 +Z = 80a5c11459f69e6e06b016d558b551858f24f73636c1772e60187a7e1f6aa4184ec0391de496cad69e46bbee8f4bf41903ea47b6af3da85d754a75a371c384d66eb7c69dc0cece36084bd376eb790856c7606970dc92819c6ccf35526193b97f + +P = 8b534642c45bbb62b7900b82668bfc6421ab70a46824f0dd0f26b3c5625801cc3f07064a916147bf166ce06d2f2575440195888226a71b447c15b730bc0e633186de90f84d76eefae5c6603b5021f8541e6fd879551ba6d973736e495b97fd60 +K = 372641db3885a3f2195de981ee97aa765594677de6d14089a10fabaa81a25da8 +Z = 85d79d1387864dc62905d58ebc166e4818f2ed608db4ba06d699714aa39863b628ddda7b5397edbdd825961454ab5f5816b0618564e3b4509201486c5791ce3c716dc21421c625594dce7f9b9913201b52688d8f48c3cf8fbff8d13001fe4e9a + +P = a34f03a03ef82ea868cfb4ec44f5048e5bd07bf376085760deb9d73009dbf0dafb728008e7075beb00a59d936c8d40aa039c699ffb93aad77670ac6134e8c8786f31d087b2bb31c2df30c6c4636ea874f87bff0b2838e20a13128aeda37e1c57 +K = 10722d5783985174741cb3fee1124fbdd7b734b12a6ce73d4a9c9ff7a5e4f528 +Z = b0901a8061066e08b75fa3c5583d603aedab35720fe4f6a242e52c10fc6a6d50fdaa36ca926b3f5f4cac7da587c43da007af08e2629ee522589ee972e453b5a56d7bbfe347740b26f92e0b6797be7668b76d055538b2e93db122cd04b7336c44 + +P = 987094a5a3abf8d9023741c70c8a5a496b0d4f0116b44cbd31e287695e924b6365e3ddaa6df72410716b4935c40f81970460764835bc0db663443211012af1cba45699237a1099f6c43a7b9cd5ab5ff280bcc9ff597adb95d9aac862a1d332bc +K = 5c7cc87150c50bf0e22324f4fa39667d48b3955bc1e033b6a34310b54bdb0c3c +Z = ac076b1bb0a10cfd65bbe1d5aac88778244428d10c64dcd53b2ca04f20356b1c41e013d8c1476b40ec66dcfe9e2105430cd380f7c526feeae2fa13b649dd275de03ccf460546e9af141f89e68681b19eb02f8eaddf4f22cc2051ee38e04932ad + +P = 8afed747dd9e9f408def0b84806e3fec30711e3d2d975e0dd7a74b02405fd969ff98499a9d67c0b5bb47740ff8149b3110a75516d1934ec0a1c198ff8b646b88196ae0cc11e35133ae8ff7966a9ea6f33035671027cf60ae287280b3fc6805a1 +K = 54b615a99327e241d7cfc86d96895aa644956929e1eec8b74995ff91c068c44d +Z = a4af92273e77f35adb886b17e461f45ae576a9342b35bfc36cb1045155ad5ad19cd15668b540e5bf97aef1cf7f8f8b180b3a17ce7d3877ecf571d1c460bbb5c60f271d7124c80603e611ae5d18f054783c6ff5dc817a90cdd9174d2e83a97453 + +P = aa003701409a61c2fdebaf39e3dba257021cc435fe33ba0724a833172b039cf14eafa1139c9714cc666adc7b5c464b0c0dea20a1374c36945408c66c8493d5afe2e4dfaaa6f11a99731af4cf112784a881bbd5e5535f8001559e45470e82e563 +K = 0eaa0bcd982655679293da572d9183ae7a291ee20dce2d0f3761083957b65f0c +Z = b9fc652700e6ea6a52a51a1ad6b095f6a6a1ff4aa6b5531b4ff6f284b9b011a1c530a6a30319d1119c6fb6fde23899210288b98ace3d83b4d1974e6ced963e5030a855aaf5c72027e53b903742c548e3ed5b4fee36f8b9311b1ae12fb6eb7404 + +P = 95da0359dca1631c912911618e1153614811db7e409441e8f21500f2cb0b23cf14c977c35f3fb6c50bc8c6fa87aa5291141b6d51a8ea5944e4da153bccc9b31f35955db1a0fd59dbaf9aadc954b71422c4fb0e776657d3273c4c06af6654943d +K = 519e995468ba661d5b8d457b34e683392798cf7820a00b0e3bd3bcc038bcd71d +Z = b480e1a22b3d592b22f5eeb22707fed78c322f9f6e7d49f9f7ce6f4e39b027aed0672f570f66072601b3e25af7fb90210430c87ae787a4f6f3aedc828010ab16d17f9c819248cd767ac01e1e2b85336c2c24ed56cdb2e0334b4d7e354ad49111 + +P = a6efa22b88122162fe78f98950a09d3f7f160180cd50644ec993c16264e6b976babf8d1fcb72c6f605f19b7425af3c2e003bbc804850f473f5ff773f2ac9ad9edc4f9f207ab6b49626994aa6022e5440d414ab1202bef475bc56eaef706c0cb4 +K = 07b22997f4b8d49bcbc3c953d9b34a08035726b9e2aeb1acaef22dcd504b7a86 +Z = a9471637a5e164919d3c0b871543a00118df7be423fbc2080efb98e8a92621f7390fb2cbdcc2038ef97a7246f0abbd190866cd7859375c45d83b9810eddfc2f5d2fd6e7635f550b6a65e002c0610537e97f9a7ce7ccc6e0d946e3d80bcca03f5 + +P = 958e112d5fbf2c15924e97e6570a8a4b943dfbf43a61b6e83f7466ecaa6c5b65ee85ebb85b35f37cf1b2979556b5d82816d31ef8743ee4b2c1e84344c400aead4c6340076f56789ec0a63d674a212973ccaa3611bdac118b432dd18c5e30723d +K = 34ba124f9cad735310fdb7dbeb5befb17a222a0c1aa053537198b5a7c5ec64e0 +Z = 8f17115e4b62f32df6a506ad622ce16d06df614149c4c52de7d1abb22a10d8b836b1babc542003b6faff894e3867c89b03f3d34a394de349b9f7fc32563fa389edb017feff0c79bffec287dd7c7652a9bb6e0caa796924165b798341d4ce422b + +P = b2d5fc2fd37d6cd6c2a03743439bc6277989c2840547436bdf5322efce0bbe92933c4526e57f521f063a9fc3d6cd876f0e0d3a37f11c2bcfadc2c9a1db4066288f9ee9444c2023d2c211c8e89c2156c8e5e43d1775adf4bbe82137226d8068bc +K = 3b8041a0ba4160f81f598068436813701340f00e7342771ec326479327fd5bb5 +Z = 805f5617bbae115f7e491871678fa1897ad48cae2f5441e9cd23634b6e3a007457730aa629aa231d0b4ca9b1e4b1174a096baf535e244e7944a4a82dfd18c2269af92ace69bf5c7b78fdc61fb595ec19fd9607edb7b06acc8de3cf0f83a1d673 + +P = a5091f045b51d7237e2535a39bdeabf6754f07407e314dab437fd748665fab3e748caccf6061102bcf5b5d27520549870cbe05ec8ab610d0a6ca78abdcc5352323bbd5b6127650a6d8bc42323c3e28d824afa98ccd91e16aae417eeb4071dcf2 +K = 57aee0316f23b41c0801c815bba9e13cb3ce23d6b7221f96c1bdc31d03de8b9b +Z = 8950af093b5c5fda9013c5ecf4ebc5a91e6355c3952cfefa8b48d907cc56829702695ef9776e6ff03eb5e8ace5785b41167e1cacc10ed00f5674f207e2e78d39d973ba247e550faeb2e946206d278fa3ad1bcc6028c35c78406167f858b4a1e6 + +P = a440d802d184b4f7dc7db1766803abe81612830f10b1156e601038d402cc4ce01a737f64de44ddd3720a4f0d2061c1030ceb38f549629f9f820efd089103d365b2b182e0ab0f1e61de35b6785f719ed4bf2975497e06de28a1bf000c5be9db10 +K = 6c14042f41e31b1a75caae2cbd033751b5e4124830a0f7b013d42af48619acba +Z = b762ce4c853bfbca24b777f469450a4a4e77e5712af1aadb84bd7659f99bd1054e4ab96e0ef42665ca0de96e0ca5a8a11058e98ef3eab45f72ee5b2e9b794610ad1ac63ccbcb7babad746cb591ad0fe85fda84fd6a8900d608fc186929a96782 + diff --git a/src/tests/data/bls12_381/h2c_g1.vec b/src/tests/data/bls12_381/h2c_g1.vec new file mode 100644 index 00000000000..50db2ed3c11 --- /dev/null +++ b/src/tests/data/bls12_381/h2c_g1.vec @@ -0,0 +1,56 @@ +# RFC 9380 hash-to-curve test vectors for BLS12-381 G1 +# Converted from appendix J.9 by gen_bls12_381_tests.py + +[RO] + +Msg = +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f524f5f +PX = 052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a1 +PY = 08ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265 + +Msg = 616263 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f524f5f +PX = 03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f6903 +PY = 0b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d + +Msg = 61626364656630313233343536373839 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f524f5f +PX = 11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d98 +PY = 03a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709 + +Msg = 713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f524f5f +PX = 15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac488 +PY = 1807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38 + +Msg = 613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f524f5f +PX = 082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe +PY = 05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8 + +[NU] + +Msg = +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f4e555f +PX = 184bb665c37ff561a89ec2122dd343f20e0f4cbcaec84e3c3052ea81d1834e192c426074b02ed3dca4e7676ce4ce48ba +PY = 04407b8d35af4dacc809927071fc0405218f1401a6d15af775810e4e460064bcc9468beeba82fdc751be70476c888bf3 + +Msg = 616263 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f4e555f +PX = 009769f3ab59bfd551d53a5f846b9984c59b97d6842b20a2c565baa167945e3d026a3755b6345df8ec7e6acb6868ae6d +PY = 1532c00cf61aa3d0ce3e5aa20c3b531a2abd2c770a790a2613818303c6b830ffc0ecf6c357af3317b9575c567f11cd2c + +Msg = 61626364656630313233343536373839 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f4e555f +PX = 1974dbb8e6b5d20b84df7e625e2fbfecb2cdb5f77d5eae5fb2955e5ce7313cae8364bc2fff520a6c25619739c6bdcb6a +PY = 15f9897e11c6441eaa676de141c8d83c37aab8667173cbe1dfd6de74d11861b961dccebcd9d289ac633455dfcc7013a3 + +Msg = 713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f4e555f +PX = 0a7a047c4a8397b3446450642c2ac64d7239b61872c9ae7a59707a8f4f950f101e766afe58223b3bff3a19a7f754027c +PY = 1383aebba1e4327ccff7cf9912bda0dbc77de048b71ef8c8a81111d71dc33c5e3aa6edee9cf6f5fe525d50cc50b77cc9 + +Msg = 613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147315f584d443a5348412d3235365f535357555f4e555f +PX = 0e7a16a975904f131682edbb03d9560d3e48214c9986bd50417a77108d13dc957500edf96462a3d01e62dc6cd468ef11 +PY = 0ae89e677711d05c30a48d6d75e76ca9fb70fe06c6dd6ff988683d89ccde29ac7d46c53bb97a59b1901abf1db66052db diff --git a/src/tests/data/bls12_381/h2c_g2.vec b/src/tests/data/bls12_381/h2c_g2.vec new file mode 100644 index 00000000000..b74cdbff296 --- /dev/null +++ b/src/tests/data/bls12_381/h2c_g2.vec @@ -0,0 +1,76 @@ +# RFC 9380 hash-to-curve test vectors for BLS12-381 G2 +# Converted from appendix J.10 by gen_bls12_381_tests.py + +[RO] + +Msg = +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f524f5f +PX0 = 0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a +PX1 = 05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d +PY0 = 0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92 +PY1 = 12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6 + +Msg = 616263 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f524f5f +PX0 = 02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6 +PX1 = 139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8 +PY0 = 1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48 +PY1 = 00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16 + +Msg = 61626364656630313233343536373839 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f524f5f +PX0 = 121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0 +PX1 = 190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c +PY0 = 05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8 +PY1 = 0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be + +Msg = 713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f524f5f +PX0 = 19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da +PX1 = 0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91 +PY0 = 14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192 +PY1 = 09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662 + +Msg = 613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f524f5f +PX0 = 01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534 +PX1 = 11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569 +PY0 = 0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e +PY1 = 03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52 + +[NU] + +Msg = +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f4e555f +PX0 = 00e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb7 +PX1 = 126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b +PY0 = 0caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42 +PY1 = 1498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d + +Msg = 616263 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f4e555f +PX0 = 108ed59fd9fae381abfd1d6bce2fd2fa220990f0f837fa30e0f27914ed6e1454db0d1ee957b219f61da6ff8be0d6441f +PX1 = 0296238ea82c6d4adb3c838ee3cb2346049c90b96d602d7bb1b469b905c9228be25c627bffee872def773d5b2a2eb57d +PY0 = 033f90f6057aadacae7963b0a0b379dd46750c1c94a6357c99b65f63b79e321ff50fe3053330911c56b6ceea08fee656 +PY1 = 153606c417e59fb331b7ae6bce4fbf7c5190c33ce9402b5ebe2b70e44fca614f3f1382a3625ed5493843d0b0a652fc3f + +Msg = 61626364656630313233343536373839 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f4e555f +PX0 = 038af300ef34c7759a6caaa4e69363cafeed218a1f207e93b2c70d91a1263d375d6730bd6b6509dcac3ba5b567e85bf3 +PX1 = 0da75be60fb6aa0e9e3143e40c42796edf15685cafe0279afd2a67c3dff1c82341f17effd402e4f1af240ea90f4b659b +PY0 = 19b148cbdf163cf0894f29660d2e7bfb2b68e37d54cc83fd4e6e62c020eaa48709302ef8e746736c0e19342cc1ce3df4 +PY1 = 0492f4fed741b073e5a82580f7c663f9b79e036b70ab3e51162359cec4e77c78086fe879b65ca7a47d34374c8315ac5e + +Msg = 713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f4e555f +PX0 = 0c5ae723be00e6c3f0efe184fdc0702b64588fe77dda152ab13099a3bacd3876767fa7bbad6d6fd90b3642e902b208f9 +PX1 = 12c8c05c1d5fc7bfa847f4d7d81e294e66b9a78bc9953990c358945e1f042eedafce608b67fdd3ab0cb2e6e263b9b1ad +PY0 = 04e77ddb3ede41b5ec4396b7421dd916efc68a358a0d7425bddd253547f2fb4830522358491827265dfc5bcc1928a569 +PY1 = 11c624c56dbe154d759d021eec60fab3d8b852395a89de497e48504366feedd4662d023af447d66926a28076813dd646 + +Msg = 613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 +Dst = 515555582d5630312d435330322d776974682d424c53313233383147325f584d443a5348412d3235365f535357555f4e555f +PX0 = 0ea4e7c33d43e17cc516a72f76437c4bf81d8f4eac69ac355d3bf9b71b8138d55dc10fd458be115afa798b55dac34be1 +PX1 = 1565c2f625032d232f13121d3cfb476f45275c303a037faa255f9da62000c2c864ea881e2bcddd111edc4a3c0da3e88d +PY0 = 043b6f5fe4e52c839148dc66f2b3751e69a0f6ebb3d056d6465d50d4108543ecd956e10fa1640dfd9bc0030cc2558d28 +PY1 = 0f8991d2a1ad662e7b6f58ab787947f1fa607fce12dde171bc17903b012091b657e15333e11701edcf5b63ba2a561247 diff --git a/src/tests/data/bls12_381/h2scalar.vec b/src/tests/data/bls12_381/h2scalar.vec new file mode 100644 index 00000000000..7cdce1eb804 --- /dev/null +++ b/src/tests/data/bls12_381/h2scalar.vec @@ -0,0 +1,73 @@ + +Msg = +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 3b3fdf74b194c0a0f683d67a312a4e72d663d74b8478dc7b56be41e0ce11caa1 + +Msg = 616263 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 47e7a8839695a3df27f202cf71e295a8554b47cef75c1e316b1865317720e188 + +Msg = 61626364656630313233343536373839 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 3dff572f262e702f2ee8fb79b70e3225f5ee543a389eea2e58eec7b2bfd6afeb + +Msg = 7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 4687d059f5ae697e878152f63ebdbf38c6e7353806aeae194d44b71ec5be9b4a + +Msg = f2 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 6942d1dc08f42d2bead2109eb5a4b4afef7f66b937af9e60d1b8482952ac6cc4 + +Msg = 6d1095c0ab54f79b0e14ce3b806ed21a797e4a18869455c4273ebf57ce5048 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 5ad21e8dd7816e3da7ad9861b0d83b44c43da101320a57180e31787ece7ee9c0 + +Msg = 123483476c80062e9d6dda0d737341732bbe13aef4ab05f0f10a48c18dd84c2f +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 4850f75bd5a574e0e5337f2b16c2aaf9794584c17c9d62b274f6a5d7df9309ea + +Msg = 266ddc552a2b72348a2d9399f0a6c8241ce63262cf79a7e6671148edf41ce4f0d7 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 66ded5dd4db27c94d68d021f1c4dd574134fd2bf12a563814b8bec39fccac464 + +Msg = 2330608734ab8f6f79965e860fd4efdc023356beb89ce568e856f12c126afa4b7086d146d0ba6b049abb389881e5ee6af30a130a4c12366c9c1972b89d5412e9ba667dacfd00fc5a6d295c3642538eb43a945fc5b1b68b8327c01bb8c2dbeed444487e2f69b9c5d4199e943ad0437a31fbeaadbd28890f6f6325ecba14ec280a4ec81b8c789b7c99ed0e1a8f0de0e005a55d719aa672e6f06a95f9c749c8ec366dbda4af7e7bdba663d0ad08ecb0ed38d3025220b32a36803bafddbd821196301567d1da401f24eecb49a6725702fdf6e5c898542ff5f8c07ca272255071cdb68dbd6b405973387a4c746e5d95a9a94918f1a7a5a9bb739628ff3dfc697bea55fa274990064f72547f199878c9702501d0b5ed9c67a7b54fbdc1cbcb466ba4d51ecb7673fe251f35e66243a018edc0070845b465619e5a0c52ee86498df7dc00e1a8580a8a275fb4861d9973ae1b97dbedb86649898d91c51f17cfe671b0afec9eabfa42c168640bc17925ea0d81ed69b65b27e61ae45106f8426cbee26641a0da510d5c0e1bf64f2c2e8794fb42be9c0d97fad5ccfffbb6844c7afd1937c1904a2ef6eeed9d853290c143bdba1b674f6d17aea4693cf617928146845fc31c82c6b271ba3b402ab90dc9aecbcd4d0d2c5b0d5b7b036a3b6a1d56c6678d4011b639fefe19ce76cbb5f15db672a7e850cc8aa43842 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f +S = 1e5524eb37c5719ab28e6c086c4dc8ab360cfa7c21e9fd9e93c463632be9cd5c + +Msg = +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 6128facdee27eaf0e9aada44514bc1b71bcfa838753f67bebe556f0d14d22c46 + +Msg = 616263 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 5439c8b94ff2f30657ef9a21fc67d89db324e318768b3875ee183c3c70eeb3c7 + +Msg = 61626364656630313233343536373839 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 115ea5a356399515b69048d05a7a14a12e00c4d17e6987538a33b4defbecb751 + +Msg = 7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 5f0c65d5446d44d3e2b15f09833952a054a5e56c9fb88b5c750ffdc2cae5329c + +Msg = f2 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 677f5c201454dc40e0359ef2fc948e8d42152bbf5b0c1a9bff3788a604418e2c + +Msg = 6d1095c0ab54f79b0e14ce3b806ed21a797e4a18869455c4273ebf57ce5048 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 56073a38aaa40d553ede055642a421a138cbe2a8176bcbab6c9687dc4f28329a + +Msg = 123483476c80062e9d6dda0d737341732bbe13aef4ab05f0f10a48c18dd84c2f +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 2aa619b4c62d517fcecf02924ca95c06857f7a4ec051fbc561e2dfe3079a282c + +Msg = 266ddc552a2b72348a2d9399f0a6c8241ce63262cf79a7e6671148edf41ce4f0d7 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 0124f8afc0d8bf5ce9fd2e8dbf51ae6877483d91dc412fb13ab2298f8a715ba5 + +Msg = 2330608734ab8f6f79965e860fd4efdc023356beb89ce568e856f12c126afa4b7086d146d0ba6b049abb389881e5ee6af30a130a4c12366c9c1972b89d5412e9ba667dacfd00fc5a6d295c3642538eb43a945fc5b1b68b8327c01bb8c2dbeed444487e2f69b9c5d4199e943ad0437a31fbeaadbd28890f6f6325ecba14ec280a4ec81b8c789b7c99ed0e1a8f0de0e005a55d719aa672e6f06a95f9c749c8ec366dbda4af7e7bdba663d0ad08ecb0ed38d3025220b32a36803bafddbd821196301567d1da401f24eecb49a6725702fdf6e5c898542ff5f8c07ca272255071cdb68dbd6b405973387a4c746e5d95a9a94918f1a7a5a9bb739628ff3dfc697bea55fa274990064f72547f199878c9702501d0b5ed9c67a7b54fbdc1cbcb466ba4d51ecb7673fe251f35e66243a018edc0070845b465619e5a0c52ee86498df7dc00e1a8580a8a275fb4861d9973ae1b97dbedb86649898d91c51f17cfe671b0afec9eabfa42c168640bc17925ea0d81ed69b65b27e61ae45106f8426cbee26641a0da510d5c0e1bf64f2c2e8794fb42be9c0d97fad5ccfffbb6844c7afd1937c1904a2ef6eeed9d853290c143bdba1b674f6d17aea4693cf617928146845fc31c82c6b271ba3b402ab90dc9aecbcd4d0d2c5b0d5b7b036a3b6a1d56c6678d4011b639fefe19ce76cbb5f15db672a7e850cc8aa43842 +Dst = 515555582d5630312d435330322d776974682d424c5331323338315343414c41525f584d443a5348412d3235365f535357555f524f5f2d6c6f6e672d4453542d3131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 +S = 2e929fc7c74ed8e9d9d4d6b527bef54e8af57c991cbdb90194a6d5362b5c4910 + diff --git a/src/tests/data/bls12_381/multi_pairing.vec b/src/tests/data/bls12_381/multi_pairing.vec new file mode 100644 index 00000000000..09bb7c8e878 --- /dev/null +++ b/src/tests/data/bls12_381/multi_pairing.vec @@ -0,0 +1,26 @@ +# Multi-pairing tests: E = e(A1, B1) * e(A2, B2) + +A1 = b84ccb9b77535b8dc25c39959366871a9176c7cf1cdb21ef8c3ff5313e42486f8139b9613721c66d097e7edfd14aae36 +B1 = 9275e601f0ac0ed8164e8a0bb0ab666aa9c4d8b883e3a71d910c4d57b63c1d1f650c01b9acc1bfc996f81db311627e5e149b2db9920a734bfe6aa7c380a876d46236b7fe8fb4071d51017d3034a29e2fc2faa202cebeecfca03b5d2855f8db27 +A2 = af75f48ff3172fa25310b4b3052d55986271f59edb149bc3eedb8fb8fb11611c571bfc21dd634263f99080ea6a49b0d7 +B2 = a8bc6761d6fd55045a8eb5f90c7cddf38fc3b150cbabbb0e06ad15333d16f00ffbc6eb9c3fab8431c61849409eafbee905397e90505cf9ea34075573bba1f3361ecb20ca1672bf3d2c24738a7bb53944b6284ebf00a82315273996577281f9a8 +E = 0e4fdcb607c558f5c2fde17e089a6feca3bdfe04d1dea3496c239dbc93ebd69415170c8b2b8be716e045ed6f9260f85d09732f513e8b7ef5635cd9967a42e04e32a65bc78541156dfa700cab078ecd3c2b097e3562b266a1b01bbeb256d116c015056edbf5505b08e12299bc76e902ee12d5c8a4ff76a731b162b93553b15ba32009eefad836f5d44eaf944f31d9fd710ed85b32a53eee123b74cfdbed21314ec6b9bb2c3656dd559f3a0b2d317ecb3455224e09445066bd97fde66406c3ac86040395063e937e00a6fd9f410a165398a80a04257145744327753334d3c8409b5b7dcb089c711afbb46f858726b614ca1905a556e6e75ca5cce25f603592688cafb33c7c97093b654a7eb1d3f2a3be7792a6dd928315bb8113a15ad43808ba5c010c81dc369972eae02818807fe8cc98aa2cee32bf4e8bc6374e58329da0b4d279b6ff4360da9f3491a5598848e2e0870906a4f9668ac613dd48a3e255393fae0d382a0155c2dbf049e41d0a901a89a46c6a83d4b5d36c594b3dfe4277abcb7f0855e772005a45ea0dd8a75f2575089ad84dcb0362b028b5d9a25574d0f289f0dc06943f6fbc2a273cc9bacca49f0af30701f9fb050aa1167523b113951b43f797140cb39c20d35e0132bdc91dfd610698bf7c1977fc07f7fa68e6e3e21beb1a016a29d71cd615769b2a4f43a9e7530f3086a49cbc83a05f323c9ce15ce326d4452355a3acc0c8d78ba359ec16baf3f50121341b1d6f8cc7747f0ba5b25b8301fcd89ece9c0dce56c9cea1613336fd1a1aaf24968dda1c238077249b38a32cd4 + +A1 = a12211cf6e14e697e9b1500b852de4ed681a881222fb8a70ab08a491ad3395d483578e978eb16ce03f5ac4b5e0ff8ed3 +B1 = b6f3e41741a2e88359cc4173fbdef89942d1903c331ee9ac84bcdb3f80556846154b45804e7782d6cde929a973fa789511263f07e5f7a78953f2c75eb31f89e5d75a34a72d100c0790f187ecd798137a10388a7aaaf7836113cd662d1c3574c2 +A2 = 9563667d6dc86a654cdd3c2b70964b700c8ef95c1fad85940eac3a6427c32cad7e9fc441a70fd692bfa2dc30b6c8aca3 +B2 = b715532087a8cc54441ce4fb94d6b68faef0455dffce1b88f67907c2ba59b0714bcee7d7fdc2247d1f64b60d16ac3761063231a618faadd39ab615dac50d2cca091ad15a5d239dd85c043e1db7ebfeb9b4a64b3a4d14425a8ed711be55cf2ee9 +E = 1713e523e77f09a3e7be9ddf56aa646b97a2550b329cfc577a6f096548ecd8a4bafb9d9b70a6e42a6d5b9b8a93f187df0ffefd2ee8f84d07a2d4962b510b7e658bc2a40771fe4421a36bf15d90a709556c7dd1bdc7d2eecb05478aad6a3f67941268b3909c7f96c31de11bd42d5a12fe5bd6101f72341003c82fc2b4a7c4e33e4ebe0d508bb55a440791c1f073379cf7035e30a5f50e80b12c76d53816dac413bb0aec22420085821a1ba5bec2d5a2aa26d4023181c906eacc2b28211a87eeb70b246dbf999c5be78a213e7ac0392139e87c0a0a6c9d5c4baa44463247788653805ab317539fa1389090bd67cbd80c7012a427f6151bb4b81b07dbd48be98b5956b54ef9d9d1522699e8aaed5e569190e4cdbb41410ea94217c877645454431f03a62a53558dcb819bbfa725bbde00350fdd34fdefa987c02ee622151dbcd4712381fa1625b309696d14634fbc3ed0ee13e6447e5aa1f2439c736bae482820c98e360de19bf7ca3a7d520076ec6699b43db5a55c0074e1cf6ea7ae0627bfaf0d1931dcac0482fccfe64312034e3b3fb59d6f944700468c4361eaacf13c38101e666fb7c212bcc23dbb31fb2e686923df047d2c4bb93840952a6a9a6f6f7cd94b564ca59d71ec5abd7a3e7a3787b28c2e1ac54db42b390d512feb9b9854d17f7c03ffb335c34c9d6188b5d89368d7939c14e6d2b23cc7f052000ab061af64d2a1c13dd9dfd1212c350312907baccaa98b1885d3cf76bb0cd07af583b8aa1568f7c86d1251f1d593cf93ed148da8e1345105f26a661036cbbb1b22e3386e4ea21a + +A1 = 917e3b490b63e4bffbd8c44c2270763129096ae73845bdfbf3a776567f752a1c987cf5c774420ba7d5b60447292de1af +B1 = b3fbdfa495e772af72b6ed2e2b146b15a52e5796b4c745561721d4e0bfe0a36765a23f1109262930b72d512c6aead4490ec27871a9b8fac8799376a43b199f31aad02bc0fb39751edd3ab6121643c3967d0bff9637192bd6c099d96753f43c8e +A2 = 83524454cbb29f1190fdc1461b29551222eb8862fd3ceeb9721b11e87b85e6c6f57649a6f1c86979722b146bc5051990 +B2 = b732d7e2b2ce6212ab0b434f6d397c51d9e232679c0e24dd5afff2063586781f1b2a8dfc7ccbe828a8b278cda8f7f1be06b362b189011e39f363b66ef4ce3b6fbd5d340e6bdbeea8bf2acf148ffbdfd141e1f22c8077fbfdc07e1978fc3cce29 +E = 02379712fdf4835dbb9af16f30e93c9143270887bf38626d9243ad2f2171ce4ac587dfcef77e3951fba8689da120232d057f610363ba0877112ab8d06d0c667849c9b4046d9665b2b81e19db398a93e239ded69e26de4525d576b366f466ee6e05c60e7d5fc702b297f30ca31287134ee57623700765e8ea97b3ac07d16e0d34339f52d2767dd090f5acea965b77a9c30cd29c6de8cf2fb3c1eba8d51fa5bc91ffdefc7b6284c6cbe2e2f6dcce6af2752f4975f086dc39abf1143ca3f0949aea18adaacb471b25c5238d116664292aaeebbdfc37be686ae550594bdb1aa5375b8294e2e4762814236318b5aeb910b26706c6f7dcc75b83ea90ce920a7446ccdf2660d907e5cdc9a8df38c6dd2b589c5f73fdd6e88d12bd672541e15aa0958c2f0e1cdef3e426787874de966c6ba953606662cd18210eb6186edf0e0a5fca9f42832323d6ffbf79c85d073fb4586630131781a163632cbcaaf0bf09935068c0d3d6ff22c76465c8c1c11bf7548c9ad29c2c4c42665a7a76e9079c4f6970d5a63601b08ce1529d7931b550bf1db3d9d1ef16cd7ab1aa0d4ece094a44b09c22b9bababd4920ad8ef5a63b73750f9905e23511184ee6902d29d0a818e4ab2f9ca8053b66ab00ec2b8776d1c6fa4c2b5a9f5e1e9558c1ff9b2ef263e4606812936004045784c15b7ec0b2da593176bc2e395903852b394992dfb30004b0d6b2c038433dafe0ff1f357fb393acbc0cfd7ff6f3130710392c491e88432716ca56b22bc902831c2e9721a66b0dad4020ae0a3a90ddf89961fe28f6f6ccaa7399eb33b975 + +A1 = a5b017b3bdb186906c37807b21be4cf0b8b54511ed96271568ed4200673fd3681652784f12ff40422efc1a83c3b001e4 +B1 = b6e778491a2be427231214af868ffa587a0aa87802709d02e5f39682d6b9095720caccca0951b5fcab9bfdefc9891958051f2ccac85750729abe8ea0f72010478a0eea7e288b5a81d8b792bac797011e3291788ee49f0c0c81de7dc0b6b033a3 +A2 = a5c0ff0efdc4b18fdf97798cc6ac4c6be069edc8f69d84a9f073a76d2fb6a62deb8a9f99efac0a00e6a8cda03844825b +B2 = b6792bd4e63ad8d90f69fe854beee377a4a5ac0a3be5388700aa1f9ec6c5eace745de7571905f0710bb288e9af9d94b41604386e77ef20a4cc79266560a9dfe478da5b932b12c0e8d62c5b5fb06354e01e379ee462fb81cb5098f2866d8d10ce +E = 195b34f96a34ee3f4051932166a6ceb39b2edb72d6b7d570bc1b2b6f3c79c1588eaded2e79248a5627d1c5d7f142e340163d8d3ffc126ea7e9d36cd89cf4257bc4e732b3046929815c329e26ea2e5016b33f77615cc2feb953dde119938b056b1739a256233653599ddccfd2857d78a9fcb73739281daa4996a65177128b5ee488477c0c16fc59a54d1a9f4982b7913b04dae4c059273c0a2dd2f14e6d07c9f16e39818eb7b98a71309c34de202736db0ce04e40f2d8ae72f20f5f66d47d69ae19f25112ac3ce661ab491303144c68fae25212f9aa822c5d3096e2348085f87d8bfc9e7a159ec3ede5f5d65a3aa35c350a2d5f3f4bd596ffbac214fbbf5b5320c4d9d704a95ec0457233666874be8a11b513f81931d630b853660276eedef1e710ec7f33c5e1e4eb4c9ad9d2e4a4da6a268e20b1959cb215259e2edbb0e38cd6c12221bc755ca847cc57dab675dae64216f1a075f2a6fe67e73f8e32af8096b01dca27daa664aba0306f50c7186d719c67cd8828da20b1759aae404910160d5002a9585c652c882aaedc6c08e9479f2b2705bc9d262ef5a28a33365851a55f5ab4086c16647fdee5a25ba4dad1a5aeb50d7445c053b84e58f5980462629ea31c08a30da6fd3cbe6eb1c30ce5f1b2a31143207e819e08dc466ba4d572d07592670a4fb8730f74850668699a7c54887454cf9d2c829b60e63afe9d043289dbb1cf295ef1951b8f4aca2b702e6e7711e4c316d643f70868aea1fb9e30879dc15d4027e8ef89d57cce4b610a0dfbfda302d638723af17793499bd8f84f2a21d2ba01 + diff --git a/src/tests/data/bls12_381/pairing.vec b/src/tests/data/bls12_381/pairing.vec new file mode 100644 index 00000000000..0cb40b96d5f --- /dev/null +++ b/src/tests/data/bls12_381/pairing.vec @@ -0,0 +1,38 @@ + +# The generators +A = 97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb +B = 93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 +E = 1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b619f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba5703350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a204c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631 + +A = 81818c7c297d50d45300bc8a3827f739c6ff832e4199f7320d9e5ff27e2952521eb2135fdf8c94cc58f68021231f420d +B = b3056f2e3fc75dfee7ecca3df39238611f1b3d767181961ca8f8e54d7ca87871dfe3170c4b45bd0462a15a225e390cf210b4251583e5fc50872a7edccb8cb179d7e84a149439668002889d564647110b4744f52b9b8a362888d658a01f0877f9 +E = 0809a5cdb44d960c9fac4522051a1b3f89d003f851335bdcb70605ff6c4e8309116bef95251c21f5452bb8cd8a76f8330ecbb3a42ed49fb40d145f4a78e4157acbb1fd799e579897d1fa720318ff0b3a4a1bea53783d7001a16207a6acfb651f0f389db48fceae344e8491d71ea8036241145c08f3579542c1f12193dceeeb439e6c5d09364046dd02852a163240109611372da424ce7aa40a62090e953acee670d03ccaefb1b4130d83b37b0fb09a061d9ca48fe92ce72ca307dd968d7bf5a816f61be0972c649d3aec57d30f8dfeb3c67ed1b74d7a43e324466bddaa0e1a79ff196529cd488f22c8498fe6983dd8ac16b5efc8fc28262a59b272f99ecc4bc3ba49df61df0ae0a711bd365077c8457b79bc9f81cb03c3f64a194f55f5e63e7b12ff7bfd08894d79f173692c815d86b627b2bf2b63aa9f30ec464b01d88b0803f138baa9c6784d7eba5aba9a91f75a8a00232498d03a1518dd73e46b325d37ba13f0a22ca6789889e112d7d8326c120756f88a9598b20e26f55f3c901aa7d2ec0fbe26550a876376b594f1a9aa48778d5b147f5a9e02d6dcab696be052fd2b56eabc504b3130a61439f2ff54b0b357b4022ae01405b4526e1f5799acc8a138df45bfc931e03bff48bad5bb6c5121c1aa4f8ba0e4e45e05af17689f590e31b4ee0c1a10d05ec9038b2500e6e7827909bd3e6d4985a5961795b16238bf66c65ce8ea13518351a2b9dd0996a9659a72ca5a08eb542b419e1d72f41ef2ea09ef7fbc9a54333441470d55abc02f7022eed5b629e15a48d615ac01d20d21158143accb + +A = b630caae647744eab090509d17e1fef45d2d067aa37e3951a9170ccaee6c111ab92deaa53ef3ed89654b03e2610641c6 +B = 825cea01adf8ef3fb77572d6c87d22d829dfb92e9a32a824062b22791c5fecd71f1f00639b482efd1c385c6ee6734a7f13dc49b3dd74ca102466143c3e0fc1fa28fa3cd8252d130b12145fec36728254ea5a88afc634cbae6bdf3e017222a1a9 +E = 138ed1afa825fbaa07a13c2da4851a9e9564ae1445f02a136773bb0f828db0c605a27fd9fb3eba657cb0723515e5d85a191e37c62a26bcf8b04756206c08445fae73b1c4f94ab14e30ed2a65a4f8e8b29c1daa45ba98cf879c0c44a8b72e96dd12a75e05a400d0335b2260c2bc9a769e52e1a6bdff21e32c961842538c5377f6def25b81b01c8eb14424b085e8f14e28015ff313757c65327f30c3396c3dc8d94dfbbd15cb758070dc095ecb8c63883b70a3c25ba6aee1011915e50069b6ef77047163d205e6fe07c188c204c143feceea3086fe693747e652a4b580b1b170191abcbf5d10411d684fa3b7893ef5064003d163b9ca633b147fb95eb3e7165995cf7c036220abce8324907b4c43e1bdb3f69a0d36c447d9f4c357e523b41daa6a035561beeb455d447d630c838d174795bd8be9db75b5416b4d69a51ef8c5462a72d6ee3805e33d9d33c5c2a6f54c420a005a0537a6c87519485c5704feb2fdeb960ef503965e292e2934ea681acdfb0bbf158478be8f19748015ed31c2572e6b10cb789bfbf269d19e06c125f6e9b7c4958a20ff988280b8c724a3cfbf5872375e291ef0cdde980a17615b29631c7ef906f7462a0fb80246132e3f02ccdaa87f0893de6d0d9f1e8f2678b2a8a2d27c58f612f2b9ff4938a705d1fbcd9a713fb7184397a8e71381d1a2000b60e226471d29eb691d0e7846708055ea32010ba8833f3bfdcd0aa667283a7d1e5dcf4bce901072cb319a05ef201f507acee3a418cf96af52c3ba785bf3493baa0d3228dbbab4cb566df67f0e6f8a6d3624f839aadd + +A = b4c7e35d57887fc301bcfd35db31f3369b9d764a746f418d5f1c172e5621060f6e3eb1a386b9768e1f82a39b2ed001b6 +B = 843f2ba2499786f00a2e3c3df4701ac3c7d7cd3aa062c13df95cdbe9328830483aafa8d09613da293ec82dfd7e3223b50a9708acf545d56803de785b5a98783906b9b0edb9342775ece6bff057e5b4bfde4b4815c8ebdc040964c39f095731ba +E = 14acf761b6f83245810c3440654889c60dab5496c963ac0f64309a9225db852221ec8015a678e0becc303b253de5bc4106af41e3a93620a0cfb83b28b7e0a3936b511ebf4004c1293d1ffd16a79643c97fef1b5196985d971da3ee15f012efa4172039d5f2dffe493dd626b238099a8edc7270bface24aee86d9807eb4d5bab2a815cd3947ff440c2a1ed64026004f250a4e7c8258c9578ce599ce91ac76061c7be215d2b0537d708729f83a56447a90312c90d93c02feb7f94b4162ed18540d1086b04a84be8032a4adc81c83470cc491b3ab8e85a37bed8034073385812f29de160001b46d65bbf3636e1b90b484a011b04f4c8a24a9ebd24cbae11ab46b7d0c643f5931a3a7cf0d088b9828d139f049f40cf505912d25185b338e580555930d843d4a43e97717206485d144fe21ed5f6f9d512cb1dee6ec12dbeb1fb2f5b7fc99cad851351a0fc84097d28445e28817541282649642b6046d5aeb090c4508b0f98b328be9bba9f50bcc22c55ef25fc24772a2a6f5d8bade0b52ae666af7d1083f5eedca58b52fa7394cba2696a1a7d51619c8e2204bf21ae7713057165b5dbd87b4072f6e2c3975d37ccb4afc54551644197349430a800b2dee9b3812ac03eb752bc1a27e62ad7b8b1fcc2f552703c0537208a61a338b389746ab566316fc11f8dbe4c4378c7293d66ab5f7aed1d8cde759b14741daa21528ce8d127a36fa564db69b3deaf50c43daa54232a10d3306321b852dd76011641b5d0323b6f0ba3c4b1cbfeb388601c8e6e72159d37ebbf4f0a6740e89ca6a99cffafd3d12e6d6 + +A = b844c759f2b6568a1684310e1b46651b25bbb6258fa0e303a25d2ca08d978f1990b328cb3fbcfbf5257d54b07fff8677 +B = 914f888ee0d89fbd7205b9206d04b5ec33943aa10e52176f291bb609b919aa6e40a929161d1c4992c39fbf3f31147ad50331a36db8bf5f0c7a120e19fe2bf5a74f2a758d4156136f467aa0c23ca093a7d6a96eea20c289c5e17ccde827e2881e +E = 0fa022fab05f89768156082354f44cd1a5541e59fc07258d4a435d3e82cd05adee378c9cefb0dccd8a4855fd6a073ec40e7cd821878f2b6532cac7e4329a0405ba7e41c16ed31d1836962f5e9f50e8955181bf53000eeabd7f6a4219d1ea45d01854ca95498e74369f995bfffe74eb9270e80b5a657833069c7ce0a178765830faa85f5f133c670bb488e51a4d1fde9405218e3481060b405eedb4c0b7053315a379c4fdd10335fe820b93bd9bfd8285a2d6428ff520f74819edd8eb614859701884f45b9d952356c3917e5650ba988437568e0fbe8a48e32ef8f849f28f72376312e25c6542279fc77b3da8b4c3b3f90bc95ca22d660feb4be1551975823c26150110d77e91694fd75f4ddbae023d94af1c49a1009188e4f3dcf42e9c1f2e8011760d89546062abc3db5e00c74cb913575f41433ad48e861a5b4e16508f89219db6563d72c0b678df49a181a21e7c8b172c521a572b04d42dee135e673e8f7171daae3393087f559478d2d93132e2fa62d5f3aa6b550fa99e41791e8d2e28e8179d1fa0e78b98fc33bdad12a095f5b226ca68ca5ef2bbf181cd20b9e2286ce69bab303bfe4a6d074b0549bf1a3f17ed03fb706b2c4b61d514b4d9da2a1c4e3ba42469c1b6e4d46c180cc6f83194591c1a90ae4ca86581f2ff2a1156c68341cf0db8332932345feed592c283da39b4acca3be30f6277652e69202c8c4cdba9e49c3de207831a0aa389f17f59c5ad445718a51ac1fead6f359f10394360bb38788e51c6c1e25c4dc12d1ca389d4dba801b9b1f6733e400d62c8c0dc38d6de07c0 + +A = 8827113af6c8546d56bb30871d7ef638d2c926dd23572826ed1609cbc3bcb953cd8c4c8963aca4f300de35b61cefb434 +B = abfdbfc3b5cbc0b044571001d0401faae48623e152db3463b972d4bbd7f7e9ec3fb0fd5627176ce1beee5349c3e46ab205bec202d90f896d4ae4370c08dd2cfc94a3a1af401f7afdcc671aa659adc11f9c37227231df3fe1942f2ef071cebd63 +E = 0a4f13fb60f70570b2afc7bb24e0556a5977c0bd62efbe3488e9792eb365b98fcc16f832ced2e5dbc15279db206bc0c006326acbccbc35cd22383af5122191a90564ed7f2adb138df9759c31fcba4698ca84ceab22a54ea787d9b13fca2fe69100b231bbbbcf2bda6ebc59135d494ebee784c7e4e601ba9a6d129913e21de8ceaa06e0cca3144fba9778ffb7e61dac99163bcbdb5335670117714e578a2a298deef978b4b06a2967c5c593995da46e487c6cfb25464627ae974982155d2f9f6207aebfe61abfd47e49e90647c6b69520c580fc73d9dd1aa96c1c28a376f31674b46edf48b441fec93c305786133a2bc71502d6670a9f4ea386c35cdd4a39c767401085adb085c4fe63f7d3c34b68d405ee566b0c095bd68f01f28e33ac3f8e95128b21cbbd22d565e70835b1197dac62e783866cb2e9aa83185a55c77fbbb7a5b093f01287478b61d4c65a8d321c4ab315290c2e7ac78a443df3cd1f2cb71b3cbd276afd73d173ecf86375aac67a8e534d46660b56eef4fd9729f263efdf2e7311df3dcc6949faf2fce215500a86f0a9b3adc23538d1f5129cc833fce2c0044122aa79aca07c0c7d308bd9c5062b0d2c19738b9f2a22047949f82405170878b889e35a08366784221ce3f522f44d7bcc7a296f404d455ed70d317e6771e61c610865f1bdd6dd22a06f9ef53478052d7c360d3a9d0e27eedf06971c95a87390617e331088c6e4fb7e31798d591b9173c7070c2e9583710f41bf6e93f94cc02d164d17b69f3d98a1c93c3a8f5d2483f125ffd504c5fac75fe13920c43e9609edcb + +A = b78d0643bd24219a10ceefa2ba0bc646cd51f4883a892f8cf2ecbd6316631ee913ab26fd275242905ce76c37e9461915 +B = a50a168309872082827242bae5964e6618d157605693315236ab804f2a828201ac8311261bab4d0d965a40666f8b86ee079bf9a8647959555a09df22fcc9c05573acd34f98b277c089fb6b6f25b0710e5cfda68bcacf37cbbba031ef72ae39b1 +E = 1037b5c2994e341181ab33432cf757e68e78ba2bc3577141cf422f71179f11495982437e8bc7cbf8ff93f2197499fdd8098996c70c566b1bed615be9859905bfead1d2dfb939924c07a6a7d9a2c9f5deb8ef1a528598c30227eac8fa9923c70c12e31d7748136e99ebcf66788d5675a87ee245a84dd0155fa25ea46be70493e306d62ca011ce12d825e0c4d3e07b8d7013d1d7c456468e01850d7ed78f4b9b31b8c4f808889d0f852e96504295e8a659e6dffaa87b0bee437f2c689fdd9687590d5248767f8407254b0d152d2733172be61375d6af260edf739394f21eda34948599fbedb0378a2337f10f3bd575c62a144ea132bdfa078ff892f299fcb2ae1800906cb0e5f4a6b8c23ca1522d229be14b8839dcd6e6c870060cc7f7db6982af0b99338e2dd2efdf21cb6bf665bcbadf7bced3e9a4c81572c495cc3d08fcb8a5be190e1a0247efe02b1aeacd1c000bba070ca3a3c2c056dbe6e838459f10f3a442a85b9860896406f456949ed4f0de5a91d79c2efa47ddc7f24fc28984edf13318993db8ba9b3d682b7cc08f41caaf086429ef9095a96a6c1a96cb9040c3b52f4e081018634f425af6ac9ff6a856a1ee1017a5f3b57bb3b1df0c2cdff569c0b7429f474fb08e70f500162eb2eea5682bb0ba193b6644927b36008e1d859809dc0fd66127d4859397a5903eb5dea55dd181ad1c0d5321c16fdba8b1976940826450ec74022df2c4e5329a8e19bc10bfd4014ba1c3d651287399decafa356a93c0fe44a5ad4c07a24cd1394212ef811f1dfda8fd56d1684a6ad231a3db045dff07 + +A = aad1937c5d849888095837c8ecd4b974a8df6701d3ff65262949a821ad9cc899c2b2e02e4101db1dfc770aa15286fcf3 +B = b518ab717979ef3ad402526ec51afc22dcca2d49ed09be2bcb71adaed57a9380066912d27852e84cca7727c155f789500aaad87caa3f9338d003c2d2ba27333ae41711722b2f28c6d17fc7c719bd0c3a9b3ef1b247e7c287f1b4e3f506b01386 +E = 130227f03c46d0131b1c03ec7d374766bc0529d5b750a82f638529a2093f07a607f9f335bce75ec8ab0aa5efc8717ab308a0c229eb28121b0428d2809ff80d24090a74c271f5df9cddb9943faeaa72230499a1e15fd9ca754beeb1f537deaf120e60d0072a0fafe369523da4a2ed73c3e12dcad054b7426116893e4307b06ed3a7b32bb30f5c25bf091134f481bc7f11122ab7e65c7e1ea51fecab7905a935abb00435c54dd0d28e1322334f438072fc15b190d223757e4a7637887e32f82ee206213d3e8006e7fa6ad71a5fa64db6d8dfd4232a91cbc2c3481ac3b4449b5bfe475f1082462a9872575b64057e6481de14386063d5c45cedc93da7ea4b56efa7cd415e67899716e3d92618ec25f98684960185e389663d613adf4710efeb4f9700bd7778a8ca2c3c7e87bf7247c567e0e5eb05761dae0320782f215da823832395b313932e6f557cb2454ff33b434121133749acae4f8aa93567cf782eff81ba038bc4ce5dbdfe6a7bc71b487ea5691a16c406717d104bc549b0b8dc9eab1bff1369297ad3b334de746a7fb012092f4259588889a5991219706a9d10d5a0efbe87892f2be2d77ef27d66d37899f1f81b0f239fdc1693929ff980cc7ae18488ba2bab9527488db957d3c6c473a8513818fd053c913dbf666d75d575a2ced5001e06a368b5f5133e2c7094c723ff5b8f0977ee8f82ae135c098d055e14a0835deb12dd35e7aa330e5aef775891badba1cb0dd4f1fc35a603ab36b51a824838027af5b6b8000d0d5ec3358539170018a8c3f24122adf5be71c0fca6b084dad89c21 + +A = b3caa188bed89f5730cb77ec20873180c531b01a4bc5aaaaf41600adc323025816d814855583f47452e78e6c0ea8e450 +B = 8d8be153f53ac26985f605af0e0ae3a922654398832e065fb4f710b0e64fb83c526983994863e372d80f5d6631ee646912e3fe0aab764a9e0f185292e4be10a3b34dd8747c00dcac3178ae47818ae2ed4ac3f06080c95e0cff49a5d5638f4520 +E = 0760b8eb16ffb02a54f6ecc235a713d9c693110d232c810d3dd710863c7c193b585fd453b87d1d17b51675159c8ab13200517bc20f862fbea30b0d702b8fbadfc78c7327b96b6d0f8834b1441e6969f401d1b064301486458b01493d10549dd616f30922e7a58ae52bdf9938b3c15738edee68b9964b8b162dfc619560e468c8111922f5e3b0c4b4ad725abc2d0cc5cf1403a390dc445b3918e79726d99e3b580edfe44793e8b705d93f924ae210612d853afeedf53f8457dd447eb3852ede7c0eb402143eae219854512d7f78aeb3e125cac91637265021dccb79d280c44ee068d97eccd1e52399dd7b02db031534d3174250da8608da4b025499fa6824e7a1acdec1e783b667fb53e77870773af52f1c3e5a5b5e76b48c6ef1de2a2afb98fb175040cefef280f8e3d0648c5d956165e207ac3c9f767b4f66c0fc44108016967dcae3b5c1d179fe641ff4192fa493bd19545df94771bf1d061f412917dfea465e480f3e06e0f18554e58ad694ff1f1ebd93368eca072002e1dcdf2868ee65fd0e5d87dc175c4d25883928839f7505afd252bc14b07ba3a4d91aba385fca13e5bf8aa43eb1999fc25b8b686b1a431c13194e4899fbd2445a1e965d610bdb7a6128a8e72cf40fb616ef36a5c5cce1490f987d3fa8dfe54034143e07cdbbbc80b20d8fcbe49a9676149e29002ed11763a5471fa2968e2ff9b0fd1e078272a9214b60b351a006f0fc0c3d712d2b735b4b4805716c3c22a4a26c59a60fc70c77d79bc669d65319d31024e35bbfde5326fa9fa8e2528ecad2595bc621aed99ff268bf + diff --git a/src/tests/data/pubkey/ec_h2c_xmd.vec b/src/tests/data/pubkey/ec_h2c_xmd.vec index e0a257cdd52..42382532129 100644 --- a/src/tests/data/pubkey/ec_h2c_xmd.vec +++ b/src/tests/data/pubkey/ec_h2c_xmd.vec @@ -31,6 +31,28 @@ Output = 48e256ddba722053ba462b2b93351fc966026e6d6db493189798181c5f3feea377b5a6f Input = a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Output = 396962db47f749ec3b5042ce2452b619607f27fd3939ece2746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a842a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf378fba044a31f5cb44583a892f5969dcd73b3fa128816e +# Long DST vectors from RFC 9380 appendix K.2, exercising the +# H2C-OVERSIZE-DST hashing of tags longer than 255 bytes +Domain = QUUX-V01-CS02-with-expander-SHA256-128-long-DST-1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 + +Input = +Output = e8dc0c8b686b7ef2074086fbdd2f30e3f8bfbd3bdf177f73f04b97ce618a3ed3 + +Input = abc +Output = 52dbf4f36cf560fca57dedec2ad924ee9c266341d8f3d6afe5171733b16bbb12 + +Input = abcdef0123456789 +Output = 35387dcf22618f3728e6c686490f8b431f76550b0b2c61cbc1ce7001536f4521 + +Input = +Output = 14604d85432c68b757e485c8894db3117992fc57e0e136f71ad987f789a0abc287c47876978e2388a02af86b1e8d1342e5ce4f7aaa07a87321e691f6fba7e0072eecc1218aebb89fb14a0662322d5edbd873f0eb35260145cd4e64f748c5dfe60567e126604bcab1a3ee2dc0778102ae8a5cfd1429ebc0fa6bf1a53c36f55dfc + +Input = abc +Output = 1a30a5e36fbdb87077552b9d18b9f0aee16e80181d5b951d0471d55b66684914aef87dbb3626eaabf5ded8cd0686567e503853e5c84c259ba0efc37f71c839da2129fe81afdaec7fbdc0ccd4c794727a17c0d20ff0ea55e1389d6982d1241cb8d165762dbc39fb0cee4474d2cbbd468a835ae5b2f20e4f959f56ab24cd6fe267 + +Input = abcdef0123456789 +Output = d2ecef3635d2397f34a9f86438d772db19ffe9924e28a1caf6f1c8f15603d4028f40891044e5c7e39ebb9b31339979ff33a4249206f67d4a1e7c765410bcd249ad78d407e303675918f20f26ce6d7027ed3774512ef5b00d816e51bfcc96c3539601fa48ef1c07e494bdc37054ba96ecb9dbd666417e3de289d4f424f502a982 + [SHA-512] Domain = QUUX-V01-CS02-with-expander @@ -63,3 +85,4 @@ Output = 64d3e59f0bc3c5e653011c914b419ba8310390a9585311fddb26791d26663bd71971c34 Input = a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Output = 01524feea5b22f6509f6b1e805c97df94faf4d821b01aadeebc89e9daaed0733b4544e50852fd3e019d58eaad6d267a134c8bc2c08bc46c10bfeff3ee03110bcd8a0d695d75a34092bd8b677bdd369a13325549abab54f4ac907b712bdd3567f38c4554c51902b735b81f43a7ef6f938c7690d107c052c7e7b795ac635b3200a + diff --git a/src/tests/test_bls12_381.cpp b/src/tests/test_bls12_381.cpp new file mode 100644 index 00000000000..9d1cc7008c0 --- /dev/null +++ b/src/tests/test_bls12_381.cpp @@ -0,0 +1,1591 @@ +/* +* (C) 2024,2025,2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#if defined(BOTAN_HAS_BLS12_381) && defined(BOTAN_HAS_NUMBERTHEORY) + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace Botan_Tests { + +namespace { + +#if defined(BOTAN_HAS_BLS12_381) && defined(BOTAN_HAS_NUMBERTHEORY) + +template +T random_test_elem(Botan::RandomNumberGenerator& rng) { + const uint8_t choice = rng.next_byte(); + + if(choice == 0) { + return T::zero(); + } else if(choice == 1) { + return T::one(); + } else if(choice == 2) { + return T::one().negate(); + } else if(choice <= 32) { + const bool flip = (rng.next_byte() % 2) == 1; + uint32_t x = 0; + rng.randomize(reinterpret_cast(&x), 4); + auto s = T::from_u32(x); + return (flip) ? s.negate() : s; + } else { + std::array buf{}; + + constexpr uint8_t BIT_MASK = 0xFF >> (8 - (T::BITS % 8)); + + for(;;) { + rng.randomize(buf); + buf[0] &= BIT_MASK; + + if(auto s = T::deserialize(buf)) { + return s.value(); + } + } + } +} + +class BLS12_381_Scalar_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(scalar_serde()); + results.push_back(scalar_vs_bigint()); + results.push_back(scalar_hash()); + + return results; + } + + private: + Test::Result scalar_hash() const { + using Botan::BLS12_381::Scalar; + + Test::Result result("Scalar hash"); + + result.start_timer(); + + const auto msg1 = Botan::hex_decode("F00F"); + const auto msg2 = Botan::hex_decode("F00E"); + const auto dst1 = Botan::hex_decode("AABB"); + const auto dst2 = Botan::hex_decode("AABC"); + + result.test_bin_eq( + "Hashing is deterministic", Scalar::hash(msg1, dst1).serialize(), Scalar::hash(msg1, dst1).serialize()); + result.test_bin_ne( + "Messages are separated", Scalar::hash(msg1, dst1).serialize(), Scalar::hash(msg2, dst1).serialize()); + result.test_bin_ne( + "Domains are separated", Scalar::hash(msg1, dst1).serialize(), Scalar::hash(msg1, dst2).serialize()); + + result.test_throws("Empty domain separation tag rejected", [&]() { Scalar::hash(msg1, {}); }); + + result.end_timer(); + + return result; + } + + Test::Result scalar_vs_bigint() const { + Test::Result result("Scalar arithmetic"); + + result.start_timer(); + + const Botan::BigInt p = Botan::BigInt::from_bytes(Botan::BLS12_381::Scalar::one().negate().serialize()) + 1; + + const auto mod_p = Botan::Modular_Reducer::for_public_modulus(p); + + for(size_t i = 0; i != 1024; ++i) { + const auto x_s = random_test_elem(rng()); + const auto y_s = random_test_elem(rng()); + + const auto x_bn = Botan::BigInt::from_bytes(x_s.serialize()); + const auto y_bn = Botan::BigInt::from_bytes(y_s.serialize()); + + // Addition test: + if(true) { + const auto z_bn = mod_p.reduce(x_bn + y_bn); + const auto z_s = x_s + y_s; + result.test_bin_eq("Addition ok", z_bn.serialize(32), z_s.serialize()); + } + + // Subtraction test: + if(true) { + const auto z_bn = mod_p.reduce(x_bn - y_bn); + const auto z_s = x_s - y_s; + result.test_bin_eq("Subtraction ok", z_bn.serialize(32), z_s.serialize()); + } + + // Multiplication test + if(true) { + const auto z_bn = mod_p.reduce(x_bn * y_bn); + const auto z_s = x_s * y_s; + result.test_bin_eq("Multiplication ok", z_bn.serialize(32), z_s.serialize()); + } + + // Squaring test + if(true) { + const auto x2_bn = mod_p.reduce(x_bn * x_bn); + const auto x2_s = x_s.square(); + result.test_bin_eq("Squaring ok", x2_bn.serialize(32), x2_s.serialize()); + + const auto y2_bn = mod_p.reduce(y_bn * y_bn); + const auto y2_s = y_s.square(); + result.test_bin_eq("Squaring ok", y2_bn.serialize(32), y2_s.serialize()); + } + + // Inversion test + if(true) { + const auto x_s_inv = x_s.invert(); + const auto x_bn_inv = Botan::inverse_mod(x_bn, p); + result.test_bin_eq("Inversion ok", x_bn_inv.serialize(32), x_s_inv.serialize()); + + const auto y_s_inv = y_s.invert(); + const auto y_bn_inv = Botan::inverse_mod(y_bn, p); + result.test_bin_eq("Inversion ok", y_bn_inv.serialize(32), y_s_inv.serialize()); + } + } + + // Wide reduction + for(size_t i = 0; i != 128; ++i) { + std::array buf{}; + rng().randomize(buf); + auto s = Botan::BLS12_381::Scalar::from_bytes_wide(buf); + auto bn = mod_p.reduce(Botan::BigInt::from_bytes(buf)); + result.test_bin_eq("Scalar::from_bytes_wide", bn.serialize(32), s.serialize()); + } + + result.end_timer(); + + return result; + } + + Test::Result scalar_serde() const { + Test::Result result("Scalar serde"); + + result.start_timer(); + + result.test_bin_eq("Expected serialization of zero", + Botan::BLS12_381::Scalar::zero().serialize(), + "0000000000000000000000000000000000000000000000000000000000000000"); + + result.test_bin_eq("Expected serialization of one", + Botan::BLS12_381::Scalar::one().serialize(), + "0000000000000000000000000000000000000000000000000000000000000001"); + + result.test_bin_eq("Expected serialization of -1", + Botan::BLS12_381::Scalar::one().negate().serialize(), + "73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000000"); + + result.test_bin_eq("Expected serialization of 666", + Botan::BLS12_381::Scalar::from_u32(666).serialize(), + "000000000000000000000000000000000000000000000000000000000000029A"); + + result.test_bin_eq("Expected serialization of 0xFEDCBA98", + Botan::BLS12_381::Scalar::from_u32(0xFEDCBA98).serialize(), + "00000000000000000000000000000000000000000000000000000000FEDCBA98"); + + for(size_t i = 0; i != 128; ++i) { + std::array buf{}; + rng().randomize(buf); + + if(auto s = Botan::BLS12_381::Scalar::deserialize(buf)) { + result.test_bin_eq("Round trip ok", s->serialize(), buf); + } else { + // A scalar is rejected only if it is >= r or is zero + const uint64_t first64 = Botan::load_be(buf.data(), 0); + + const bool exactly_zero = [&]() { + for(const uint8_t b : buf) { + if(b > 0) { + return true; + } + } + return false; + }(); + + result.test_is_true("Rejected scalar is out of range or zero", + exactly_zero || first64 >= 0x73EDA753299D7D48); + } + } + + // The group order r itself is rejected as non-canonical + const auto r_bytes = Botan::hex_decode("73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001"); + result.test_is_true("Scalar equal to r rejected", !Botan::BLS12_381::Scalar::deserialize(r_bytes).has_value()); + + // The zero scalar is rejected as a likely mistake + std::array zero_buf{}; + result.test_is_true("Zero scalar rejected", !Botan::BLS12_381::Scalar::deserialize(zero_buf).has_value()); + result.test_is_true( + "Serialized zero rejected on deserialize", + !Botan::BLS12_381::Scalar::deserialize(Botan::BLS12_381::Scalar::zero().serialize()).has_value()); + + // Wrong length is rejected + std::array short_buf{}; + result.test_is_true("Wrong length rejected", !Botan::BLS12_381::Scalar::deserialize(short_buf).has_value()); + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_scalar", BLS12_381_Scalar_Tests); + +class BLS12_381_FieldElement_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(scalar_serde()); + results.push_back(scalar_vs_bigint()); + results.push_back(fe_sqrt()); + + return results; + } + + private: + Test::Result fe_sqrt() const { + Test::Result result("FieldElement sqrt"); + + result.start_timer(); + + for(size_t i = 0; i != 128; ++i) { + const auto x = random_test_elem(rng()); + const auto x2 = x.square(); + + const auto s = x2.sqrt(); + if(result.test_is_true("sqrt of a square exists", s.has_value())) { + result.test_is_true("sqrt returns +-x", (*s == x || *s == x.negate()).as_bool()); + } + + // -1 is not a square since p == 3 (mod 4), so -x^2 has no root for x != 0 + if(!x.is_zero().as_bool()) { + result.test_is_true("sqrt of a nonsquare fails", !x2.negate().sqrt().has_value()); + } + } + + const auto zero_sqrt = Botan::BLS12_381::FieldElement::zero().sqrt(); + if(result.test_is_true("sqrt of zero exists", zero_sqrt.has_value())) { + result.test_is_true("sqrt of zero is zero", zero_sqrt->is_zero().as_bool()); + } + + result.end_timer(); + + return result; + } + + Test::Result scalar_vs_bigint() const { + Test::Result result("FieldElement arithmetic"); + + result.start_timer(); + + const Botan::BigInt p = + Botan::BigInt::from_bytes(Botan::BLS12_381::FieldElement::one().negate().serialize()) + 1; + + const auto mod_p = Botan::Modular_Reducer::for_public_modulus(p); + + for(size_t i = 0; i != 1024; ++i) { + const auto x_s = random_test_elem(rng()); + const auto y_s = random_test_elem(rng()); + + const auto x_bn = Botan::BigInt::from_bytes(x_s.serialize()); + const auto y_bn = Botan::BigInt::from_bytes(y_s.serialize()); + + // Addition test: + if(true) { + const auto z_bn = mod_p.reduce(x_bn + y_bn); + const auto z_s = x_s + y_s; + result.test_bin_eq("Addition ok", z_bn.serialize(48), z_s.serialize()); + } + + // Subtraction test: + if(true) { + const auto z_bn = mod_p.reduce(x_bn - y_bn); + const auto z_s = x_s - y_s; + result.test_bin_eq("Subtraction ok", z_bn.serialize(48), z_s.serialize()); + } + + // Multiplication test + if(true) { + const auto z_bn = mod_p.reduce(x_bn * y_bn); + const auto z_s = x_s * y_s; + result.test_bin_eq("Multiplication ok", z_bn.serialize(48), z_s.serialize()); + } + + // Squaring test + if(true) { + const auto x2_bn = mod_p.reduce(x_bn * x_bn); + const auto x2_s = x_s.square(); + result.test_bin_eq("Squaring ok", x2_bn.serialize(48), x2_s.serialize()); + + const auto y2_bn = mod_p.reduce(y_bn * y_bn); + const auto y2_s = y_s.square(); + result.test_bin_eq("Squaring ok", y2_bn.serialize(48), y2_s.serialize()); + } + + // Inversion test + if(true) { + const auto x_s_inv = x_s.invert(); + const auto x_bn_inv = Botan::inverse_mod(x_bn, p); + result.test_bin_eq("Inversion ok", x_bn_inv.serialize(48), x_s_inv.serialize()); + + const auto y_s_inv = y_s.invert(); + const auto y_bn_inv = Botan::inverse_mod(y_bn, p); + result.test_bin_eq("Inversion ok", y_bn_inv.serialize(48), y_s_inv.serialize()); + } + } + + // Wide reduction + for(size_t i = 0; i != 128; ++i) { + std::array buf{}; + rng().randomize(buf); + auto s = Botan::BLS12_381::FieldElement::from_bytes_wide(buf); + auto bn = mod_p.reduce(Botan::BigInt::from_bytes(buf)); + result.test_bin_eq("FieldElement::from_bytes_wide", bn.serialize(48), s.serialize()); + } + + result.end_timer(); + + return result; + } + + Test::Result scalar_serde() const { + Test::Result result("FieldElement serde"); + + result.start_timer(); + + result.test_bin_eq( + "Expected serialization of zero", + Botan::BLS12_381::FieldElement::zero().serialize(), + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + + result.test_bin_eq( + "Expected serialization of one", + Botan::BLS12_381::FieldElement::one().serialize(), + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); + + result.test_bin_eq( + "Expected serialization of -1", + Botan::BLS12_381::FieldElement::one().negate().serialize(), + "1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAA"); + + result.test_bin_eq( + "Expected serialization of 666", + Botan::BLS12_381::FieldElement::from_u32(666).serialize(), + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029A"); + + result.test_bin_eq( + "Expected serialization of 0xFEDCBA98", + Botan::BLS12_381::FieldElement::from_u32(0xFEDCBA98).serialize(), + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEDCBA98"); + + for(size_t i = 0; i != 128; ++i) { + std::array buf{}; + rng().randomize(buf); + + if(auto s = Botan::BLS12_381::FieldElement::deserialize(buf)) { + result.test_bin_eq("Round trip ok", s->serialize(), buf); + } else { + const uint64_t first64 = Botan::load_be(buf.data(), 0); + result.test_is_true("Expected leading 64 bits for rejected element", first64 >= 0x1A0111EA397FE69A); + } + } + + // The field modulus p itself is rejected as non-canonical + const auto p_bytes = Botan::hex_decode( + "1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAB"); + result.test_is_true("FieldElement equal to p rejected", + !Botan::BLS12_381::FieldElement::deserialize(p_bytes).has_value()); + + // Wrong length is rejected + std::array short_buf{}; + result.test_is_true("Wrong length rejected", + !Botan::BLS12_381::FieldElement::deserialize(short_buf).has_value()); + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_fe", BLS12_381_FieldElement_Tests); + +Botan::BLS12_381::FieldElement2 random_fp2(Botan::RandomNumberGenerator& rng) { + const auto c0 = random_test_elem(rng); + const auto c1 = random_test_elem(rng); + return Botan::BLS12_381::FieldElement2(c0, c1); +} + +class BLS12_381_Fp2_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(fp2_serde()); + results.push_back(fp2_vs_bigint()); + results.push_back(fp2_sqrt()); + + return results; + } + + private: + Test::Result fp2_serde() const { + using Botan::BLS12_381::FieldElement2; + + Test::Result result("Fp2 serde"); + + result.start_timer(); + + result.test_bin_eq( + "Expected serialization of one", + FieldElement2::one().serialize(), + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); + + for(size_t i = 0; i != 64; ++i) { + const auto x = random_fp2(rng()); + const auto x2 = FieldElement2::deserialize(x.serialize()); + if(result.test_is_true("Round trip deserializes", x2.has_value())) { + result.test_is_true("Round trip value", (*x2 == x).as_bool()); + } + } + + // c0 out of range (the encoding of -1 with c0 replaced by p) + const char* p_hex = + "1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAB"; + const auto p_bytes = Botan::hex_decode(p_hex); + + std::array buf{}; + std::copy(p_bytes.begin(), p_bytes.end(), buf.begin()); + result.test_is_true("Non-canonical c1 rejected", !FieldElement2::deserialize(buf).has_value()); + + std::array buf2{}; + std::copy(p_bytes.begin(), p_bytes.end(), buf2.begin() + FieldElement2::BYTES / 2); + result.test_is_true("Non-canonical c0 rejected", !FieldElement2::deserialize(buf2).has_value()); + + std::array short_buf{}; + result.test_is_true("Wrong length rejected", !FieldElement2::deserialize(short_buf).has_value()); + + result.end_timer(); + + return result; + } + + Test::Result fp2_vs_bigint() const { + using Botan::BLS12_381::FieldElement2; + + Test::Result result("Fp2 arithmetic"); + + result.start_timer(); + + const Botan::BigInt p = + Botan::BigInt::from_bytes(Botan::BLS12_381::FieldElement::one().negate().serialize()) + 1; + const auto mod_p = Botan::Modular_Reducer::for_public_modulus(p); + + auto fp2_to_bn = [](const FieldElement2& x) { + return std::pair{Botan::BigInt::from_bytes(x.c0().serialize()), + Botan::BigInt::from_bytes(x.c1().serialize())}; + }; + + auto bn_eq = + [&](const char* what, const FieldElement2& x, const Botan::BigInt& c0_bn, const Botan::BigInt& c1_bn) { + result.test_bin_eq(what, x.c0().serialize(), mod_p.reduce(c0_bn).serialize(48)); + result.test_bin_eq(what, x.c1().serialize(), mod_p.reduce(c1_bn).serialize(48)); + }; + + for(size_t i = 0; i != 256; ++i) { + const auto x = random_fp2(rng()); + const auto y = random_fp2(rng()); + + const auto [x0, x1] = fp2_to_bn(x); + const auto [y0, y1] = fp2_to_bn(y); + + bn_eq("Addition", x + y, x0 + y0, x1 + y1); + bn_eq("Subtraction", x - y, x0 - y0, x1 - y1); + bn_eq("Multiplication", x * y, x0 * y0 - x1 * y1, x0 * y1 + x1 * y0); + bn_eq("Squaring", x.square(), x0 * x0 - x1 * x1, x0 * x1 + x1 * x0); + bn_eq("Negation", x.negate(), -x0, -x1); + bn_eq("Conjugation", x.conjugate(), x0, -x1); + bn_eq("Nonresidue mul", x.mul_by_nonresidue(), x0 - x1, x0 + x1); + + const auto xinv = x.invert(); + result.test_is_true( + "Inversion", + x.is_zero().as_bool() ? xinv.is_zero().as_bool() : ((x * xinv) == FieldElement2::one()).as_bool()); + } + + result.end_timer(); + + return result; + } + + Test::Result fp2_sqrt() const { + using Botan::BLS12_381::FieldElement2; + + Test::Result result("Fp2 sqrt"); + + result.start_timer(); + + for(size_t i = 0; i != 64; ++i) { + const auto x = random_fp2(rng()); + const auto x2 = x.square(); + + const auto s = x2.sqrt(); + if(result.test_is_true("sqrt of a square exists", s.has_value())) { + result.test_is_true("sqrt returns +-x", (*s == x || *s == x.negate()).as_bool()); + } + + // (u+1) is a quadratic nonresidue in Fp2 + if(!x.is_zero().as_bool()) { + result.test_is_true("sqrt of a nonsquare fails", !x2.mul_by_nonresidue().sqrt().has_value()); + } + } + + const auto zero_sqrt = FieldElement2::zero().sqrt(); + if(result.test_is_true("sqrt of zero exists", zero_sqrt.has_value())) { + result.test_is_true("sqrt of zero is zero", zero_sqrt->is_zero().as_bool()); + } + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_fp2", BLS12_381_Fp2_Tests); + +class BLS12_381_Fp2_Arith_Tests final : public Text_Based_Test { + public: + BLS12_381_Fp2_Arith_Tests() : Text_Based_Test("bls12_381/fp2_arith.vec", "X,Y,A,S,M,I") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + using Botan::BLS12_381::FieldElement2; + + Test::Result result("BLS12-381 Fp2 arithmetic KAT"); + + const auto x = FieldElement2::deserialize(vars.get_req_bin("X")); + const auto y = FieldElement2::deserialize(vars.get_req_bin("Y")); + + result.test_is_true("X is accepted", x.has_value()); + result.test_is_true("Y is accepted", y.has_value()); + + result.test_bin_eq("X+Y", (*x + *y).serialize(), vars.get_req_bin("A")); + result.test_bin_eq("X-Y", (*x - *y).serialize(), vars.get_req_bin("S")); + result.test_bin_eq("X*Y", (*x * *y).serialize(), vars.get_req_bin("M")); + result.test_bin_eq("1/X", x->invert().serialize(), vars.get_req_bin("I")); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_fp2_arith", BLS12_381_Fp2_Arith_Tests); + +Botan::BLS12_381::Fp6 random_fp6(Botan::RandomNumberGenerator& rng) { + return Botan::BLS12_381::Fp6(random_fp2(rng), random_fp2(rng), random_fp2(rng)); +} + +Botan::BLS12_381::Fp12 random_fp12(Botan::RandomNumberGenerator& rng) { + return Botan::BLS12_381::Fp12(random_fp6(rng), random_fp6(rng)); +} + +Botan::BLS12_381::Fp12 fp12_pow(const Botan::BLS12_381::Fp12& x, const Botan::BigInt& e) { + auto r = Botan::BLS12_381::Fp12::one(); + for(size_t i = e.bits(); i > 0; --i) { + r = r.square(); + if(e.get_bit(i - 1)) { + r = r * x; + } + } + return r; +} + +std::optional fp12_deserialize(std::span bytes) { + using namespace Botan::BLS12_381; + + if(bytes.size() != Fp12::BYTES) { + return {}; + } + + std::array coeffs; + for(size_t i = 0; i != coeffs.size(); ++i) { + auto fe = FieldElement::deserialize(bytes.subspan(i * FieldElement::BYTES, FieldElement::BYTES)); + if(!fe) { + return {}; + } + coeffs[i] = *fe; + } + + auto fp6_at = [&](size_t base) { + return Fp6(FieldElement2(coeffs[base], coeffs[base + 1]), + FieldElement2(coeffs[base + 2], coeffs[base + 3]), + FieldElement2(coeffs[base + 4], coeffs[base + 5])); + }; + + return Fp12(fp6_at(0), fp6_at(6)); +} + +class BLS12_381_Tower_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(fp6_ops()); + results.push_back(fp12_ops()); + results.push_back(frobenius()); + + return results; + } + + private: + Test::Result fp6_ops() const { + using namespace Botan::BLS12_381; + + Test::Result result("Fp6 operations"); + + result.start_timer(); + + result.test_is_true("one is not zero", !Fp6::one().is_zero().as_bool()); + result.test_is_true("zero is zero", Fp6::zero().is_zero().as_bool()); + result.test_is_true("inverse of zero is zero", Fp6::zero().invert().is_zero().as_bool()); + + for(size_t i = 0; i != 64; ++i) { + const auto x = random_fp6(rng()); + const auto y = random_fp6(rng()); + const auto z = random_fp6(rng()); + + result.test_is_true("addition commutes", (x + y == y + x).as_bool()); + result.test_is_true("multiplication commutes", (x * y == y * x).as_bool()); + result.test_is_true("multiplication associates", ((x * y) * z == x * (y * z)).as_bool()); + result.test_is_true("distributive law", (x * (y + z) == x * y + x * z).as_bool()); + result.test_is_true("squaring matches multiplication", (x.square() == x * x).as_bool()); + result.test_is_true("negation", (x - x).is_zero().as_bool()); // NOLINT(*-redundant-expression) + + if(!x.is_zero().as_bool()) { + result.test_is_true("x times inverse is one", (x * x.invert() == Fp6::one()).as_bool()); + } + + const auto b0 = random_fp2(rng()); + const auto b1 = random_fp2(rng()); + + result.test_is_true( + "mul_by_nonresidue matches", + (x.mul_by_nonresidue() == x * Fp6(FieldElement2::zero(), FieldElement2::one(), FieldElement2::zero())) + .as_bool()); + result.test_is_true( + "mul_by_1 matches", + (x.mul_by_1(b1) == x * Fp6(FieldElement2::zero(), b1, FieldElement2::zero())).as_bool()); + result.test_is_true("mul_by_01 matches", + (x.mul_by_01(b0, b1) == x * Fp6(b0, b1, FieldElement2::zero())).as_bool()); + } + + result.end_timer(); + + return result; + } + + Test::Result fp12_ops() const { + using namespace Botan::BLS12_381; + + Test::Result result("Fp12 operations"); + + result.start_timer(); + + result.test_is_true("one is not zero", !Fp12::one().is_zero().as_bool()); + result.test_is_true("inverse of zero is zero", Fp12::zero().invert().is_zero().as_bool()); + + for(size_t i = 0; i != 64; ++i) { + const auto x = random_fp12(rng()); + const auto y = random_fp12(rng()); + const auto z = random_fp12(rng()); + + result.test_is_true("addition commutes", (x + y == y + x).as_bool()); + result.test_is_true("multiplication commutes", (x * y == y * x).as_bool()); + result.test_is_true("multiplication associates", ((x * y) * z == x * (y * z)).as_bool()); + result.test_is_true("distributive law", (x * (y + z) == x * y + x * z).as_bool()); + result.test_is_true("squaring matches multiplication", (x.square() == x * x).as_bool()); + + if(!x.is_zero().as_bool()) { + result.test_is_true("x times inverse is one", (x * x.invert() == Fp12::one()).as_bool()); + } + + const auto b0 = random_fp2(rng()); + const auto b1 = random_fp2(rng()); + const auto b4 = random_fp2(rng()); + + const auto sparse = + Fp12(Fp6(b0, b1, FieldElement2::zero()), Fp6(FieldElement2::zero(), b4, FieldElement2::zero())); + result.test_is_true("mul_by_014 matches", (x.mul_by_014(b0, b1, b4) == x * sparse).as_bool()); + + // An element of the cyclotomic subgroup, via the easy part of + // the final exponentiation: g = f^((p^6-1)(p^2+1)) + if(!x.is_zero().as_bool()) { + const auto t = x.conjugate() * x.invert(); + const auto g = t.frobenius_map().frobenius_map() * t; + result.test_is_true("cyclotomic squaring matches", (g.cyclotomic_square() == g.square()).as_bool()); + } + } + + result.end_timer(); + + return result; + } + + Test::Result frobenius() const { + using namespace Botan::BLS12_381; + + Test::Result result("Fp12 Frobenius"); + + result.start_timer(); + + const Botan::BigInt p = Botan::BigInt::from_bytes(FieldElement::one().negate().serialize()) + 1; + + for(size_t i = 0; i != 8; ++i) { + const auto x = random_fp12(rng()); + + result.test_is_true("frobenius is x^p", (x.frobenius_map() == fp12_pow(x, p)).as_bool()); + + auto frob12 = x; + for(size_t j = 0; j != 12; ++j) { + frob12 = frob12.frobenius_map(); + } + result.test_is_true("frobenius^12 is the identity map", (frob12 == x).as_bool()); + + auto frob6 = x; + for(size_t j = 0; j != 6; ++j) { + frob6 = frob6.frobenius_map(); + } + result.test_is_true("frobenius^6 is conjugation", (frob6 == x.conjugate()).as_bool()); + + const auto y = random_fp12(rng()); + result.test_is_true("frobenius is multiplicative", + ((x * y).frobenius_map() == x.frobenius_map() * y.frobenius_map()).as_bool()); + } + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_tower", BLS12_381_Tower_Tests); + +class BLS12_381_Fp12_Arith_Tests final : public Text_Based_Test { + public: + BLS12_381_Fp12_Arith_Tests() : Text_Based_Test("bls12_381/fp12_arith.vec", "X,Y,M,I") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + using Botan::BLS12_381::Fp12; + + Test::Result result("BLS12-381 Fp12 arithmetic KAT"); + + const auto x = fp12_deserialize(vars.get_req_bin("X")); + const auto y = fp12_deserialize(vars.get_req_bin("Y")); + + result.test_is_true("X is accepted", x.has_value()); + result.test_is_true("Y is accepted", y.has_value()); + + result.test_bin_eq("X*Y", (*x * *y).serialize(), vars.get_req_bin("M")); + result.test_bin_eq("1/X", x->invert().serialize(), vars.get_req_bin("I")); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_fp12_arith", BLS12_381_Fp12_Arith_Tests); + +class BLS12_381_G1_Mul_Tests final : public Text_Based_Test { + public: + BLS12_381_G1_Mul_Tests() : Text_Based_Test("bls12_381/g1_mul.vec", "P,K,Z") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("BLS12-381 G1 mul"); + + const auto pt = Botan::BLS12_381::G1Affine::deserialize(vars.get_req_bin("P")); + const auto k = Botan::BLS12_381::Scalar::deserialize(vars.get_req_bin("K")); + const auto z = vars.get_req_bin("Z"); + + result.test_is_true("P is accepted", pt.has_value()); + result.test_is_true("K is accepted", k.has_value()); + + auto cz = Botan::BLS12_381::G1Projective::from_affine(pt.value()).mul(k.value()); + + result.test_bin_eq("Expected Z", cz.to_affine().serialize(), z); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_g1_mul", BLS12_381_G1_Mul_Tests); + +class BLS12_381_G1_Deser_Tests final : public Text_Based_Test { + public: + BLS12_381_G1_Deser_Tests() : Text_Based_Test("bls12_381/g1_deser.vec", "P") {} + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + Test::Result result("BLS12-381 G1 deserialization"); + + const auto bytes = vars.get_req_bin("P"); + const auto pt = Botan::BLS12_381::G1Affine::deserialize(bytes); + + if(header == "Valid") { + if(result.test_is_true("Valid encoding accepted", pt.has_value())) { + result.test_bin_eq("Round trip", pt->serialize(), bytes); + } + } else { + result.test_is_true("Invalid encoding rejected", !pt.has_value()); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_g1_deser", BLS12_381_G1_Deser_Tests); + +class BLS12_381_G1_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(g1_group_law()); + results.push_back(g1_mul2()); + results.push_back(g1_msm()); + results.push_back(g1_batch_affine()); + + return results; + } + + private: + Test::Result g1_mul2() const { + using Botan::BLS12_381::G1Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G1 2-ary multiplication"); + + result.start_timer(); + + const auto g = G1Projective::generator(); + + for(size_t i = 0; i != 8; ++i) { + const auto p = g.mul(random_test_elem(rng())); + const auto q = g.mul(random_test_elem(rng())); + const auto a = random_test_elem(rng()); + const auto b = random_test_elem(rng()); + + const auto ref = p.mul(a).add(q.mul(b)); + + result.test_bin_eq("mul2 matches mul+add", + G1Projective::mul2(p, a, q, b).to_affine().serialize(), + ref.to_affine().serialize()); + result.test_bin_eq("mul2_vartime matches mul+add", + G1Projective::mul2_vartime(p, a, q, b).to_affine().serialize(), + ref.to_affine().serialize()); + + // p == q exercises the doubling case within additions + result.test_bin_eq("mul2 with equal points", + G1Projective::mul2(p, a, p, b).to_affine().serialize(), + p.mul(a + b).to_affine().serialize()); + + result.test_bin_eq("mul2 with zero scalar", + G1Projective::mul2(p, Scalar::zero(), q, b).to_affine().serialize(), + q.mul(b).to_affine().serialize()); + result.test_bin_eq("mul2_vartime with zero scalar", + G1Projective::mul2_vartime(p, a, q, Scalar::zero()).to_affine().serialize(), + p.mul(a).to_affine().serialize()); + + result.test_bin_eq("mul2 with identity point", + G1Projective::mul2(G1Projective::identity(), a, q, b).to_affine().serialize(), + q.mul(b).to_affine().serialize()); + } + + result.test_is_true("mul2 of zeros is the identity", + G1Projective::mul2(g, Scalar::zero(), g, Scalar::zero()).is_identity()); + result.test_is_true("mul2_vartime of zeros is the identity", + G1Projective::mul2_vartime(g, Scalar::zero(), g, Scalar::zero()).is_identity()); + + result.end_timer(); + + return result; + } + + Test::Result g1_msm() const { + using Botan::BLS12_381::G1Affine; + using Botan::BLS12_381::G1Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G1 multiscalar multiplication"); + + result.start_timer(); + + result.test_is_true("Empty sum is the identity", G1Projective::msm_vartime({}, {}).is_identity()); + + result.test_throws("Mismatched span lengths throw", [&]() { + const std::array p{G1Affine::generator()}; + G1Projective::msm_vartime(p, {}); + }); + + const auto g = G1Projective::generator(); + + // Sizes on both sides of the dispatch between the 2-ary chain + // and the Pippenger bucket method + for(const size_t n : {1, 2, 3, 15, 16, 40}) { + std::vector points; + std::vector scalars; + + auto naive = G1Projective::identity(); + for(size_t i = 0; i != n; ++i) { + // Include edge cases: the identity point and special scalars + const auto pt = (i == 1) ? G1Affine::identity() : g.mul(random_test_elem(rng())).to_affine(); + const auto k = random_test_elem(rng()); + + points.push_back(pt); + scalars.push_back(k); + naive = naive.add(G1Projective::from_affine(pt).mul(k)); + } + + const auto msm = G1Projective::msm_vartime(points, scalars); + result.test_bin_eq("MSM matches naive sum for n=" + std::to_string(n), + msm.to_affine().serialize(), + naive.to_affine().serialize()); + } + + result.end_timer(); + + return result; + } + + Test::Result g1_batch_affine() const { + using Botan::BLS12_381::G1Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G1 batch affine conversion"); + + result.start_timer(); + + result.test_sz_eq("Empty batch", G1Projective::to_affine_batch({}).size(), 0); + + const auto g = G1Projective::generator(); + + // Sizes chosen to place identity elements (every third point) + // first, last, and in the interior of the batch + for(const size_t n : {1, 2, 3, 4, 20}) { + std::vector pts; + pts.reserve(n); + for(size_t i = 0; i != n; ++i) { + if(i % 3 == 0) { + pts.push_back(G1Projective::identity()); + } else { + pts.push_back(g.mul(random_test_elem(rng()))); + } + } + + const auto affine = G1Projective::to_affine_batch(pts); + + if(result.test_sz_eq("Batch size matches for n=" + std::to_string(n), affine.size(), n)) { + for(size_t i = 0; i != n; ++i) { + const auto ref = pts[i].to_affine(); + result.test_is_true("Batch identity flag matches", affine[i].is_identity() == ref.is_identity()); + result.test_bin_eq("Batch x matches", affine[i]._x().serialize(), ref._x().serialize()); + result.test_bin_eq("Batch y matches", affine[i]._y().serialize(), ref._y().serialize()); + result.test_bin_eq("Batch serialization matches", affine[i].serialize(), ref.serialize()); + } + } + } + + result.end_timer(); + + return result; + } + + Test::Result g1_group_law() const { + using Botan::BLS12_381::G1Affine; + using Botan::BLS12_381::G1Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G1 group operations"); + + result.start_timer(); + + const auto g = G1Projective::generator(); + + result.test_bin_eq("Affine and projective generator serialize identically", + g.to_affine().serialize(), + G1Affine::generator().serialize()); + + result.test_is_true("Generator is not the identity", !g.is_identity()); + result.test_is_true("Identity is the identity", G1Projective::identity().is_identity()); + + result.test_bin_eq("0*G is the identity", + g.mul(Scalar::zero()).to_affine().serialize(), + G1Projective::identity().to_affine().serialize()); + + // Regression test: to_affine must canonicalize the identity, so + // that round tripping it through from_affine yields a valid + // (non absorbing) projective identity + result.test_bin_eq("Identity affine round trip is not absorbing", + G1Projective::from_affine(g.mul(Scalar::zero()).to_affine()).add(g).to_affine().serialize(), + g.to_affine().serialize()); + + result.test_bin_eq("1*G is G", g.mul(Scalar::one()).to_affine().serialize(), g.to_affine().serialize()); + + for(size_t i = 0; i != 16; ++i) { + const auto a = random_test_elem(rng()); + const auto b = random_test_elem(rng()); + + const auto apb_g = g.mul(a + b); + const auto ag_bg = g.mul(a).add(g.mul(b)); + result.test_bin_eq("(a+b)*G == a*G + b*G", apb_g.to_affine().serialize(), ag_bg.to_affine().serialize()); + + const auto ab_g = g.mul(a * b); + const auto b_ag = g.mul(a).mul(b); + result.test_bin_eq("(a*b)*G == b*(a*G)", ab_g.to_affine().serialize(), b_ag.to_affine().serialize()); + + const auto ag = g.mul(a); + const auto ag_maybe = G1Affine::deserialize(ag.to_affine().serialize()); + if(result.test_is_true("Serialization of a*G accepted", ag_maybe.has_value())) { + result.test_bin_eq("Round trip of a*G", ag_maybe->serialize(), ag.to_affine().serialize()); + } + + const auto sum = ag.add(ag.negate()); + result.test_is_true("P + -P is the identity", sum.is_identity()); + + const auto mixed = ag.add_mixed(g.mul(b).to_affine()); + result.test_bin_eq( + "Mixed and projective addition agree", mixed.to_affine().serialize(), apb_g.to_affine().serialize()); + + const auto plus_identity = ag.add_mixed(G1Affine::identity()); + result.test_bin_eq( + "Mixed addition of the identity", plus_identity.to_affine().serialize(), ag.to_affine().serialize()); + } + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_g1", BLS12_381_G1_Tests); + +class BLS12_381_G2_Mul_Tests final : public Text_Based_Test { + public: + BLS12_381_G2_Mul_Tests() : Text_Based_Test("bls12_381/g2_mul.vec", "P,K,Z") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("BLS12-381 G2 mul"); + + const auto pt = Botan::BLS12_381::G2Affine::deserialize(vars.get_req_bin("P")); + const auto k = Botan::BLS12_381::Scalar::deserialize(vars.get_req_bin("K")); + const auto z = vars.get_req_bin("Z"); + + result.test_is_true("P is accepted", pt.has_value()); + result.test_is_true("K is accepted", k.has_value()); + + auto cz = Botan::BLS12_381::G2Projective::from_affine(pt.value()).mul(k.value()); + + result.test_bin_eq("Expected Z", cz.to_affine().serialize(), z); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_g2_mul", BLS12_381_G2_Mul_Tests); + +class BLS12_381_G2_Deser_Tests final : public Text_Based_Test { + public: + BLS12_381_G2_Deser_Tests() : Text_Based_Test("bls12_381/g2_deser.vec", "P") {} + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + Test::Result result("BLS12-381 G2 deserialization"); + + const auto bytes = vars.get_req_bin("P"); + const auto pt = Botan::BLS12_381::G2Affine::deserialize(bytes); + + if(header == "Valid") { + if(result.test_is_true("Valid encoding accepted", pt.has_value())) { + result.test_bin_eq("Round trip", pt->serialize(), bytes); + } + } else { + result.test_is_true("Invalid encoding rejected", !pt.has_value()); + } + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_g2_deser", BLS12_381_G2_Deser_Tests); + +class BLS12_381_G2_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(g2_group_law()); + results.push_back(g2_mul2()); + results.push_back(g2_msm()); + results.push_back(g2_batch_affine()); + + return results; + } + + private: + Test::Result g2_mul2() const { + using Botan::BLS12_381::G2Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G2 2-ary multiplication"); + + result.start_timer(); + + const auto g = G2Projective::generator(); + + for(size_t i = 0; i != 4; ++i) { + const auto p = g.mul(random_test_elem(rng())); + const auto q = g.mul(random_test_elem(rng())); + const auto a = random_test_elem(rng()); + const auto b = random_test_elem(rng()); + + const auto ref = p.mul(a).add(q.mul(b)); + + result.test_bin_eq("mul2 matches mul+add", + G2Projective::mul2(p, a, q, b).to_affine().serialize(), + ref.to_affine().serialize()); + result.test_bin_eq("mul2_vartime matches mul+add", + G2Projective::mul2_vartime(p, a, q, b).to_affine().serialize(), + ref.to_affine().serialize()); + + // p == q exercises the doubling case within additions + result.test_bin_eq("mul2 with equal points", + G2Projective::mul2(p, a, p, b).to_affine().serialize(), + p.mul(a + b).to_affine().serialize()); + + result.test_bin_eq("mul2 with identity point", + G2Projective::mul2(G2Projective::identity(), a, q, b).to_affine().serialize(), + q.mul(b).to_affine().serialize()); + } + + result.test_is_true("mul2 of zeros is the identity", + G2Projective::mul2(g, Scalar::zero(), g, Scalar::zero()).is_identity()); + + result.end_timer(); + + return result; + } + + Test::Result g2_msm() const { + using Botan::BLS12_381::G2Affine; + using Botan::BLS12_381::G2Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G2 multiscalar multiplication"); + + result.start_timer(); + + result.test_is_true("Empty sum is the identity", G2Projective::msm_vartime({}, {}).is_identity()); + + const auto g = G2Projective::generator(); + + // Sizes on both sides of the dispatch between the 2-ary chain + // and the Pippenger bucket method + for(const size_t n : {1, 2, 3, 15, 16}) { + std::vector points; + std::vector scalars; + + auto naive = G2Projective::identity(); + for(size_t i = 0; i != n; ++i) { + const auto pt = (i == 1) ? G2Affine::identity() : g.mul(random_test_elem(rng())).to_affine(); + const auto k = random_test_elem(rng()); + + points.push_back(pt); + scalars.push_back(k); + naive = naive.add(G2Projective::from_affine(pt).mul(k)); + } + + const auto msm = G2Projective::msm_vartime(points, scalars); + result.test_bin_eq("MSM matches naive sum for n=" + std::to_string(n), + msm.to_affine().serialize(), + naive.to_affine().serialize()); + } + + result.end_timer(); + + return result; + } + + Test::Result g2_batch_affine() const { + using Botan::BLS12_381::G2Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G2 batch affine conversion"); + + result.start_timer(); + + result.test_sz_eq("Empty batch", G2Projective::to_affine_batch({}).size(), 0); + + const auto g = G2Projective::generator(); + + // Sizes chosen to place identity elements (every third point) + // first, last, and in the interior of the batch + for(const size_t n : {1, 2, 3, 4, 20}) { + std::vector pts; + pts.reserve(n); + for(size_t i = 0; i != n; ++i) { + if(i % 3 == 0) { + pts.push_back(G2Projective::identity()); + } else { + pts.push_back(g.mul(random_test_elem(rng()))); + } + } + + const auto affine = G2Projective::to_affine_batch(pts); + + if(result.test_sz_eq("Batch size matches for n=" + std::to_string(n), affine.size(), n)) { + for(size_t i = 0; i != n; ++i) { + const auto ref = pts[i].to_affine(); + result.test_is_true("Batch identity flag matches", affine[i].is_identity() == ref.is_identity()); + result.test_bin_eq("Batch x matches", affine[i]._x().serialize(), ref._x().serialize()); + result.test_bin_eq("Batch y matches", affine[i]._y().serialize(), ref._y().serialize()); + result.test_bin_eq("Batch serialization matches", affine[i].serialize(), ref.serialize()); + } + } + } + + result.end_timer(); + + return result; + } + + Test::Result g2_group_law() const { + using Botan::BLS12_381::G2Affine; + using Botan::BLS12_381::G2Projective; + using Botan::BLS12_381::Scalar; + + Test::Result result("G2 group operations"); + + result.start_timer(); + + const auto g = G2Projective::generator(); + + result.test_bin_eq("Affine and projective generator serialize identically", + g.to_affine().serialize(), + G2Affine::generator().serialize()); + + result.test_is_true("Generator is not the identity", !g.is_identity()); + result.test_is_true("Identity is the identity", G2Projective::identity().is_identity()); + + result.test_bin_eq("0*G is the identity", + g.mul(Scalar::zero()).to_affine().serialize(), + G2Projective::identity().to_affine().serialize()); + + // Regression test: to_affine must canonicalize the identity, so + // that round tripping it through from_affine yields a valid + // (non absorbing) projective identity + result.test_bin_eq("Identity affine round trip is not absorbing", + G2Projective::from_affine(g.mul(Scalar::zero()).to_affine()).add(g).to_affine().serialize(), + g.to_affine().serialize()); + + result.test_bin_eq("1*G is G", g.mul(Scalar::one()).to_affine().serialize(), g.to_affine().serialize()); + + for(size_t i = 0; i != 8; ++i) { + const auto a = random_test_elem(rng()); + const auto b = random_test_elem(rng()); + + const auto apb_g = g.mul(a + b); + const auto ag_bg = g.mul(a).add(g.mul(b)); + result.test_bin_eq("(a+b)*G == a*G + b*G", apb_g.to_affine().serialize(), ag_bg.to_affine().serialize()); + + const auto ab_g = g.mul(a * b); + const auto b_ag = g.mul(a).mul(b); + result.test_bin_eq("(a*b)*G == b*(a*G)", ab_g.to_affine().serialize(), b_ag.to_affine().serialize()); + + const auto ag = g.mul(a); + const auto ag_maybe = G2Affine::deserialize(ag.to_affine().serialize()); + if(result.test_is_true("Serialization of a*G accepted", ag_maybe.has_value())) { + result.test_bin_eq("Round trip of a*G", ag_maybe->serialize(), ag.to_affine().serialize()); + } + + const auto sum = ag.add(ag.negate()); + result.test_is_true("P + -P is the identity", sum.is_identity()); + + const auto mixed = ag.add_mixed(g.mul(b).to_affine()); + result.test_bin_eq( + "Mixed and projective addition agree", mixed.to_affine().serialize(), apb_g.to_affine().serialize()); + + const auto plus_identity = ag.add_mixed(G2Affine::identity()); + result.test_bin_eq( + "Mixed addition of the identity", plus_identity.to_affine().serialize(), ag.to_affine().serialize()); + } + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_g2", BLS12_381_G2_Tests); + +class BLS12_381_Pairing_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(pairing_properties()); + + return results; + } + + private: + Test::Result pairing_properties() const { + using namespace Botan::BLS12_381; + + Test::Result result("Pairing properties"); + + result.start_timer(); + + const auto g1 = G1Projective::generator(); + const auto g2 = G2Projective::generator(); + + const auto e_g1_g2 = Gt::pairing(G1Affine::generator(), G2Affine::generator()); + result.test_is_true("Non-degeneracy", !e_g1_g2.is_identity()); + + result.test_is_true("Identity in G1 absorbs", + Gt::pairing(G1Affine::identity(), G2Affine::generator()).is_identity()); + result.test_is_true("Identity in G2 absorbs", + Gt::pairing(G1Affine::generator(), G2Affine::identity()).is_identity()); + + result.test_is_true("Empty product is the identity", Gt::multi_pairing({}, {}).is_identity()); + + result.test_throws("Mismatched span lengths throw", [&]() { + const std::array p{G1Affine::generator()}; + Gt::multi_pairing(p, {}); + }); + + for(size_t i = 0; i != 4; ++i) { + const auto a = random_test_elem(rng()); + const auto b = random_test_elem(rng()); + + const auto a_g1 = g1.mul(a).to_affine(); + const auto b_g2 = g2.mul(b).to_affine(); + const auto b_g1 = g1.mul(b).to_affine(); + const auto a_g2 = g2.mul(a).to_affine(); + const auto ab_g2 = g2.mul(a * b).to_affine(); + + const auto e_ab = Gt::pairing(a_g1, b_g2); + result.test_is_true("e(aG1, bG2) == e(bG1, aG2)", e_ab == Gt::pairing(b_g1, a_g2)); + result.test_is_true("e(aG1, bG2) == e(G1, abG2)", e_ab == Gt::pairing(G1Affine::generator(), ab_g2)); + + // multi_pairing({P, -P}, {Q, Q}) == identity + const auto neg_a_g1 = g1.mul(a).negate().to_affine(); + const std::array ps{a_g1, neg_a_g1}; + const std::array qs{b_g2, b_g2}; + result.test_is_true("e(P, Q)*e(-P, Q) is the identity", Gt::multi_pairing(ps, qs).is_identity()); + } + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_pairing", BLS12_381_Pairing_Tests); + +class BLS12_381_Pairing_KAT_Tests final : public Text_Based_Test { + public: + BLS12_381_Pairing_KAT_Tests() : Text_Based_Test("bls12_381/pairing.vec", "A,B,E") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + using namespace Botan::BLS12_381; + + Test::Result result("BLS12-381 pairing KAT"); + + const auto a = G1Affine::deserialize(vars.get_req_bin("A")); + const auto b = G2Affine::deserialize(vars.get_req_bin("B")); + + result.test_is_true("A is accepted", a.has_value()); + result.test_is_true("B is accepted", b.has_value()); + + result.test_bin_eq("e(A, B)", Gt::pairing(*a, *b).serialize(), vars.get_req_bin("E")); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_pairing_kat", BLS12_381_Pairing_KAT_Tests); + +class BLS12_381_Multi_Pairing_KAT_Tests final : public Text_Based_Test { + public: + BLS12_381_Multi_Pairing_KAT_Tests() : Text_Based_Test("bls12_381/multi_pairing.vec", "A1,B1,A2,B2,E") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + using namespace Botan::BLS12_381; + + Test::Result result("BLS12-381 multi-pairing KAT"); + + const auto a1 = G1Affine::deserialize(vars.get_req_bin("A1")); + const auto b1 = G2Affine::deserialize(vars.get_req_bin("B1")); + const auto a2 = G1Affine::deserialize(vars.get_req_bin("A2")); + const auto b2 = G2Affine::deserialize(vars.get_req_bin("B2")); + + result.test_is_true("Points accepted", a1.has_value() && b1.has_value() && a2.has_value() && b2.has_value()); + + const std::array ps{*a1, *a2}; + const std::array qs{*b1, *b2}; + + result.test_bin_eq("e(A1, B1)*e(A2, B2)", Gt::multi_pairing(ps, qs).serialize(), vars.get_req_bin("E")); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_multi_pairing_kat", BLS12_381_Multi_Pairing_KAT_Tests); + +class BLS12_381_H2C_G1_Tests final : public Text_Based_Test { + public: + BLS12_381_H2C_G1_Tests() : Text_Based_Test("bls12_381/h2c_g1.vec", "Msg,Dst,PX,PY") {} + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + using Botan::BLS12_381::G1Projective; + + Test::Result result("BLS12-381 G1 hash to curve"); + + const auto msg = vars.get_req_bin("Msg"); + const auto dst = vars.get_req_bin("Dst"); + + const auto pt = + (header == "RO") ? G1Projective::hash_to_curve_ro(msg, dst) : G1Projective::hash_to_curve_nu(msg, dst); + + const auto affine = pt.to_affine(); + result.test_bin_eq("P.x", affine._x().serialize(), vars.get_req_bin("PX")); + result.test_bin_eq("P.y", affine._y().serialize(), vars.get_req_bin("PY")); + + // The result must be in the prime order subgroup + const auto reparsed = Botan::BLS12_381::G1Affine::deserialize(affine.serialize()); + result.test_is_true("Output accepted by deserialize", reparsed.has_value()); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_h2c_g1", BLS12_381_H2C_G1_Tests); + +class BLS12_381_H2C_G2_Tests final : public Text_Based_Test { + public: + BLS12_381_H2C_G2_Tests() : Text_Based_Test("bls12_381/h2c_g2.vec", "Msg,Dst,PX0,PX1,PY0,PY1") {} + + Test::Result run_one_test(const std::string& header, const VarMap& vars) override { + using Botan::BLS12_381::G2Projective; + + Test::Result result("BLS12-381 G2 hash to curve"); + + const auto msg = vars.get_req_bin("Msg"); + const auto dst = vars.get_req_bin("Dst"); + + const auto pt = + (header == "RO") ? G2Projective::hash_to_curve_ro(msg, dst) : G2Projective::hash_to_curve_nu(msg, dst); + + const auto affine = pt.to_affine(); + result.test_bin_eq("P.x c0", affine._x().c0().serialize(), vars.get_req_bin("PX0")); + result.test_bin_eq("P.x c1", affine._x().c1().serialize(), vars.get_req_bin("PX1")); + result.test_bin_eq("P.y c0", affine._y().c0().serialize(), vars.get_req_bin("PY0")); + result.test_bin_eq("P.y c1", affine._y().c1().serialize(), vars.get_req_bin("PY1")); + + // The result must be in the prime order subgroup + const auto reparsed = Botan::BLS12_381::G2Affine::deserialize(affine.serialize()); + result.test_is_true("Output accepted by deserialize", reparsed.has_value()); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_h2c_g2", BLS12_381_H2C_G2_Tests); + +class BLS12_381_H2Scalar_Tests final : public Text_Based_Test { + public: + BLS12_381_H2Scalar_Tests() : Text_Based_Test("bls12_381/h2scalar.vec", "Msg,Dst,S") {} + + Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override { + Test::Result result("BLS12-381 hash to scalar"); + + const auto msg = vars.get_req_bin("Msg"); + const auto dst = vars.get_req_bin("Dst"); + + result.test_bin_eq( + "Expected scalar", Botan::BLS12_381::Scalar::hash(msg, dst).serialize(), vars.get_req_bin("S")); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_h2scalar", BLS12_381_H2Scalar_Tests); + +class BLS12_381_H2C_DST_Tests final : public Test { + public: + std::vector run() override { + std::vector results; + + results.push_back(dst_handling()); + + return results; + } + + private: + Test::Result dst_handling() const { + using namespace Botan::BLS12_381; + + Test::Result result("Hash to curve DST handling"); + + result.start_timer(); + + const auto msg = Botan::hex_decode("F00F"); + + result.test_throws("G1 RO rejects empty DST", [&]() { G1Projective::hash_to_curve_ro(msg, {}); }); + result.test_throws("G1 NU rejects empty DST", [&]() { G1Projective::hash_to_curve_nu(msg, {}); }); + result.test_throws("G2 RO rejects empty DST", [&]() { G2Projective::hash_to_curve_ro(msg, {}); }); + result.test_throws("G2 NU rejects empty DST", [&]() { G2Projective::hash_to_curve_nu(msg, {}); }); + + // A DST longer than 255 bytes must be replaced by + // H("H2C-OVERSIZE-DST-" || dst) per RFC 9380 5.3.3 + const std::string long_dst_str(300, 'x'); + const std::vector long_dst(long_dst_str.begin(), long_dst_str.end()); + + auto sha256 = Botan::HashFunction::create_or_throw("SHA-256"); + sha256->update("H2C-OVERSIZE-DST-"); + sha256->update(long_dst); + const auto hashed_dst = sha256->final_stdvec(); + + result.test_bin_eq("G1 RO long DST hashed", + G1Projective::hash_to_curve_ro(msg, long_dst).to_affine().serialize(), + G1Projective::hash_to_curve_ro(msg, hashed_dst).to_affine().serialize()); + result.test_bin_eq("G2 RO long DST hashed", + G2Projective::hash_to_curve_ro(msg, long_dst).to_affine().serialize(), + G2Projective::hash_to_curve_ro(msg, hashed_dst).to_affine().serialize()); + result.test_bin_eq("G1 NU long DST hashed", + G1Projective::hash_to_curve_nu(msg, long_dst).to_affine().serialize(), + G1Projective::hash_to_curve_nu(msg, hashed_dst).to_affine().serialize()); + result.test_bin_eq("Scalar hash long DST hashed", + Scalar::hash(msg, long_dst).serialize(), + Scalar::hash(msg, hashed_dst).serialize()); + + result.end_timer(); + + return result; + } +}; + +BOTAN_REGISTER_TEST("bls12_381", "bls12_381_h2c_dst", BLS12_381_H2C_DST_Tests); + +#endif + +} // namespace + +} // namespace Botan_Tests