Skip to content

account/GeniusSigner.cpp

Namespaces

Name
sgns

Source code

// Keep these includes before EthereumKeyGenerator to satisfy crypto3's headers.
#include <nil/crypto3/algebra/marshalling.hpp>
#include <nil/crypto3/pubkey/algorithm/sign.hpp>
#include <nil/crypto3/pubkey/algorithm/verify.hpp>

#include "account/GeniusSigner.hpp"

#include <secp256k1.h>

#include <algorithm>
#include <array>
#include <cstdlib>
#include <memory>
#include <utility>

#include "base/hexutil.hpp"
#include "base/logger.hpp"

namespace
{
    sgns::base::Logger genius_signer_logger()
    {
        return sgns::base::createLogger( "GeniusSigner" );
    }

    const secp256k1_context *GetSecp256k1Context()
    {
        using Context = std::unique_ptr<secp256k1_context, decltype( &secp256k1_context_destroy )>;
        static const Context context( secp256k1_context_create( SECP256K1_CONTEXT_NONE ), &secp256k1_context_destroy );
        if ( context == nullptr )
        {
            genius_signer_logger()->critical( "Could not create the secp256k1 context" );
            std::abort();
        }
        return context.get();
    }
} // namespace

namespace sgns
{
    GeniusSigner GeniusSigner::Generate()
    {
        return GeniusSigner( ethereum::EthereumKeyGenerator{} );
    }

    GeniusSigner::GeniusSigner( ethereum::EthereumKeyGenerator keypair ) : keypair_( std::move( keypair ) )
    {
    }

    std::string GeniusSigner::GetAddress() const
    {
        return keypair_.GetEntirePubValue();
    }

    bool GeniusSigner::VerifySignature( const std::string          &address,
                                        std::string_view            signature,
                                        const std::vector<uint8_t> &data )
    {
        if ( signature.size() != SIGNATURE_SIZE )
        {
            genius_signer_logger()->error( "Incorrect signature size {}, expected {}",
                                           signature.size(),
                                           SIGNATURE_SIZE );
            return false;
        }

        auto public_key_bytes = base::unhex( address );
        if ( public_key_bytes.has_error() || public_key_bytes.value().size() != 64 )
        {
            return false;
        }

        const auto *context = GetSecp256k1Context();

        std::array<uint8_t, 65> uncompressed_public_key{};
        uncompressed_public_key.front() = SECP256K1_TAG_PUBKEY_UNCOMPRESSED;
        std::copy( public_key_bytes.value().begin(),
                   public_key_bytes.value().end(),
                   uncompressed_public_key.begin() + 1 );

        secp256k1_pubkey public_key;
        if ( secp256k1_ec_pubkey_parse( context,
                                        &public_key,
                                        uncompressed_public_key.data(),
                                        uncompressed_public_key.size() ) == 0 )
        {
            return false;
        }

        // Genius signatures store each scalar least-significant byte first; libsecp256k1 uses big endian.
        std::array<uint8_t, SIGNATURE_SIZE> compact_signature{};
        std::reverse_copy( signature.begin(), signature.begin() + 32, compact_signature.begin() );
        std::reverse_copy( signature.begin() + 32, signature.end(), compact_signature.begin() + 32 );

        secp256k1_ecdsa_signature parsed_signature;
        if ( secp256k1_ecdsa_signature_parse_compact( context, &parsed_signature, compact_signature.data() ) == 0 )
        {
            return false;
        }
        secp256k1_ecdsa_signature_normalize( context, &parsed_signature, &parsed_signature );

        // Preserve the historical SHA256(SHA256(data)) Genius signing protocol.
        const std::array<uint8_t, 32> first_hash   = nil::crypto3::hash<nil::crypto3::hashes::sha2<256>>( data );
        const std::array<uint8_t, 32> message_hash = nil::crypto3::hash<nil::crypto3::hashes::sha2<256>>( first_hash );

        return secp256k1_ecdsa_verify( context, &parsed_signature, message_hash.data(), &public_key ) == 1;
    }

    bool GeniusSigner::VerifySignature( const std::string          &address,
                                        const std::vector<uint8_t> &signature,
                                        const std::vector<uint8_t> &data )
    {
        const std::string_view signature_view = signature.empty()
                                                    ? std::string_view{}
                                                    : std::string_view(
                                                          reinterpret_cast<const char *>( signature.data() ),
                                                          signature.size() );
        return VerifySignature( address, signature_view, data );
    }

    std::vector<uint8_t> GeniusSigner::Sign( const std::vector<uint8_t> &data ) const
    {
        const auto *context = GetSecp256k1Context();

        std::array<uint8_t, 32> secret_key{};
        const auto              private_key = keypair_.get_private_key();
        nil::marshalling::bincode::field<ethereum::scalar_field_type>::field_element_to_bytes<
            std::array<uint8_t, 32>::iterator>( private_key.private_key_data(), secret_key.begin(), secret_key.end() );
        std::reverse( secret_key.begin(), secret_key.end() );

        // Preserve the historical SHA256(SHA256(data)) Genius signing protocol.
        const std::array<uint8_t, 32> first_hash   = nil::crypto3::hash<nil::crypto3::hashes::sha2<256>>( data );
        const std::array<uint8_t, 32> message_hash = nil::crypto3::hash<nil::crypto3::hashes::sha2<256>>( first_hash );

        secp256k1_ecdsa_signature signature;
        if ( secp256k1_ecdsa_sign( context, &signature, message_hash.data(), secret_key.data(), nullptr, nullptr ) ==
             0 )
        {
            genius_signer_logger()->error( "Could not sign data with the account key" );
            return {};
        }

        std::array<uint8_t, SIGNATURE_SIZE> compact_signature{};
        secp256k1_ecdsa_signature_serialize_compact( context, compact_signature.data(), &signature );

        std::vector<uint8_t> signed_vector( SIGNATURE_SIZE );
        std::reverse_copy( compact_signature.begin(), compact_signature.begin() + 32, signed_vector.begin() );
        std::reverse_copy( compact_signature.begin() + 32, compact_signature.end(), signed_vector.begin() + 32 );
        return signed_vector;
    }
} // namespace sgns

Updated on 2026-08-07 at 17:48:03 +0000