forked from lthn/blockchain
crypto: basic eth signature implementation + functional test
This commit is contained in:
parent
486fb05f73
commit
f05d14a944
3 changed files with 191 additions and 0 deletions
125
src/crypto/eth_signature.cpp
Normal file
125
src/crypto/eth_signature.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) 2024 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#include "eth_signature.h"
|
||||
#include "crypto.h"
|
||||
#include "bitcoin-secp256k1/include/secp256k1.h"
|
||||
#include "random.h"
|
||||
#include "misc_language.h"
|
||||
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
|
||||
secp256k1_context_destroy(ctx);
|
||||
ctx = nullptr;
|
||||
});
|
||||
|
||||
uint8_t randomness[32];
|
||||
crypto::generate_random_bytes(sizeof randomness, randomness);
|
||||
if (!secp256k1_context_randomize(ctx, randomness))
|
||||
return false;
|
||||
|
||||
for(size_t i = 1024; i != 0; --i)
|
||||
{
|
||||
crypto::generate_random_bytes(sizeof sec_key, sec_key.data);
|
||||
if (secp256k1_ec_seckey_verify(ctx, sec_key.data))
|
||||
break;
|
||||
if (i == 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
secp256k1_pubkey uncompressed_pub_key{};
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &uncompressed_pub_key, sec_key.data))
|
||||
return false;
|
||||
|
||||
size_t output_len = sizeof pub_key;
|
||||
if (!secp256k1_ec_pubkey_serialize(ctx, pub_key.data, &output_len, &uncompressed_pub_key, SECP256K1_EC_COMPRESSED))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generates secp256k1 ECDSA signature
|
||||
bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
|
||||
secp256k1_context_destroy(ctx);
|
||||
ctx = nullptr;
|
||||
});
|
||||
|
||||
uint8_t randomness[32];
|
||||
crypto::generate_random_bytes(sizeof randomness, randomness);
|
||||
if (!secp256k1_context_randomize(ctx, randomness))
|
||||
return false;
|
||||
|
||||
secp256k1_ecdsa_signature secp256k1_ecdsa_sig{};
|
||||
if (!secp256k1_ecdsa_sign(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, sec_key.data, NULL, NULL))
|
||||
return false;
|
||||
|
||||
if (!secp256k1_ecdsa_signature_serialize_compact(ctx, sig.data, &secp256k1_ecdsa_sig))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// verifies secp256k1 ECDSA signature
|
||||
bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO (performance) consider using secp256k1_context_static for verification -- sowle
|
||||
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
auto slh = epee::misc_utils::create_scope_leave_handler([&ctx](){
|
||||
secp256k1_context_destroy(ctx);
|
||||
ctx = nullptr;
|
||||
});
|
||||
|
||||
uint8_t randomness[32];
|
||||
crypto::generate_random_bytes(sizeof randomness, randomness);
|
||||
if (!secp256k1_context_randomize(ctx, randomness))
|
||||
return false;
|
||||
|
||||
secp256k1_ecdsa_signature secp256k1_ecdsa_sig{};
|
||||
secp256k1_pubkey uncompressed_pub_key{};
|
||||
|
||||
if (!secp256k1_ecdsa_signature_parse_compact(ctx, &secp256k1_ecdsa_sig, sig.data))
|
||||
return false;
|
||||
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &uncompressed_pub_key, pub_key.data, sizeof pub_key))
|
||||
return false;
|
||||
|
||||
// verify a signature
|
||||
if (!secp256k1_ecdsa_verify(ctx, &secp256k1_ecdsa_sig, (const unsigned char*)m.data, &uncompressed_pub_key))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace crypto
|
||||
39
src/crypto/eth_signature.h
Normal file
39
src/crypto/eth_signature.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2024 Zano Project
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "hash.h"
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
// secp256k1 public key in serialized (compressed) form that is used in Etherium
|
||||
struct eth_public_key
|
||||
{
|
||||
uint8_t data[33];
|
||||
};
|
||||
|
||||
// secp256k1 secret key
|
||||
struct eth_secret_key
|
||||
{
|
||||
uint8_t data[32];
|
||||
};
|
||||
|
||||
// secp256k1 ECDSA signature is serialized (compressed) form that is used in Etherium
|
||||
struct eth_signature
|
||||
{
|
||||
uint8_t data[64];
|
||||
};
|
||||
|
||||
// generates secp256k1 keypair
|
||||
bool generate_eth_key_pair(eth_secret_key& sec_key, eth_public_key& pub_key) noexcept;
|
||||
|
||||
// generates secp256k1 ECDSA signature
|
||||
bool generate_eth_signature(const hash& m, const eth_secret_key& sec_key, eth_signature& sig) noexcept;
|
||||
|
||||
// verifies secp256k1 ECDSA signature
|
||||
bool verify_eth_signature(const hash& m, const eth_public_key& pub_key, const eth_signature& sig) noexcept;
|
||||
|
||||
|
||||
} // namespace crypto
|
||||
|
|
@ -1957,6 +1957,33 @@ TEST(crypto, secp256k1_ecdsa_native)
|
|||
}
|
||||
|
||||
|
||||
TEST(crypto, eth_signature_basics)
|
||||
{
|
||||
eth_secret_key sk{};
|
||||
eth_public_key pk{};
|
||||
|
||||
ASSERT_TRUE(generate_eth_key_pair(sk, pk));
|
||||
|
||||
eth_signature sig{};
|
||||
hash m = hash_helper_t::h("How many of you have ever felt personally victimized by elliptic curves?");
|
||||
|
||||
ASSERT_TRUE(generate_eth_signature(m, sk, sig));
|
||||
|
||||
const eth_signature const_sig = sig;
|
||||
ASSERT_TRUE(verify_eth_signature(m, pk, const_sig));
|
||||
|
||||
for(size_t i = 0; i < sizeof sig; ++i)
|
||||
{
|
||||
eth_signature bad_sig = sig;
|
||||
bad_sig.data[i] ^= 1 + (rand() % 254); // xor with a number fom [1; 255] to make sure this byte will change
|
||||
ASSERT_FALSE(verify_eth_signature(m, pk, bad_sig));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// test's runner
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue