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

Implement EIP-152: Add Blake2 compression function F precompile #5751

Merged
merged 9 commits into from
Sep 23, 2019
189 changes: 189 additions & 0 deletions libdevcrypto/Blake2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/// Aleth: Ethereum C++ client, tools and libraries.
// Copyright 2019 Aleth Authors.
// Licensed under the GNU General Public License, Version 3.

#include "Blake2.h"

#include <libdevcore/Exceptions.h>

namespace dev
{
namespace crypto
{
namespace
{
DEV_SIMPLE_EXCEPTION(InvalidInputSize);

enum blake2b_constant
{
BLAKE2B_BLOCKBYTES = 128,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convert to constexpr.

};

typedef struct blake2b_state__
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop typedef.

{
uint64_t h[8];
uint64_t t[2];
uint64_t f[2];
uint8_t buf[BLAKE2B_BLOCKBYTES];
size_t buflen;
size_t outlen;
uint8_t last_node;
} blake2b_state;

// clang-format off
const uint64_t blake2b_IV[8] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr.

{
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
};

const uint8_t blake2b_sigma[12][16] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
};
// clang-format on

inline uint64_t load64(const void* src) noexcept
{
#if defined(NATIVE_LITTLE_ENDIAN)
uint64_t w;
memcpy(&w, src, sizeof w);
return w;
#else
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chfast Should we care about big endian platforms?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so.

const uint8_t* p = (const uint8_t*)src;
return ((uint64_t)(p[0]) << 0) | ((uint64_t)(p[1]) << 8) | ((uint64_t)(p[2]) << 16) |
((uint64_t)(p[3]) << 24) | ((uint64_t)(p[4]) << 32) | ((uint64_t)(p[5]) << 40) |
((uint64_t)(p[6]) << 48) | ((uint64_t)(p[7]) << 56);
#endif
}

inline void store64(void* dst, uint64_t w) noexcept
{
#if defined(NATIVE_LITTLE_ENDIAN)
memcpy(dst, &w, sizeof w);
#else
uint8_t* p = (uint8_t*)dst;
p[0] = (uint8_t)(w >> 0);
p[1] = (uint8_t)(w >> 8);
p[2] = (uint8_t)(w >> 16);
p[3] = (uint8_t)(w >> 24);
p[4] = (uint8_t)(w >> 32);
p[5] = (uint8_t)(w >> 40);
p[6] = (uint8_t)(w >> 48);
p[7] = (uint8_t)(w >> 56);
#endif
}

inline uint64_t rotr64(const uint64_t w, const unsigned c) noexcept
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inline uint64_t rotr64(const uint64_t w, const unsigned c) noexcept
inline constexpr uint64_t rotr64(uint64_t w, unsigned c) noexcept

{
return (w >> c) | (w << (64 - c));
}

inline void G(
uint8_t r, uint8_t i, uint64_t& a, uint64_t& b, uint64_t& c, uint64_t& d, uint64_t* m) noexcept
{
a = a + b + m[blake2b_sigma[r][2 * i + 0]];
d = rotr64(d ^ a, 32);
c = c + d;
b = rotr64(b ^ c, 24);
a = a + b + m[blake2b_sigma[r][2 * i + 1]];
d = rotr64(d ^ a, 16);
c = c + d;
b = rotr64(b ^ c, 63);
}

inline void ROUND(uint32_t round, uint64_t* v, uint64_t* m) noexcept
{
uint8_t const r = round % 10;
G(r, 0, v[0], v[4], v[8], v[12], m);
G(r, 1, v[1], v[5], v[9], v[13], m);
G(r, 2, v[2], v[6], v[10], v[14], m);
G(r, 3, v[3], v[7], v[11], v[15], m);
G(r, 4, v[0], v[5], v[10], v[15], m);
G(r, 5, v[1], v[6], v[11], v[12], m);
G(r, 6, v[2], v[7], v[8], v[13], m);
G(r, 7, v[3], v[4], v[9], v[14], m);
}


void blake2b_compress(
uint32_t rounds, blake2b_state* S, const uint8_t block[BLAKE2B_BLOCKBYTES]) noexcept
{
uint64_t m[16];
uint64_t v[16];
size_t i;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move i to loops.


for (i = 0; i < 16; ++i)
{
m[i] = load64(block + i * sizeof(m[i]));
}

for (i = 0; i < 8; ++i)
{
v[i] = S->h[i];
}

v[8] = blake2b_IV[0];
v[9] = blake2b_IV[1];
v[10] = blake2b_IV[2];
v[11] = blake2b_IV[3];
v[12] = blake2b_IV[4] ^ S->t[0];
v[13] = blake2b_IV[5] ^ S->t[1];
v[14] = blake2b_IV[6] ^ S->f[0];
v[15] = blake2b_IV[7] ^ S->f[1];

for (uint32_t r = 0; r < rounds; ++r)
ROUND(r, v, m);

for (i = 0; i < 8; ++i)
{
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
}
}

} // namespace


bytes blake2FCompression(uint32_t _rounds, bytesConstRef _stateVector, bytesConstRef _t0,
bytesConstRef _t1, bool _lastBlock, bytesConstRef _messageBlockVector)
{
if (_stateVector.size() != 64)
BOOST_THROW_EXCEPTION(InvalidInputSize());
if (_messageBlockVector.size() != 128)
BOOST_THROW_EXCEPTION(InvalidInputSize());
if (_t0.size() != 8 || _t1.size() != 8)
BOOST_THROW_EXCEPTION(InvalidInputSize());

blake2b_state s{};
for (size_t i = 0; i < 8; ++i)
s.h[i] = load64(&_stateVector[i * sizeof(uint64_t)]);

s.t[0] = load64(_t0.data());
s.t[1] = load64(_t1.data());
s.f[0] = _lastBlock ? (uint64_t)-1 : 0;

uint8_t block[BLAKE2B_BLOCKBYTES];
std::copy(_messageBlockVector.begin(), _messageBlockVector.end(), &block[0]);

blake2b_compress(_rounds, &s, block);

bytes result(64);
for (size_t i = 0; i < 8; ++i)
store64(&result[i * sizeof(uint64_t)], s.h[i]);

return result;
}

} // namespace crypto
} // namespace dev
17 changes: 17 additions & 0 deletions libdevcrypto/Blake2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// Aleth: Ethereum C++ client, tools and libraries.
// Copyright 2019 Aleth Authors.
// Licensed under the GNU General Public License, Version 3.

