1
0
Fork 0
forked from lthn/blockchain

crypto: basic eth signature implementation + functional test

This commit is contained in:
sowle 2024-07-02 13:15:48 +02:00
parent 486fb05f73
commit f05d14a944
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
3 changed files with 191 additions and 0 deletions

View 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

View 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

View file

@ -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