Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Commit

Permalink
Input checks for EC precompiles.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Feb 24, 2017
1 parent 94880a2 commit 1746645
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 71 deletions.
1 change: 1 addition & 0 deletions libdevcrypto/Exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace crypto

/// Rare malfunction of cryptographic functions.
DEV_SIMPLE_EXCEPTION(CryptoException);
DEV_SIMPLE_EXCEPTION(InvalidEncoding);

}
}
162 changes: 101 additions & 61 deletions libdevcrypto/LibSnark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,21 @@
#include <libsnark/algebra/curves/alt_bn128/alt_bn128_pp.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>

#include <libdevcrypto/Exceptions.h>

#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
#include <libdevcore/FixedHash.h>

#include <fstream>

using namespace std;
using namespace dev;
using namespace dev::snark;
using namespace dev::crypto;

namespace
{


void initLibSnark()
{
static bool initialized = 0;
Expand All @@ -54,8 +56,6 @@ void initLibSnark()
}
}

}

libsnark::bigint<libsnark::alt_bn128_q_limbs> toLibsnarkBigint(h256 const& _x)
{
libsnark::bigint<libsnark::alt_bn128_q_limbs> x;
Expand All @@ -76,106 +76,146 @@ h256 fromLibsnarkBigint(libsnark::bigint<libsnark::alt_bn128_q_limbs> _x)

libsnark::alt_bn128_Fq decodeFqElement(dev::bytesConstRef _data)
{
return toLibsnarkBigint(h256(_data.cropped(0, 32)));
// h256::AlignLeft ensures that the h256 is zero-filled on the right if _data
// is too short.
h256 xbin(_data, h256::AlignLeft);
if (u256(xbin) >= u256(fromLibsnarkBigint(libsnark::alt_bn128_Fq::mod)))
BOOST_THROW_EXCEPTION(InvalidEncoding());
return toLibsnarkBigint(xbin);
}

libsnark::alt_bn128_G1 decodePointG1(dev::bytesConstRef _data)
{
return libsnark::alt_bn128_G1(
decodeFqElement(_data.cropped(0, 32)),
decodeFqElement(_data.cropped(32, 32)),
decodeFqElement(_data.cropped(64, 32))
);
libsnark::alt_bn128_Fq X = decodeFqElement(_data.cropped(0));
libsnark::alt_bn128_Fq Y = decodeFqElement(_data.cropped(32));
if (X == libsnark::alt_bn128_Fq::zero() && Y == libsnark::alt_bn128_Fq::zero())
return libsnark::alt_bn128_G1::zero();
libsnark::alt_bn128_G1 p(X, Y, libsnark::alt_bn128_Fq::one());
if (!p.is_well_formed())
BOOST_THROW_EXCEPTION(InvalidEncoding());
return p;
}

bytes encodePointG1(libsnark::alt_bn128_G1 _p)
{
libsnark::alt_bn128_G1 p_norm = _p;
p_norm.to_affine_coordinates();
if (_p.is_zero())
return h256().asBytes() + h256().asBytes();
_p.to_affine_coordinates();
return
fromLibsnarkBigint(p_norm.X.as_bigint()).asBytes() +
fromLibsnarkBigint(p_norm.Y.as_bigint()).asBytes() +
fromLibsnarkBigint(p_norm.Z.as_bigint()).asBytes();
fromLibsnarkBigint(_p.X.as_bigint()).asBytes() +
fromLibsnarkBigint(_p.Y.as_bigint()).asBytes();
}

libsnark::alt_bn128_Fq2 decodeFq2Element(dev::bytesConstRef _data)
{
// Encoding: c1 (256 bits) c0 (256 bits)
// "Big endian", just like the numbers
return libsnark::alt_bn128_Fq2(
decodeFqElement(_data.cropped(32, 32)),
decodeFqElement(_data.cropped(0, 32))
decodeFqElement(_data.cropped(32)),
decodeFqElement(_data.cropped(0))
);
}


libsnark::alt_bn128_G2 decodePointG2(dev::bytesConstRef _data)
{
return libsnark::alt_bn128_G2(
decodeFq2Element(_data.cropped(0, 64)),
decodeFq2Element(_data.cropped(64, 64)),
decodeFq2Element(_data.cropped(128, 64))
);
libsnark::alt_bn128_Fq2 X = decodeFq2Element(_data);
libsnark::alt_bn128_Fq2 Y = decodeFq2Element(_data.cropped(64));
if (X == libsnark::alt_bn128_Fq2::zero() && Y == libsnark::alt_bn128_Fq2::zero())
return libsnark::alt_bn128_G2::zero();
libsnark::alt_bn128_G2 p(X, Y, libsnark::alt_bn128_Fq2::one());
if (!p.is_well_formed())
BOOST_THROW_EXCEPTION(InvalidEncoding());
return p;
}

}

