|
|
- package hd
-
- import (
- "crypto/ecdsa"
- "crypto/hmac"
- "crypto/sha256"
- "crypto/sha512"
- "encoding/base64"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "fmt"
- "hash"
- "math/big"
- "strconv"
- "strings"
-
- "github.com/btcsuite/btcd/btcec"
- "github.com/btcsuite/btcutil/base58"
- "github.com/tendermint/go-crypto"
- "golang.org/x/crypto/ripemd160"
- )
-
- func ComputeAddress(pubKeyHex string, chainHex string, path string, index int32) string {
- pubKeyBytes := DerivePublicKeyForPath(
- HexDecode(pubKeyHex),
- HexDecode(chainHex),
- fmt.Sprintf("%v/%v", path, index),
- )
- return AddrFromPubKeyBytes(pubKeyBytes)
- }
-
- func ComputePrivateKey(mprivHex string, chainHex string, path string, index int32) string {
- privKeyBytes := DerivePrivateKeyForPath(
- HexDecode(mprivHex),
- HexDecode(chainHex),
- fmt.Sprintf("%v/%v", path, index),
- )
- return HexEncode(privKeyBytes)
- }
-
- func ComputeAddressForPrivKey(privKey string) string {
- pubKeyBytes := PubKeyBytesFromPrivKeyBytes(HexDecode(privKey), true)
- return AddrFromPubKeyBytes(pubKeyBytes)
- }
-
- func SignMessage(privKey string, message string, compress bool) string {
- prefixBytes := []byte("Bitcoin Signed Message:\n")
- messageBytes := []byte(message)
- bytes := []byte{}
- bytes = append(bytes, byte(len(prefixBytes)))
- bytes = append(bytes, prefixBytes...)
- bytes = append(bytes, byte(len(messageBytes)))
- bytes = append(bytes, messageBytes...)
- privKeyBytes := HexDecode(privKey)
- x, y := btcec.S256().ScalarBaseMult(privKeyBytes)
- ecdsaPubKey := ecdsa.PublicKey{
- Curve: btcec.S256(),
- X: x,
- Y: y,
- }
- ecdsaPrivKey := &btcec.PrivateKey{
- PublicKey: ecdsaPubKey,
- D: new(big.Int).SetBytes(privKeyBytes),
- }
- sigbytes, err := btcec.SignCompact(btcec.S256(), ecdsaPrivKey, crypto.Sha256(crypto.Sha256(bytes)), compress)
- if err != nil {
- panic(err)
- }
- return base64.StdEncoding.EncodeToString(sigbytes)
- }
-
- // returns MPK, Chain, and master secret in hex.
- func ComputeMastersFromSeed(seed string) (string, string, string, string) {
- secret, chain := I64([]byte("Bitcoin seed"), []byte(seed))
- pubKeyBytes := PubKeyBytesFromPrivKeyBytes(secret, true)
- return HexEncode(pubKeyBytes), HexEncode(secret), HexEncode(chain), HexEncode(secret)
- }
-
- func ComputeWIF(privKey string, compress bool) string {
- return WIFFromPrivKeyBytes(HexDecode(privKey), compress)
- }
-
- func ComputeTxId(rawTxHex string) string {
- return HexEncode(ReverseBytes(CalcHash256(HexDecode(rawTxHex))))
- }
-
- // Private methods...
-
- func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []byte {
- data := privKeyBytes
- parts := strings.Split(path, "/")
- for _, part := range parts {
- prime := part[len(part)-1:] == "'"
- // prime == private derivation. Otherwise public.
- if prime {
- part = part[:len(part)-1]
- }
- i, err := strconv.Atoi(part)
- if err != nil {
- panic(err)
- }
- if i < 0 {
- panic(errors.New("index too large."))
- }
- data, chain = DerivePrivateKey(data, chain, uint32(i), prime)
- //printKeyInfo(data, nil, chain)
- }
- return data
- }
-
- func DerivePublicKeyForPath(pubKeyBytes []byte, chain []byte, path string) []byte {
- data := pubKeyBytes
- parts := strings.Split(path, "/")
- for _, part := range parts {
- prime := part[len(part)-1:] == "'"
- if prime {
- panic(errors.New("cannot do a prime derivation from public key"))
- }
- i, err := strconv.Atoi(part)
- if err != nil {
- panic(err)
- }
- if i < 0 {
- panic(errors.New("index too large."))
- }
- data, chain = DerivePublicKey(data, chain, uint32(i))
- //printKeyInfo(nil, data, chain)
- }
- return data
- }
-
- func DerivePrivateKey(privKeyBytes []byte, chain []byte, i uint32, prime bool) ([]byte, []byte) {
- data := []byte{} // nolint [ megacheck, deadcode ]
- if prime {
- i = i | 0x80000000
- data = append([]byte{byte(0)}, privKeyBytes...)
- } else {
- public := PubKeyBytesFromPrivKeyBytes(privKeyBytes, true)
- data = public
- }
- data = append(data, uint32ToBytes(i)...)
- data2, chain2 := I64(chain, data)
- x := addScalars(privKeyBytes, data2)
- return x, chain2
- }
-
- func DerivePublicKey(pubKeyBytes []byte, chain []byte, i uint32) ([]byte, []byte) {
- data := []byte{}
- data = append(data, pubKeyBytes...)
- data = append(data, uint32ToBytes(i)...)
- data2, chain2 := I64(chain, data)
- data2p := PubKeyBytesFromPrivKeyBytes(data2, true)
- return addPoints(pubKeyBytes, data2p), chain2
- }
-
- func addPoints(a []byte, b []byte) []byte {
- ap, err := btcec.ParsePubKey(a, btcec.S256())
- if err != nil {
- panic(err)
- }
- bp, err := btcec.ParsePubKey(b, btcec.S256())
- if err != nil {
- panic(err)
- }
- sumX, sumY := btcec.S256().Add(ap.X, ap.Y, bp.X, bp.Y)
- sum := (*btcec.PublicKey)(&btcec.PublicKey{ // nolint: unconvert
- Curve: btcec.S256(),
- X: sumX,
- Y: sumY,
- })
- return sum.SerializeCompressed()
- }
-
- func addScalars(a []byte, b []byte) []byte {
- aInt := new(big.Int).SetBytes(a)
- bInt := new(big.Int).SetBytes(b)
- sInt := new(big.Int).Add(aInt, bInt)
- x := sInt.Mod(sInt, btcec.S256().N).Bytes()
- x2 := [32]byte{}
- copy(x2[32-len(x):], x)
- return x2[:]
- }
-
- func uint32ToBytes(i uint32) []byte {
- b := [4]byte{}
- binary.BigEndian.PutUint32(b[:], i)
- return b[:]
- }
-
- func HexEncode(b []byte) string {
- return hex.EncodeToString(b)
- }
-
- func HexDecode(str string) []byte {
- b, _ := hex.DecodeString(str)
- return b
- }
-
- func I64(key []byte, data []byte) ([]byte, []byte) {
- mac := hmac.New(sha512.New, key)
- mac.Write(data)
- I := mac.Sum(nil)
- return I[:32], I[32:]
- }
-
- // This returns a Bitcoin-like address.
- func AddrFromPubKeyBytes(pubKeyBytes []byte) string {
- prefix := byte(0x00) // TODO Make const or configurable
- h160 := CalcHash160(pubKeyBytes)
- h160 = append([]byte{prefix}, h160...)
- checksum := CalcHash256(h160)
- b := append(h160, checksum[:4]...)
- return base58.Encode(b)
- }
-
- func AddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) {
- prefix := byte(0x00) // TODO Make const or configurable
- h160 := CalcHash160(pubKeyBytes)
- _h160 := append([]byte{prefix}, h160...)
- checksum = CalcHash256(_h160)[:4]
- return h160, checksum
- }
-
- func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string {
- prefix := byte(0x80) // TODO Make const or configurable
- bytes := append([]byte{prefix}, privKeyBytes...)
- if compress {
- bytes = append(bytes, byte(1))
- }
- checksum := CalcHash256(bytes)
- bytes = append(bytes, checksum[:4]...)
- return base58.Encode(bytes)
- }
-
- func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyBytes []byte) {
- x, y := btcec.S256().ScalarBaseMult(privKeyBytes)
- pub := (*btcec.PublicKey)(&btcec.PublicKey{ // nolint: unconvert
- Curve: btcec.S256(),
- X: x,
- Y: y,
- })
-
- if compress {
- return pub.SerializeCompressed()
- }
- return pub.SerializeUncompressed()
- }
-
- // Calculate the hash of hasher over buf.
- func CalcHash(buf []byte, hasher hash.Hash) []byte {
- hasher.Write(buf)
- return hasher.Sum(nil)
- }
-
- // calculate hash160 which is ripemd160(sha256(data))
- func CalcHash160(buf []byte) []byte {
- return CalcHash(CalcHash(buf, sha256.New()), ripemd160.New())
- }
-
- // calculate hash256 which is sha256(sha256(data))
- func CalcHash256(buf []byte) []byte {
- return CalcHash(CalcHash(buf, sha256.New()), sha256.New())
- }
-
- // calculate sha512(data)
- func CalcSha512(buf []byte) []byte {
- return CalcHash(buf, sha512.New())
- }
-
- func ReverseBytes(buf []byte) []byte {
- var res []byte
- if len(buf) == 0 {
- return res
- }
-
- // Walk till mid-way, swapping bytes from each end:
- // b[i] and b[len-i-1]
- blen := len(buf)
- res = make([]byte, blen)
- mid := blen / 2
- for left := 0; left <= mid; left++ {
- right := blen - left - 1
- res[left] = buf[right]
- res[right] = buf[left]
- }
- return res
- }
|