Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Add RECOVER_KEY_SAFE protocol feature #10831

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ struct controller_impl {
set_activation_handler<builtin_protocol_feature_t::configurable_wasm_limits>();
set_activation_handler<builtin_protocol_feature_t::blockchain_parameters>();
set_activation_handler<builtin_protocol_feature_t::security_group>();
set_activation_handler<builtin_protocol_feature_t::recover_key_safe>();

self.irreversible_block.connect([this](const block_state_ptr& bsp) {
wasmif.current_lib(bsp->block_num);
Expand Down Expand Up @@ -3465,6 +3466,13 @@ void controller_impl::on_activation<builtin_protocol_feature_t::security_group>(
} );
}

template<>
void controller_impl::on_activation<builtin_protocol_feature_t::recover_key_safe>() {
db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "recover_key_safe" );
} );
}

/// End of protocol feature activation handlers

} } /// eosio::chain
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ enum class builtin_protocol_feature_t : uint32_t {
blockchain_parameters,
security_group,
resource_payer,
get_wasm_parameters_packed_fix
get_wasm_parameters_packed_fix,
recover_key_safe,
};

struct protocol_feature_subjective_restrictions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ constexpr auto intrinsic_table = boost::hana::make_tuple(
"env.add_security_group_participants"_s,
"env.remove_security_group_participants"_s,
"env.in_active_security_group"_s,
"env.get_active_security_group"_s,
"env.get_active_security_group"_s,
"env.recover_key_safe"_s,
// the following should always be in the end of the tuple
"env.push_data"_s,
"env.print_time_us"_s,
Expand Down
14 changes: 14 additions & 0 deletions libraries/chain/include/eosio/chain/webassembly/error_codes.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

namespace eosio { namespace chain { namespace webassembly { namespace error_codes {

enum recover_key_safe : int32_t {
undefined = -1,
none = 0,
invalid_signature_format,
unactivated_key_type,
invalid_signature_data,
insufficient_output_buffer,
conr2d marked this conversation as resolved.
Show resolved Hide resolved
};

}}}} // ns eosio::chain::webassembly::error_codes
14 changes: 14 additions & 0 deletions libraries/chain/include/eosio/chain/webassembly/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,20 @@ namespace webassembly {
*/
int32_t recover_key(legacy_ptr<const fc::sha256> digest, legacy_span<const char> sig, legacy_span<char> pub) const;

/**
* Calculates the public key used for a given signature on a given digest.
* Unlike @ref recover_key it returns an error code rather than throws an exception with invalid input.
*
* @ingroup crypto
* @param digest - digest of the message that was signed.
* @param sig - signature.
* @param[out] pub - pointer to output buffer for the public key result.
conr2d marked this conversation as resolved.
Show resolved Hide resolved
* @param[inout] publen - pointer to an integer initially set to the expected size of output, and overwritten by the written size
conr2d marked this conversation as resolved.
Show resolved Hide resolved
*
* @return 0 when success, or error code
*/
int32_t recover_key_safe(legacy_ptr<const fc::sha256> digest, legacy_span<const char> sig, legacy_ptr<char> pub, legacy_ptr<uint32_t> publen) const;

/**
* Tests if the sha256 hash generated from data matches the provided digest.
*
Expand Down
11 changes: 11 additions & 0 deletions libraries/chain/protocol_feature_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,17 @@ Builtin protocol feature: GET_WASM_PARAMETERS_PACKED_FIX

Fix an issue with the host function get_wasm_parameters_packed, due to miscalculating the required size for
the packed output data.
*/
{}
} )
(builtin_protocol_feature_t::recover_key_safe, builtin_protocol_feature_spec{
"RECOVER_KEY_SAFE",
fc::variant("80f1baa2605aec0a7a797b386cdea5c4b612a51d406de9ab098c9b0b5f70843d").as<digest_type>(),
// SHA256 hash of the raw message below within the comment delimiters (do not modify message below).
/*
Builtin protocol feature: RECOVER_KEY_SAFE

Adds an alternative intrinsic for recover_key that doesn't throw an exception with invalid input.
*/
{}
} )
Expand Down
45 changes: 45 additions & 0 deletions libraries/chain/webassembly/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <eosio/chain/protocol_state_object.hpp>
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/webassembly/error_codes.hpp>

namespace eosio { namespace chain { namespace webassembly {

Expand Down Expand Up @@ -65,6 +66,50 @@ namespace eosio { namespace chain { namespace webassembly {
}
}

int32_t interface::recover_key_safe( legacy_ptr<const fc::sha256> digest,
legacy_span<const char> sig,
legacy_ptr<char> pub,
legacy_ptr<uint32_t> publen) const {
conr2d marked this conversation as resolved.
Show resolved Hide resolved
using error_code = eosio::chain::webassembly::error_codes::recover_key_safe;

try {
fc::crypto::signature s;
try {
datastream<const char*> ds( sig.data(), sig.size() );
fc::raw::unpack(ds, s);
} catch (...) {
conr2d marked this conversation as resolved.
Show resolved Hide resolved
return error_code::invalid_signature_format;
}

if( static_cast<unsigned>(s.which()) >= context.db.get<protocol_state_object>().num_supported_key_types ) {
return error_code::unactivated_key_type;
}

if(context.control.is_producing_block())
EOS_ASSERT(s.variable_size() <= context.control.configured_subjective_signature_length_limit(),
sig_variable_size_limit_exception, "signature variable length component size greater than subjective maximum");

fc::crypto::public_key recovered;
try {
recovered = fc::crypto::public_key(s, *digest, false);
} catch (...) {
return error_code::invalid_signature_data;
}

auto packed_pubkey = fc::raw::pack(recovered);
if( *publen.get() < packed_pubkey.size() ) {
conr2d marked this conversation as resolved.
Show resolved Hide resolved
return error_code::insufficient_output_buffer;
}
std::memcpy(pub.get(), packed_pubkey.data(), packed_pubkey.size());
*publen.get() = packed_pubkey.size();
} catch( const eosio::chain::sig_variable_size_limit_exception& ) {
throw;
} catch (...) {
return error_code::undefined;
}
return error_code::none;
}

void interface::assert_sha256(legacy_span<const char> data, legacy_ptr<const fc::sha256> hash_val) const {
auto result = context.trx_context.hash_with_checktime<fc::sha256>( data.data(), data.size() );
EOS_ASSERT( result == *hash_val, crypto_api_exception, "hash mismatch" );
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/webassembly/runtimes/eos-vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ REGISTER_LEGACY_HOST_FUNCTION(get_active_producers);
// crypto api
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_recover_key);
REGISTER_LEGACY_CF_HOST_FUNCTION(recover_key);
REGISTER_LEGACY_CF_HOST_FUNCTION(recover_key_safe);
conr2d marked this conversation as resolved.
Show resolved Hide resolved
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_sha256);
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_sha1);
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_sha512);
Expand Down