bytes dev::snark::alt_bn128_pairing_product(dev::bytesConstRef _in)
pair<bool, bytes> dev::crypto::alt_bn128_pairing_product(dev::bytesConstRef _in)
{
initLibSnark();
// Input: list of pairs of G1 and G2 points
// Output: 1 if pairing evaluates to 1, 0 otherwise (left-padded to 32 bytes)

// TODO catch exceptions from assertions
// TODO check that the second points are part of the correct subgroup

size_t const pairSize = 3 * 32 + 3 * 64;
// TODO this does not round correctly
bool result = true;
size_t const pairSize = 2 * 32 + 2 * 64;
size_t const pairs = _in.size() / pairSize;
libsnark::alt_bn128_Fq12 x = libsnark::alt_bn128_Fq12::one();
for (size_t i = 0; i < pairs; ++i)
if (pairs * pairSize != _in.size())
{
// Invalid length.
return make_pair(false, bytes());
}
try
{
initLibSnark();
libsnark::alt_bn128_Fq12 x = libsnark::alt_bn128_Fq12::one();
for (size_t i = 0; i < pairs; ++i)
{
dev::bytesConstRef pair = _in.cropped(i * pairSize, pairSize);
libsnark::alt_bn128_G2 p = decodePointG2(pair.cropped(2 * 32));
if (libsnark::alt_bn128_G2::order() * p != libsnark::alt_bn128_G2::one())
// p is not an element of the group (has wrong order)
return {false, bytes()};
x = x * libsnark::alt_bn128_miller_loop(
libsnark::alt_bn128_precompute_G1(decodePointG1(pair)),
libsnark::alt_bn128_precompute_G2(p)
);
}
result = libsnark::alt_bn128_final_exponentiation(x) == libsnark::alt_bn128_GT::one();
}
catch (InvalidEncoding const&)
{
return make_pair(false, bytes());
}
catch (...)
{
dev::bytesConstRef pair = _in.cropped(i * pairSize, pairSize);
x = x * libsnark::alt_bn128_miller_loop(
libsnark::alt_bn128_precompute_G1(decodePointG1(pair)),
libsnark::alt_bn128_precompute_G2(decodePointG2(pair.cropped(3 * 32)))
);
cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure.";
return make_pair(false, bytes());
}
bool result = libsnark::alt_bn128_final_exponentiation(x) == libsnark::alt_bn128_GT::one();

bytes res(32, 0);
res[31] = unsigned(result);
return res;
return {true, res};
}

bytes dev::snark::alt_bn128_G1_add(dev::bytesConstRef _in)
pair<bool, bytes> dev::crypto::alt_bn128_G1_add(dev::bytesConstRef _in)
{
initLibSnark();
// Elliptic curve point addition in Jacobian, big endian encoding:
// (P1.X: 256 bits, P1.Y: 256 bits, P1.Z: 256 bits,
// P2.X: 256 bits, P2.Y: 256 bits, P2.Z: 256 bits)

// TODO: This cannot be final code because it behaves incorrectly if
// the input is too short (decoder returns zero instead of filling with zeros).
libsnark::alt_bn128_G1 p1 = decodePointG1(_in);
libsnark::alt_bn128_G1 p2 = decodePointG1(_in.cropped(32 * 3));
try
{
initLibSnark();
libsnark::alt_bn128_G1 p1 = decodePointG1(_in);
libsnark::alt_bn128_G1 p2 = decodePointG1(_in.cropped(32 * 3));

return encodePointG1(p1 + p2);
return {true, encodePointG1(p1 + p2)};
}
catch (InvalidEncoding const&)
{
}
catch (...)
{
cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure.";
}
return make_pair(false, bytes());
}

