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 all commits
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
15 changes: 15 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,15 @@
#pragma once

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

enum recover_key_safe : int32_t {
undefined = -1, ///< undefined error
none = 0, ///< succeed
invalid_message_digest, ///< failed to deserialize a message digest
invalid_signature_format, ///< failed to deserialize a signature
unactivated_key_type, ///< failed to recover a key using unactivated key algorithm
invalid_signature_data, ///< failed to recover a key from the given signature data
insufficient_output_buffer, ///< failed to store a recovered key due to insufficient output buffer
};

}}}} // 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[inout] pub - pointer to output buffer for the public key result.
* @param[out] publen - pointer to the size of output buffer of the public key pub. This value can be used when error_code::insufficient_output_buffer is returned to determine required size of pub.
*
* @return 0 when success, or error code
*/
int32_t recover_key_safe(span<const char> digest, span<const char> sig, span<char> pub, 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
52 changes: 52 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,57 @@ namespace eosio { namespace chain { namespace webassembly {
}
}

int32_t interface::recover_key_safe( span<const char> digest,
span<const char> sig,
span<char> pub,
uint32_t* publen) const {
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 ( fc::exception& ) {
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::sha256 _digest;
try {
_digest = fc::sha256(digest.data(), digest.size());
} catch ( fc::exception& ) {
return error_code::invalid_message_digest;
}

fc::crypto::public_key recovered;
try {
recovered = fc::crypto::public_key(s, _digest, false);
} catch ( fc::exception& ) {
return error_code::invalid_signature_data;
}

auto packed_pubkey = fc::raw::pack(recovered);
if( pub.size() < packed_pubkey.size() ) {
return error_code::insufficient_output_buffer;
}
std::memcpy(pub.data(), packed_pubkey.data(), packed_pubkey.size());
*publen = packed_pubkey.size();
} catch( const eosio::chain::sig_variable_size_limit_exception& ) {
throw;
} catch ( fc::exception& ) {
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_CF_HOST_FUNCTION(recover_key_safe);
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_sha256);
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_sha1);
REGISTER_LEGACY_CF_HOST_FUNCTION(assert_sha512);
Expand Down