#pragma once

#include <libdevcore/Common.h>

namespace dev
{
namespace crypto
{
bytes blake2FCompression(uint32_t _rounds, bytesConstRef _stateVector, bytesConstRef _t0,
bytesConstRef _t1, bool _lastBlock, bytesConstRef _messageBlockVector);

}
} // namespace dev
25 changes: 25 additions & 0 deletions libethcore/Precompiled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "ChainOperationParams.h"
#include <libdevcore/Log.h>
#include <libdevcore/SHA3.h>
#include <libdevcrypto/Blake2.h>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/Hash.h>
#include <libdevcrypto/LibSnark.h>
Expand Down Expand Up @@ -219,4 +220,28 @@ ETH_REGISTER_PRECOMPILED_PRICER(alt_bn128_pairing_product)
return _blockNumber < _chainParams.istanbulForkBlock ? 100000 + k * 80000 : 45000 + k * 34000;
}

ETH_REGISTER_PRECOMPILED(blake2_compression)(bytesConstRef _in)
{
if (_in.size() != 213)
return {false, {}};

auto const rounds = fromBigEndian<uint32_t>(_in.cropped(0, 4));
auto const stateVectorRef = _in.cropped(4, 8 * 8);
auto const messageBlockVectorRef = _in.cropped(4 + 8 * 8, 16 * 8);
auto const offsetCounter0 = _in.cropped(4 + 8 * 8 + 16 * 8, 8);
auto const offsetCounter1 = _in.cropped(4 + 8 * 8 + 16 * 8 + 8, 8);
byte const finalBlockIndicator = _in[4 + 8 * 8 + 16 * 8 + 8 + 8];
if (finalBlockIndicator != 0 && finalBlockIndicator != 1)
return {false, {}};

return {true, dev::crypto::blake2FCompression(rounds, stateVectorRef, offsetCounter0,
offsetCounter1, finalBlockIndicator, messageBlockVectorRef)};
}