bytes dev::snark::alt_bn128_G1_mul(dev::bytesConstRef _in)
pair<bool, bytes> dev::crypto::alt_bn128_G1_mul(dev::bytesConstRef _in)
{
initLibSnark();
// Scalar multiplication with a curve point in Jacobian encoding, big endian:
// (s: u256, X: 256 bits, Y: 256 bits, Z: 256 bits)

// TODO: This cannot be final code because it behaves incorrectly if
// the input is too short (decoder returns zero instead of filling with zeros).
u256 s = h256(_in.cropped(0, 32));
libsnark::alt_bn128_G1 p = decodePointG1(_in.cropped(32));
try
{
initLibSnark();
libsnark::alt_bn128_G1 p = decodePointG1(_in.cropped(0));

libsnark::alt_bn128_G1 result = libsnark::bigint<libsnark::alt_bn128_q_limbs>(s.str().c_str()) * p;
libsnark::alt_bn128_G1 result = toLibsnarkBigint(h256(_in.cropped(64), h256::AlignLeft)) * p;

return encodePointG1(result);
return {true, encodePointG1(result)};
}
catch (InvalidEncoding const&)
{
}
catch (...)
{
cwarn << "Internal exception from libsnark. Forwarding as precompiled contract failure.";
}
return make_pair(false, bytes());
}

std::string outputPointG1Affine(libsnark::alt_bn128_G1 _p)
Expand Down Expand Up @@ -276,7 +316,7 @@ void testProof(libsnark::r1cs_ppzksnark_verification_key<libsnark::alt_bn128_pp>
cout << "Verified: " << verified << endl;
}

void dev::snark::exportVK(string const& _VKFilename)
void dev::crypto::exportVK(string const& _VKFilename)
{
initLibSnark();

Expand Down
8 changes: 4 additions & 4 deletions libdevcrypto/LibSnark.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@

namespace dev
{
namespace snark
namespace crypto
{

bytes alt_bn128_pairing_product(bytesConstRef _in);
bytes alt_bn128_G1_add(bytesConstRef _in);
bytes alt_bn128_G1_mul(bytesConstRef _in);
std::pair<bool, bytes> alt_bn128_pairing_product(bytesConstRef _in);
std::pair<bool, bytes> alt_bn128_G1_add(bytesConstRef _in);
std::pair<bool, bytes> alt_bn128_G1_mul(bytesConstRef _in);

void exportVK(std::string const& _VKFilename);

Expand Down
10 changes: 5 additions & 5 deletions libethcore/Precompiled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,28 @@ ETH_REGISTER_PRECOMPILED(ecrecover)(bytesConstRef _in)
{
ret = dev::sha3(rec);
memset(ret.data(), 0, 12);
return ret.asBytes();
return {true, ret.asBytes()};
}
}
catch (...) {}
}
}
return {};
return {true, {}};
}

ETH_REGISTER_PRECOMPILED(sha256)(bytesConstRef _in)
{
return dev::sha256(_in).asBytes();
return {true, dev::sha256(_in).asBytes()};
}

ETH_REGISTER_PRECOMPILED(ripemd160)(bytesConstRef _in)
{
return h256(dev::ripemd160(_in), h256::AlignRight).asBytes();
return {true, h256(dev::ripemd160(_in), h256::AlignRight).asBytes()};
}

ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in)
{
return _in.toBytes();
return {true, _in.toBytes()};
}

ETH_REGISTER_PRECOMPILED(alt_bn128_pairing_product)(bytesConstRef _in)
Expand Down
2 changes: 1 addition & 1 deletion libethcore/Precompiled.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace dev
namespace eth
{

using PrecompiledExecutor = std::function<bytes(bytesConstRef _in)>;
using PrecompiledExecutor = std::function<std::pair<bool, bytes>(bytesConstRef _in)>;

DEV_SIMPLE_EXCEPTION(ExecutorNotFound);

Expand Down

0 comments on commit 1746645

Please sign in to comment.