I am trying to sign a message in go
generated via hd wallet's private key using cosmos sdk. Below is the equivalent implementation in python which generates the signed message / signature as expected when submitted/verified is working properly but unable to get it working wtih Go
implementation. Any inputs for equivalent golang version of the python implementation is much appreciated. Thank you.
Python version uses sha256 , ecdsa but when using the equivalent cyrpto/ecdsa doesn't return valid signature.
Python
def test_sign_message(self):
""" Tests the ability of the signer to sing message """
# Loading up the signer object to use for the operation
signer: TestSigners = TestSigners.from_mnemonic("blast about old claw current first paste risk involve victory edit current")
sample_payload_to_sign = "75628d14409a5126e6c882d05422c06f5eccaa192c082a9a5695a8e707109842'
# print("test".encode("UTF-8").hex())
s = signer.sign(sample_payload_to_sign)
print(s)
from typing import List, Tuple, Dict, Union, Any
from hdwallet.hdwallet import HDWallet
from ecdsa.util import sigencode_der
from ecdsa.curves import SECP256k1
from ecdsa.keys import SigningKey
import mnemonic
import hashlib
import ecdsa
class TestSigners():
HD_WALLET_PARAMS: Dict[str, Tuple[int, bool]] = {
"purpose": (44, True),
"coinType": (1022, True),
"account": (0, True),
"change": (0, False),
}
def __init__(
self,
seed: Union[bytes, bytearray, str]
) -> None:
""" Instantiates a new signer object from the seed phrase
Args:
seed (Union[bytes, bytearray, str]): The seed phrase used to generate the public and
private keys.
"""
self.seed: Union[bytes, bytearray] = seed if isinstance(seed, (bytes, bytearray)) else bytearray.fromhex(seed)
@classmethod
def from_mnemonic(
cls,
mnemonic_phrase: Union[str, List[str], Tuple[str]]
) -> 'Signer':
"""
Instantiates a new Signer object from the mnemonic phrase passed.
Args:
mnemonic_phrase (Union[str, :obj:`list` of :obj:`str`, :obj:`tuple` of :obj:`str`):
A string, list, or a tuple of the mnemonic phrase. If the argument is passed as an
iterable, then it will be joined with a space.
Returns:
Signer: A new signer initalized through the mnemonic phrase.
"""
# If the supplied mnemonic phrase is a list then convert it to a string
if isinstance(mnemonic_phrase, (list, tuple)):
mnemonic_string: str = " ".join(mnemonic_phrase)
else:
mnemonic_string: str = mnemonic_phrase
mnemonic_string: str = " ".join(mnemonic_phrase) if isinstance(mnemonic_phrase,
(list, tuple)) else mnemonic_phrase
return cls(mnemonic.Mnemonic.to_seed(mnemonic_string))
def public_key(
self,
index: int = 0
) -> str:
"""
Gets the public key for the signer for the specified account index
Args:
index (int): The account index to get the public keys for.
Returns:
str: A string of the public key for the wallet
"""
return str(self.hdwallet(index).public_key())
def private_key(
self,
index: int = 0
) -> str:
"""
Gets the private key for the signer for the specified account index
Args:
index (int): The account index to get the private keys for.
Returns:
str: A string of the private key for the wallet
"""
return str(self.hdwallet(index).private_key())
def hdwallet(
self,
index: int = 0
) -> HDWallet:
"""
Creates an HDWallet object suitable for the Radix blockchain with the passed account index.
Args:
index (int): The account index to create the HDWallet object for.
Returns:
HDWallet: An HD wallet object created with the Radix Parameters for a given account
index.
"""
hdwallet: HDWallet = HDWallet()
hdwallet.from_seed(seed=self.seed.hex())
for _, values_tuple in self.HD_WALLET_PARAMS.items():
value, hardened = values_tuple
hdwallet.from_index(value, hardened=hardened)
hdwallet.from_index(index, True)
return hdwallet
def sign(
self,
data: str,
index: int = 0
) -> str:
"""
Signs the given data using the private keys for the account at the specified account index.
Arguments:
data (str): A string of the data which we wish to sign.
index (int): The account index to get the private keys for.
Returns:
str: A string of the signed data
"""
signing_key: SigningKey = ecdsa.SigningKey.from_string( # type: ignore
string=bytearray.fromhex(self.private_key(index)),
curve=SECP256k1,
hashfunc=hashlib.sha256
)
return signing_key.sign_digest( # type: ignore
digest=bytearray.fromhex(data),
sigencode=sigencode_der
).hex()
GO ( Not Working )
package main
import (
"encoding/hex"
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/go-bip39"
"github.com/decred/dcrd/bech32"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
func main() {
seed := bip39.NewSeed("blast about old claw current first paste risk involve victory edit current", "")
fmt.Println("Seed: ", hex.EncodeToString(seed)) // Seed: dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd
master, ch := hd.ComputeMastersFromSeed(seed)
path := "m/44'/1022'/0'/0/0'"
priv, err := hd.DerivePrivateKeyForPath(master, ch, path)
if err != nil {
t.Fatal(err)
}
fmt.Println("Derivation Path: ", path) // Derivation Path: m/44'/118'/0'/0/0'
fmt.Println("Private Key: ", hex.EncodeToString(priv)) // Private Key: 69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a
var privKey = secp256k1.PrivKey(priv)
pubKey := privKey.PubKey()
fmt.Println("Public Key: ", hex.EncodeToString(pubKey.Bytes())) // Public Key: 03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8
//str := "test"
str := "75628d14409a5126e6c882d05422c06f5eccaa192c082a9a5695a8e707109842"
//hx := hex.EncodeToString([]byte(str))
//fmt.Println(hx)
sign, err := privKey.Sign([]byte(str))
if err != nil {
return
}
fmt.Println(hex.EncodeToString(sign))
}