10.5. Signing a message with go

The following file should give you a quick introduction into how the messages are signed with go

10.5.1. Create files

Listing 10.1 sign.go
package main

import (
    "crypto/ecdsa"
    "encoding/hex"
    "fmt"
    "github.com/ethereum/go-ethereum/crypto"
    "golang.org/x/crypto/sha3"
    "os"
    "strconv"
    "strings"
)

// ---- Utility functions ----

// strip0x remove the 0x prefix, which is not important to us
func strip0x(s string) string {
    if strings.HasPrefix(s, "0x") {
        s = s[2:]
    }
    return s
}

// ---- Main functions ----

func Sign(hash []byte, privateKey string) (string, error) {
    var key *ecdsa.PrivateKey
    var bytes []byte

    if b, err := hex.DecodeString(strip0x(privateKey)); err != nil {
        return "", err
    } else {
        bytes = b
    }
    if pk, err := crypto.ToECDSA(bytes); err != nil {
        return "", err
    } else {
        key = pk
    }
    if sig, err := crypto.Sign(hash, key); err != nil {
        return "", err
    } else {
        return "0x" + hex.EncodeToString(sig), nil
    }
}

func SignHash(message []byte, privateKey string) (string, error) {
    msglen := []byte(strconv.Itoa(len(message)))

    hash := sha3.NewLegacyKeccak256()
    hash.Write([]byte{0x19})
    hash.Write([]byte("Ethereum Signed Message:"))
    hash.Write([]byte{0x0A})
    hash.Write(msglen)
    hash.Write(message)
    buf := hash.Sum([]byte{})

    return Sign(buf, privateKey)
}

func SignChallenge(challenge, privateKey string) (string, error) {
    hash := sha3.NewLegacyKeccak256()
    hash.Write([]byte(challenge))
    buf := hash.Sum([]byte{})
    return SignHash(buf, privateKey)
}

func main() {
    // Remove executable name from arguments
    args := os.Args[1:]

    // Just take the parameters. In real-live scenario, you would probably want to read the
    // private key from stdin and do some parameter validation.
    challenge := args[0]
    privateKey := args[1]

    // Quick and dirty way of doing it. You probably don't want to call panic in your code, though.
    if result, err := SignChallenge(challenge, privateKey); err != nil {
        panic(err)
    } else {
        if _, err := fmt.Fprintf(os.Stdout, "%v", result); err != nil {
            panic(err)
        }
    }
}

10.5.2. Install dependencies

Execute the following command:

go get ./...

10.5.3. Test signing

Execute the following command:

go run sign.go u3Z5LUDVp31tMtKtwCGr1FCs3kw a0f0e1232dae3ce8a0bf968c452602663975005537e37e79d28c23af52b3114e

Example output:

0x2a2785be5d38ba773765df2232d749b01c4ebc97721f9d033a696b7f893ba45d20bccf4341ae6b01a8f697e22e1e4b4697e02e04352363bee1eeaa9494e096cb01