ETH_REGISTER_PRECOMPILED_PRICER(blake2_compression)
(bytesConstRef _in, ChainOperationParams const&, u256 const&)
{
auto const rounds = fromBigEndian<uint32_t>(_in.cropped(0, 4));
return rounds;
}
}
130 changes: 130 additions & 0 deletions test/unittests/libethcore/PrecompiledTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,39 @@ void benchmarkPrecompiled(char const name[], vector_ref<const PrecompiledTest> t
std::cout << ut::framework::current_test_case().p_name << "/" << test.name << ": " << t << " ns\n";
}
}

void testPrecompiled(char const name[], vector_ref<const PrecompiledTest> tests)
{
PrecompiledExecutor exec = PrecompiledRegistrar::executor(name);

for (auto&& test : tests)
{
bytes input = fromHex(test.input);
bytesConstRef inputRef = &input;

auto res = exec(inputRef);
BOOST_REQUIRE_MESSAGE(res.first, test.name);
BOOST_REQUIRE_EQUAL(toHex(res.second), test.expected);

std::cout << ut::framework::current_test_case().p_name << "/" << test.name << " PASSED\n";
}
}

void testPrecompiledFail(char const name[], vector_ref<const PrecompiledTest> tests)
{
PrecompiledExecutor exec = PrecompiledRegistrar::executor(name);

for (auto&& test : tests)
{
bytes input = fromHex(test.input);
bytesConstRef inputRef = &input;

auto res = exec(inputRef);
BOOST_REQUIRE_MESSAGE(!res.first, test.name);

std::cout << ut::framework::current_test_case().p_name << "/" << test.name << " PASSED\n";
}
}
}

/// @}
Expand Down Expand Up @@ -785,4 +818,101 @@ BOOST_AUTO_TEST_CASE(ecpairingCost)
BOOST_CHECK_EQUAL(static_cast<int>(costIstanbul), in.size() / 192 * 34000 + 45000);
}

constexpr PrecompiledTest blake2FCompressionFailTests[] = {
{"00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2"
"b8c68059b6bbd41fbabd9831f79217e1319cde05b6162630000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000300000000000000000000000000000001",
"", "test1"},
{
"000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e51"
"1f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b616263000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000300000000000000000000000000000001",
"",
"test2",
},
{
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f"
"6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000300000000000000000000000000000002",
"",
"test3",
}};

constexpr PrecompiledTest blake2FCompressionTests[] = {
{"0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3"
"e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000300000000000000000000000000000001",
"08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c"
"68059b9442be0454267ce079217e1319cde05b",
"test4"},
{
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f"
"6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000300000000000000000000000000000001",
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de45"
"33cc9518d38aa8dbf1925ab92386edd4009923",
"test5",
},
{
"0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f"
"6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000300000000000000000000000000000000",
"75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d22603"
"9cd31b4e426ac4f2d3d666a610c2116fde4735",
"test6",
},
{
"0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f"
"6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000300000000000000000000000000000001",
"b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993"
"d53923de3d64fcc68c034e717b9293fed7a421",
"test7",
},
{
"ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f"
"6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000300000000000000000000000000000001",
"fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01"
"846e6b5df8cc7703041bbceb571de6631d2615",
"test8",
}};

BOOST_AUTO_TEST_CASE(blake2compression)
{
vector_ref<const PrecompiledTest> tests{blake2FCompressionTests,
sizeof(blake2FCompressionTests) / sizeof(blake2FCompressionTests[0])};
testPrecompiled("blake2_compression", tests);
}

BOOST_AUTO_TEST_CASE(blake2compressionFail)
{
vector_ref<const PrecompiledTest> tests{blake2FCompressionFailTests,
sizeof(blake2FCompressionFailTests) / sizeof(blake2FCompressionFailTests[0])};
testPrecompiledFail("blake2_compression", tests);
}

BOOST_AUTO_TEST_CASE(bench_blake2compression, *ut::label("bench"))
{
vector_ref<const PrecompiledTest> tests{blake2FCompressionTests,
sizeof(blake2FCompressionTests) / sizeof(blake2FCompressionTests[0])};
benchmarkPrecompiled("blake2_compression", tests, 1);
}

BOOST_AUTO_TEST_SUITE_END()