Skip to content

Commit 8a59faf

Browse files
Feature/secp256k1 recovery pubkey (#160)
Signed-off-by: Alexey-N-Chernyshov <[email protected]>
1 parent b647351 commit 8a59faf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+893
-103
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "test/core/serialization/serialization_vectors"]
88
path = test/core/serialization/serialization_vectors
99
url = https://github.com/filecoin-project/serialization-vectors.git
10+
[submodule "deps/libsecp256k1"]
11+
path = deps/libsecp256k1
12+
url = https://github.com/Alexey-N-Chernyshov/libsecp256k1.git

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ include_directories(
114114
SYSTEM
115115
# system includes
116116
deps/tinycbor/src
117+
deps/libsecp256k1/include
117118
)
118119

119120
add_subdirectory(core)

core/blockchain/block_validator/impl/block_validator_impl.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ namespace fc::blockchain::block_validator {
8787
},
8888
[&block_signature, &block_bytes, *this](
8989
const SecpPubKey &public_key) -> outcome::result<void> {
90-
libp2p::crypto::secp256k1::PublicKey secp_public_key;
90+
crypto::secp256k1::PublicKey secp_public_key;
9191
auto secp_signature =
92-
boost::get<libp2p::crypto::secp256k1::Signature>(block_signature);
92+
boost::get<crypto::secp256k1::Signature>(block_signature);
9393
std::copy_n(public_key.begin(),
9494
secp_public_key.size(),
9595
secp_public_key.begin());

core/blockchain/block_validator/impl/block_validator_impl.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace fc::blockchain::block_validator {
3232
using WeightCalculator = blockchain::weight::WeightCalculator;
3333
using PowerTable = power::PowerTable;
3434
using BlsProvider = crypto::bls::BlsProvider;
35-
using SecpProvider = libp2p::crypto::secp256k1::Secp256k1Provider;
35+
using SecpProvider = crypto::secp256k1::Secp256k1ProviderDefault;
3636
using Interpreter = vm::interpreter::Interpreter;
3737
using Tipset = primitives::tipset::Tipset;
3838

core/crypto/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ add_subdirectory(hasher)
99
add_subdirectory(murmur)
1010
add_subdirectory(randomness)
1111
add_subdirectory(vrf)
12+
add_subdirectory(secp256k1)
1213
add_subdirectory(signature)

core/crypto/secp256k1/CMakeLists.txt

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright Soramitsu Co., Ltd. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
add_library(secp256k1_provider
5+
impl/secp256k1_provider_impl.cpp
6+
impl/secp256k1_sha256_provider_impl.cpp
7+
impl/secp256k1_error.cpp
8+
)
9+
target_link_libraries(secp256k1_provider
10+
libsecp256k1
11+
outcome
12+
)

core/crypto/secp256k1/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Reference implementation is
2+
https://github.com/ipsn/go-secp256k1/blob/master/secp256.go
3+
(commit
4+
43bfef0653c5a154ac1fff973f03b2664b394d3c).
5+
6+
It is a standalone clone of of `secp256k1` from https://github.com/ethereum/go-ethereum repository.
7+
8+
It uses as default:
9+
- SHA256 as digest algorithm
10+
- Uncompressed form of `PublicKey`
11+
- compact form of `Signature`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "crypto/secp256k1/secp256k1_error.hpp"
7+
8+
OUTCOME_CPP_DEFINE_CATEGORY(fc::crypto::secp256k1, Secp256k1Error, e) {
9+
using fc::crypto::secp256k1::Secp256k1Error;
10+
switch (e) {
11+
case Secp256k1Error::KEY_GENERATION_FAILED:
12+
return "Secp256k1ProviderError: generation failed";
13+
case Secp256k1Error::SIGNATURE_PARSE_ERROR:
14+
return "Secp256k1ProviderError: signature parsing error";
15+
case Secp256k1Error::SIGNATURE_SERIALIZATION_ERROR:
16+
return "Secp256k1ProviderError: signature serialization error";
17+
case Secp256k1Error::CANNOT_SIGN_ERROR:
18+
return "Secp256k1ProviderError: error when signing";
19+
case Secp256k1Error::PUBKEY_PARSE_ERROR:
20+
return "Secp256k1ProviderError: public key parse error";
21+
case Secp256k1Error::PUBKEY_SERIALIZATION_ERROR:
22+
return "Secp256k1ProviderError: public key serialization error";
23+
case Secp256k1Error::RECOVER_ERROR:
24+
return "Secp256k1ProviderError: recover error";
25+
26+
default:
27+
return "Secp256k1ProviderError: unknown error";
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "crypto/secp256k1/impl/secp256k1_provider_impl.hpp"
7+
8+
#include <openssl/bn.h>
9+
#include <openssl/ec.h>
10+
#include <openssl/obj_mac.h>
11+
#include "crypto/secp256k1/secp256k1_error.hpp"
12+
#include "secp256k1_recovery.h"
13+
14+
namespace fc::crypto::secp256k1 {
15+
16+
Secp256k1ProviderImpl::Secp256k1ProviderImpl()
17+
: context_(secp256k1_context_create(SECP256K1_CONTEXT_SIGN
18+
| SECP256K1_CONTEXT_VERIFY),
19+
secp256k1_context_destroy) {}
20+
21+
outcome::result<KeyPair> Secp256k1ProviderImpl::generate() const {
22+
PublicKeyUncompressed public_key{};
23+
PrivateKey private_key{};
24+
std::shared_ptr<EC_KEY> key{EC_KEY_new_by_curve_name(NID_secp256k1),
25+
EC_KEY_free};
26+
if (EC_KEY_generate_key(key.get()) != 1) {
27+
return Secp256k1Error::KEY_GENERATION_FAILED;
28+
}
29+
const BIGNUM *privateNum = EC_KEY_get0_private_key(key.get());
30+
if (BN_bn2binpad(privateNum, private_key.data(), private_key.size()) < 0) {
31+
return Secp256k1Error::KEY_GENERATION_FAILED;
32+
}
33+
EC_KEY_set_conv_form(key.get(), POINT_CONVERSION_UNCOMPRESSED);
34+
auto public_key_length = i2o_ECPublicKey(key.get(), nullptr);
35+
if (public_key_length != public_key.size()) {
36+
return Secp256k1Error::KEY_GENERATION_FAILED;
37+
}
38+
uint8_t *public_key_ptr = public_key.data();
39+
if (i2o_ECPublicKey(key.get(), &public_key_ptr) != public_key_length) {
40+
return Secp256k1Error::KEY_GENERATION_FAILED;
41+
}
42+
return KeyPair{private_key, public_key};
43+
}
44+
45+
outcome::result<PublicKeyUncompressed> Secp256k1ProviderImpl::derive(
46+
const PrivateKey &key) const {
47+
secp256k1_pubkey pubkey;
48+
49+
if (!secp256k1_ec_pubkey_create(context_.get(), &pubkey, key.data())) {
50+
return Secp256k1Error::KEY_GENERATION_FAILED;
51+
}
52+
53+
PublicKeyUncompressed public_key{};
54+
size_t outputlen = kPublicKeyUncompressedLength;
55+
if (!secp256k1_ec_pubkey_serialize(context_.get(),
56+
public_key.data(),
57+
&outputlen,
58+
&pubkey,
59+
SECP256K1_EC_UNCOMPRESSED)) {
60+
return Secp256k1Error::PUBKEY_SERIALIZATION_ERROR;
61+
}
62+
63+
return public_key;
64+
}
65+
66+
outcome::result<PublicKeyUncompressed> Secp256k1ProviderImpl::sign(
67+
gsl::span<const uint8_t> message, const PrivateKey &key) const {
68+
secp256k1_ecdsa_recoverable_signature sig_struct;
69+
if (!secp256k1_ecdsa_sign_recoverable(context_.get(),
70+
&sig_struct,
71+
message.data(),
72+
key.cbegin(),
73+
secp256k1_nonce_function_rfc6979,
74+
nullptr)) {
75+
return Secp256k1Error::CANNOT_SIGN_ERROR;
76+
}
77+
SignatureCompact signature{};
78+
int recid = 0;
79+
if (!secp256k1_ecdsa_recoverable_signature_serialize_compact(
80+
context_.get(), signature.data(), &recid, &sig_struct)) {
81+
return Secp256k1Error::SIGNATURE_SERIALIZATION_ERROR;
82+
}
83+
signature[64] = (uint8_t)recid;
84+
return signature;
85+
}
86+
87+
outcome::result<bool> Secp256k1ProviderImpl::verify(
88+
gsl::span<const uint8_t> message,
89+
const SignatureCompact &signature,
90+
const PublicKeyUncompressed &key) const {
91+
OUTCOME_TRY(checkSignature(signature));
92+
93+
secp256k1_ecdsa_signature sig;
94+
secp256k1_pubkey pubkey;
95+
96+
if (!secp256k1_ecdsa_signature_parse_compact(
97+
context_.get(), &sig, signature.data())) {
98+
return Secp256k1Error::SIGNATURE_PARSE_ERROR;
99+
}
100+
if (!secp256k1_ec_pubkey_parse(
101+
context_.get(), &pubkey, key.data(), key.size())) {
102+
return Secp256k1Error::PUBKEY_PARSE_ERROR;
103+
}
104+
return (secp256k1_ecdsa_verify(
105+
context_.get(), &sig, message.data(), &pubkey) == 1);
106+
}
107+
108+
outcome::result<PublicKeyUncompressed>
109+
Secp256k1ProviderImpl::recoverPublicKey(
110+
gsl::span<const uint8_t> message,
111+
const SignatureCompact &signature) const {
112+
OUTCOME_TRY(checkSignature(signature));
113+
114+
secp256k1_ecdsa_recoverable_signature sig_rec;
115+
secp256k1_pubkey pubkey;
116+
117+
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(
118+
context_.get(), &sig_rec, signature.data(), (int)signature[64])) {
119+
return Secp256k1Error::SIGNATURE_PARSE_ERROR;
120+
}
121+
if (!secp256k1_ecdsa_recover(
122+
context_.get(), &pubkey, &sig_rec, message.data())) {
123+
return Secp256k1Error::RECOVER_ERROR;
124+
}
125+
PublicKeyUncompressed pubkey_out;
126+
size_t outputlen = kPublicKeyUncompressedLength;
127+
if (!secp256k1_ec_pubkey_serialize(context_.get(),
128+
pubkey_out.data(),
129+
&outputlen,
130+
&pubkey,
131+
SECP256K1_EC_UNCOMPRESSED)) {
132+
return Secp256k1Error::PUBKEY_SERIALIZATION_ERROR;
133+
}
134+
135+
return pubkey_out;
136+
}
137+
138+
outcome::result<void> Secp256k1ProviderImpl::checkSignature(
139+
const SignatureCompact &signature) const {
140+
if (signature[64] > 3) {
141+
return Secp256k1Error::SIGNATURE_PARSE_ERROR;
142+
}
143+
return outcome::success();
144+
}
145+
146+
} // namespace fc::crypto::secp256k1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef CPP_FILECOIN_CORE_CRYPTO_SECP256K1_SECP256K1_PROVIDER_RECOVER_HPP
7+
#define CPP_FILECOIN_CORE_CRYPTO_SECP256K1_SECP256K1_PROVIDER_RECOVER_HPP
8+
9+
#include "crypto/secp256k1/secp256k1_provider.hpp"
10+
#include "crypto/secp256k1/secp256k1_types.hpp"
11+
#include "secp256k1.h"
12+
13+
namespace fc::crypto::secp256k1 {
14+
15+
/**
16+
* Implemetation of Secp256k1 provider with
17+
* - public key in uncompressed form
18+
* - signature in compact form
19+
* - NO digest function
20+
*/
21+
class Secp256k1ProviderImpl : public Secp256k1ProviderDefault {
22+
public:
23+
Secp256k1ProviderImpl();
24+
25+
outcome::result<KeyPair> generate() const override;
26+
27+
outcome::result<PublicKeyUncompressed> derive(
28+
const PrivateKey &key) const override;
29+
30+
outcome::result<SignatureCompact> sign(
31+
gsl::span<const uint8_t> message, const PrivateKey &key) const override;
32+
33+
outcome::result<bool> verify(
34+
gsl::span<const uint8_t> message,
35+
const SignatureCompact &signature,
36+
const PublicKeyUncompressed &key) const override;
37+
38+
outcome::result<PublicKeyUncompressed> recoverPublicKey(
39+
gsl::span<const uint8_t> message,
40+
const SignatureCompact &signature) const override;
41+
42+
private:
43+
std::unique_ptr<secp256k1_context, void (*)(secp256k1_context *)> context_;
44+
45+
outcome::result<void> checkSignature(
46+
const SignatureCompact &signature) const;
47+
};
48+
49+
} // namespace fc::crypto::secp256k1
50+
51+
#endif // CPP_FILECOIN_CORE_CRYPTO_SECP256K1_SECP256K1_PROVIDER_RECOVER_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "crypto/secp256k1/impl/secp256k1_sha256_provider_impl.hpp"
7+
8+
#include <libp2p/common/types.hpp>
9+
#include <libp2p/crypto/sha/sha256.hpp>
10+
11+
namespace fc::crypto::secp256k1 {
12+
13+
using libp2p::common::Hash256;
14+
using libp2p::crypto::sha256;
15+
16+
outcome::result<SignatureCompact> Secp256k1Sha256ProviderImpl::sign(
17+
gsl::span<const uint8_t> message, const PrivateKey &key) const {
18+
Hash256 digest = sha256(message);
19+
return Secp256k1ProviderImpl::sign(digest, key);
20+
}
21+
22+
outcome::result<bool> Secp256k1Sha256ProviderImpl::verify(
23+
gsl::span<const uint8_t> message,
24+
const SignatureCompact &signature,
25+
const PublicKeyUncompressed &key) const {
26+
Hash256 digest = sha256(message);
27+
return Secp256k1ProviderImpl::verify(digest, signature, key);
28+
}
29+
30+
outcome::result<PublicKeyUncompressed>
31+
Secp256k1Sha256ProviderImpl::recoverPublicKey(
32+
gsl::span<const uint8_t> message,
33+
const SignatureCompact &signature) const {
34+
Hash256 digest = sha256(message);
35+
return Secp256k1ProviderImpl::recoverPublicKey(digest, signature);
36+
}
37+
38+
} // namespace fc::crypto::secp256k1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef CPP_FILECOIN_CORE_CRYPTO_SECP256K1_IMPL_SECP_256_K_1_SHA_256_PROVIDER_IMPL_HPP
7+
#define CPP_FILECOIN_CORE_CRYPTO_SECP256K1_IMPL_SECP_256_K_1_SHA_256_PROVIDER_IMPL_HPP
8+
9+
#include "crypto/secp256k1/impl/secp256k1_provider_impl.hpp"
10+
11+
namespace fc::crypto::secp256k1 {
12+
13+
/**
14+
* Implementation of secp256k1 provider with sha256 digest function
15+
*/
16+
class Secp256k1Sha256ProviderImpl : public Secp256k1ProviderImpl {
17+
public:
18+
outcome::result<SignatureCompact> sign(
19+
gsl::span<const uint8_t> message, const PrivateKey &key) const override;
20+
21+
outcome::result<bool> verify(
22+
gsl::span<const uint8_t> message,
23+
const SignatureCompact &signature,
24+
const PublicKeyUncompressed &key) const override;
25+
26+
outcome::result<PublicKeyUncompressed> recoverPublicKey(
27+
gsl::span<const uint8_t> message,
28+
const SignatureCompact &signature) const override;
29+
};
30+
31+
} // namespace fc::crypto::secp256k1
32+
33+
#endif // CPP_FILECOIN_CORE_CRYPTO_SECP256K1_IMPL_SECP_256_K_1_SHA_256_PROVIDER_IMPL_HPP
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef CPP_FILECOIN_CORE_CRYPTO_SECP256K1_SECP256K1_ERROR_HPP
7+
#define CPP_FILECOIN_CORE_CRYPTO_SECP256K1_SECP256K1_ERROR_HPP
8+
9+
#include "common/outcome.hpp"
10+
11+
namespace fc::crypto::secp256k1 {
12+
13+
enum class Secp256k1Error {
14+
KEY_GENERATION_FAILED = 1,
15+
SIGNATURE_PARSE_ERROR,
16+
SIGNATURE_SERIALIZATION_ERROR,
17+
CANNOT_SIGN_ERROR,
18+
PUBKEY_PARSE_ERROR,
19+
PUBKEY_SERIALIZATION_ERROR,
20+
RECOVER_ERROR,
21+
UNKNOWN_ERROR
22+
};
23+
24+
}
25+
26+
OUTCOME_HPP_DECLARE_ERROR(fc::crypto::secp256k1, Secp256k1Error);
27+
28+
#endif // CPP_FILECOIN_CORE_CRYPTO_SECP256K1_SECP256K1_ERROR_HPP

0 commit comments

Comments
 (0)