Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add `sign_message` command to `cli_wallet` #1878

Merged
merged 8 commits into from Aug 7, 2019

Rewrite/refactor sign_message for compatibility with python-bitshares

  • Loading branch information...
pmconrad committed Aug 1, 2019
commit d218bcaadb694975ff1455df8d217470e9a9b7f1
@@ -267,9 +267,20 @@ struct vesting_balance_object_with_info : public vesting_balance_object
fc::time_point_sec allowed_withdraw_time;
};

struct signed_message {
fc::variants payload;
fc::ecc::compact_signature signature;
struct signed_message_meta {
string account;
public_key_type memo_key;
uint32_t block;
int64_t time;
};

class signed_message {
public:
string message;
signed_message_meta meta;
fc::optional<fc::ecc::compact_signature> signature;

fc::sha256 digest()const;
};

namespace detail {
@@ -1060,20 +1071,21 @@ class wallet_api
string read_memo(const memo_data& memo);


/** Sign a message using an account's memo key.
/** Sign a message using an account's memo key. The signature is generated as in
* in https://github.com/xeroc/python-graphenelib/blob/d9634d74273ebacc92555499eca7c444217ecba0/graphenecommon/message.py#L64 .
*
* @param signer the name or id of signing account
* @param message text to sign
* @return the signed message in the format used in https://github.com/xeroc/python-graphenelib/blob/d9634d74273ebacc92555499eca7c444217ecba0/graphenecommon/message.py#L64
* @return the signed message in an abstract format
*/
signed_message sign_message(string signer, string message);

/** Verify a message signed with sign_message
*
* @param message JSON-encoded signed message in the format used in https://github.com/xeroc/python-graphenelib/blob/d9634d74273ebacc92555499eca7c444217ecba0/graphenecommon/message.py#L64
* @param message either a JSON-encoded signed_message structure, or a signed message in encapsulated format
* @return true if signature matches
*/
bool verify_message(signed_message message);
bool verify_message(string message);

/** These methods are used for stealth transfers */
///@{
@@ -2054,7 +2066,8 @@ FC_REFLECT(graphene::wallet::operation_detail_ex,
FC_REFLECT( graphene::wallet::account_history_operation_detail,
(total_count)(result_count)(details))

FC_REFLECT( graphene::wallet::signed_message, (payload)(signature) )
FC_REFLECT( graphene::wallet::signed_message_meta, (account)(memo_key)(block)(time) )
FC_REFLECT( graphene::wallet::signed_message, (message)(meta)(signature) )

FC_API( graphene::wallet::wallet_api,
(help)
@@ -107,6 +107,11 @@ using std::endl;

namespace detail {

static const string ENC_HEADER( "-----BEGIN BITSHARES SIGNED MESSAGE-----\n" );
static const string ENC_META( "-----BEGIN META-----\n" );
static const string ENC_SIG( "-----BEGIN SIGNATURE-----\n" );
static const string ENC_FOOTER( "-----END BITSHARES SIGNED MESSAGE-----" );

struct operation_result_printer
{
public:
@@ -2250,27 +2255,19 @@ class wallet_api_impl

const account_object from_account = get_account(signer);

signed_message result;
result.payload.emplace_back( std::string("from") );
result.payload.emplace_back( from_account.name );
result.payload.emplace_back( std::string("key") );
result.payload.emplace_back( std::string( from_account.options.memo_key ) );
result.payload.emplace_back( std::string("time") );
result.payload.emplace_back( time(nullptr) );
result.payload.emplace_back( std::string("text") );
result.payload.emplace_back( std::move(message) );

digest_type::encoder enc;
fc::raw::pack( enc, _chain_id );
fc::raw::pack( enc, result.payload );
result.signature = get_private_key( from_account.options.memo_key ).sign_compact( enc.result() );

return result;
signed_message msg;
msg.message = message;
msg.meta.account = from_account.name;
msg.meta.memo_key = from_account.options.memo_key;
msg.meta.block = 0;
msg.meta.time = 0;
This conversation was marked as resolved by abitmore

This comment has been minimized.

Copy link
@abitmore

abitmore Aug 3, 2019

Member

Why not use head_block_num and the timestamp?

This comment has been minimized.

Copy link
@pmconrad

pmconrad Aug 5, 2019

Author Contributor

Thanks. Had planned to add it after getting the proof-of-concept to work, then forgot. :-/

msg.signature = get_private_key( from_account.options.memo_key ).sign_compact( msg.digest() );
return msg;
}

bool verify_message( signed_message message )
bool verify_message( string message )
{
FC_ASSERT( message.payload.size() == 8 );
/*FC_ASSERT( message.payload.size() == 8 );
FC_ASSERT( message.payload[0].is_string() && message.payload[0].as_string() == "from" );
FC_ASSERT( message.payload[1].is_string() );
FC_ASSERT( message.payload[2].is_string() && message.payload[2].as_string() == "key" );
@@ -2289,7 +2286,8 @@ class wallet_api_impl
const public_key signer( message.signature, enc.result() );
FC_ASSERT( signer == key.key_data, "Message wasn't signed by contained key!" );
FC_ASSERT( signer == from_account.options.memo_key.key_data,
"Message was signed by contained key, but it doesn't belong to the contained account!" );
"Message was signed by contained key, but it doesn't belong to the contained account!" );*/
FC_ASSERT( !"Not implemented!" );
return true;
}

@@ -2686,6 +2684,25 @@ class wallet_api_impl
return ss.str();
};

m["sign_message"] = [this](variant result, const fc::variants& a)
{
auto r = result.as<signed_message>( GRAPHENE_MAX_NESTED_OBJECTS );

fc::stringstream encapsulated;
encapsulated << ENC_HEADER;
encapsulated << r.message << '\n';
encapsulated << ENC_META;
encapsulated << "account=" << r.meta.account << '\n';
encapsulated << "memokey=" << std::string( r.meta.memo_key ) << '\n';
encapsulated << "block=" << r.meta.block << '\n';
encapsulated << "timestamp=" << r.meta.time << '\n';
encapsulated << ENC_SIG;
encapsulated << fc::to_hex( (const char*)r.signature->data, r.signature->size() ) << '\n';
encapsulated << ENC_FOOTER;

return encapsulated.str();
};

return m;
}

@@ -3185,6 +3202,17 @@ std::string operation_result_printer::operator()(const asset& a)
}}}

namespace graphene { namespace wallet {
fc::sha256 signed_message::digest()const
{
fc::stringstream to_sign;
to_sign << message << '\n';
to_sign << "account=" << meta.account << '\n';
to_sign << "memokey=" << std::string( meta.memo_key ) << '\n';
to_sign << "block=" << meta.block << '\n';
to_sign << "timestamp=" << meta.time;

return fc::sha256::hash( to_sign.str() );
}
vector<brain_key_info> utility::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys)
{
// Safety-check
@@ -4535,7 +4563,7 @@ signed_message wallet_api::sign_message(string signer, string message)
return my->sign_message(signer, message);
}

bool wallet_api::verify_message(signed_message message)
bool wallet_api::verify_message(string message)
{
return my->verify_message(message);
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.