diff --git a/commons/base58/alphabet.go b/commons/base58/alphabet.go new file mode 100644 index 0000000..44b9b52 --- /dev/null +++ b/commons/base58/alphabet.go @@ -0,0 +1,44 @@ +// Package base58 provides ... +package base58 + +const ( + // alphabet is the modified base58 alphabet used by Bitcoin. + alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + alphabetIdx0 = '1' +) + +var b58 = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 255, 255, 255, 255, 255, 255, + 255, 9, 10, 11, 12, 13, 14, 15, + 16, 255, 17, 18, 19, 20, 21, 255, + 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 255, 255, 255, 255, 255, + 255, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 255, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, +} diff --git a/commons/base58/base58.go b/commons/base58/base58.go new file mode 100644 index 0000000..510d35b --- /dev/null +++ b/commons/base58/base58.go @@ -0,0 +1,69 @@ +// Package base58 provides ... +// https://github.com/btcsuite/btcutil/blob/master/base58/base58.go +package base58 + +import "math/big" + +var bigRadix = big.NewInt(58) +var bigZero = big.NewInt(0) + +// Decode decodes a modified base58 string to a byte slice. +func Decode(b string) []byte { + answer := big.NewInt(0) + j := big.NewInt(1) + + scratch := new(big.Int) + for i := len(b) - 1; i >= 0; i-- { + tmp := b58[b[i]] + if tmp == 255 { + return []byte("") + } + scratch.SetInt64(int64(tmp)) + scratch.Mul(j, scratch) + answer.Add(answer, scratch) + j.Mul(j, bigRadix) + } + + tmpval := answer.Bytes() + + var numZeros int + for numZeros = 0; numZeros < len(b); numZeros++ { + if b[numZeros] != alphabetIdx0 { + break + } + } + flen := numZeros + len(tmpval) + val := make([]byte, flen) + copy(val[numZeros:], tmpval) + + return val +} + +// Encode encodes a byte slice to a modified base58 string. +func Encode(b []byte) string { + x := new(big.Int) + x.SetBytes(b) + + answer := make([]byte, 0, len(b)*136/100) + for x.Cmp(bigZero) > 0 { + mod := new(big.Int) + x.DivMod(x, bigRadix, mod) + answer = append(answer, alphabet[mod.Int64()]) + } + + // leading zero bytes + for _, i := range b { + if i != 0 { + break + } + answer = append(answer, alphabetIdx0) + } + + // reverse + alen := len(answer) + for i := 0; i < alen/2; i++ { + answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] + } + + return string(answer) +} diff --git a/commons/base58/base58check.go b/commons/base58/base58check.go new file mode 100644 index 0000000..3917aca --- /dev/null +++ b/commons/base58/base58check.go @@ -0,0 +1,23 @@ +// Package basen provides ... +package base58 + +import ( + "crypto/sha256" +) + +func checksum(input []byte) (cksum [4]byte) { + h := sha256.Sum256(input) + h2 := sha256.Sum256(h[:]) + copy(cksum[:], h2[:4]) + return +} + +func CheckEncode(input []byte) string { + b := make([]byte, 0, len(input)+4) + b = append(b, input[:]...) + cksum := checksum(input) + b = append(b, cksum[:]...) + // return base582.Encode(b) + return Encode(b) + +} diff --git a/commons/bip32path.go b/commons/bip32path.go new file mode 100644 index 0000000..fb56e7a --- /dev/null +++ b/commons/bip32path.go @@ -0,0 +1,77 @@ +// Package bip32 provides ... +package commons + +import ( + "errors" + "strconv" + "strings" +) + +var ErrKeyPathFormat = errors.New("Wallet Path Error") +var ErrParentKey = errors.New("Key must master") + +var mapKey = make(map[string]*ExtendedKey) + +// DerivePath return key by path : m/0'/1/2' etc... +func (key *ExtendedKey) DerivePath(pathStr string) (*ExtendedKey, error) { + //fmt.Println("###########", len(mapKey)) + if key.childNum > 0 { + return nil, ErrParentKey + } + keyTmp := mapKey[pathStr] + path := strings.Split(pathStr, "/") + err := vaildPath(path) + if err != nil { + return nil, err + } + tmpPath := []string{} + var tmpPathStr string + var tmpParentKey *ExtendedKey + for _, childNumStr := range path { + tmpPath = append(tmpPath, childNumStr) + tmpPathStr = strings.Join(tmpPath, "/") + keyTmp = mapKey[tmpPathStr] + if tmpPathStr == "m" { + keyTmp = key + } else { + isHardenedChild := false + if strings.HasSuffix(childNumStr, "'") { + childNumStr = strings.Replace(childNumStr, "'", "", -1) + isHardenedChild = true + } + childNum, _ := strconv.Atoi(childNumStr) + var err error + if isHardenedChild { + keyTmp, err = tmpParentKey.HardenedChild(uint32(childNum)) + } else { + keyTmp, err = tmpParentKey.Child(uint32(childNum)) + } + if err != nil { + return nil, err + } + } + mapKey[tmpPathStr] = keyTmp + tmpParentKey = keyTmp + } + return keyTmp, nil +} + +func vaildPath(path []string) error { + if path[0] != "m" { + return ErrKeyPathFormat + } + for i := 1; i < len(path); i++ { + childNumStr := path[i] + if strings.HasSuffix(childNumStr, "'") { + childNumStr = strings.Replace(childNumStr, "'", "", -1) + } + childNum, err := strconv.Atoi(childNumStr) + if err != nil { + return ErrKeyPathFormat + } + if uint32(childNum) >= HardenedKeyStart || childNum < 0 { + return ErrKeyPathFormat + } + } + return nil +} diff --git a/commons/bytes/bytes.go b/commons/bytes/bytes.go new file mode 100644 index 0000000..28e96a4 --- /dev/null +++ b/commons/bytes/bytes.go @@ -0,0 +1,35 @@ +// Package bytes provides ... +package bytes + +import ( + "errors" + "math/big" +) + +// PaddedAppend append src to dst, if less than size padding 0 at start +func PaddedAppend(dst []byte, srcPaddedSize int, src []byte) []byte { + return append(dst, PaddedBytes(srcPaddedSize, src)...) +} + +// PaddedBytes padding byte array to size length +func PaddedBytes(size int, src []byte) []byte { + offset := size - len(src) + tmp := src + if offset > 0 { + tmp = make([]byte, size) + copy(tmp[offset:], src) + } + return tmp +} + +// BytesFromHexStrFixZeroPrefix return fix Zero start strings +// like 00010203040506 +func BytesFromHexStrFixZeroPrefix(str string) ([]byte, error) { + strNum, ok := new(big.Int).SetString(str, 16) + if !ok { + return nil, errors.New("string error") + } + bytes := strNum.Bytes() + bytes = PaddedBytes(len(str)/2, bytes) + return bytes, nil +} diff --git a/commons/ec/address.go b/commons/ec/address.go new file mode 100644 index 0000000..e090d8b --- /dev/null +++ b/commons/ec/address.go @@ -0,0 +1,47 @@ +// Package address provides ... +package ec + +import ( + "errors" + + "golang.org/x/crypto/ripemd160" + "github.com/WaykiChain/wicc-wallet-utils-go/commons/base58" +) + +// AddressPubKeyHash pay-to-pubkey-hash (P2PKH) +type AddressPubKeyHash struct { + hash [ripemd160.Size]byte + version byte +} + +func NewAddressPubKeyHash(pkHash []byte, version byte) (*AddressPubKeyHash, error) { + return newAddressPubKeyHash(pkHash, version) +} + +func newAddressPubKeyHash(pkHash []byte, version byte) (*AddressPubKeyHash, error) { + if len(pkHash) != ripemd160.Size { + return nil, errors.New("pkHash must be 20 bytes") + } + addr := &AddressPubKeyHash{} + addr.version = version + copy(addr.hash[:], pkHash) + return addr, nil +} + +// EncodeAddress return P2PKH address +func (a *AddressPubKeyHash) EncodeAddress() string { + return encodeAddress(a.hash[:], a.version) +} + +// P2PKH P2SH address encoding +func encodeAddress(hash160 []byte, version byte) string { + input := make([]byte, 21) + input[0] = version + copy(input[1:], hash160) + return base58.CheckEncode(input) +} + +// Hash160 return hash160 +func (a *AddressPubKeyHash) Hash160() []byte { + return a.hash[:] +} diff --git a/commons/ec/privkey.go b/commons/ec/privkey.go new file mode 100644 index 0000000..dbd1105 --- /dev/null +++ b/commons/ec/privkey.go @@ -0,0 +1,30 @@ +// Package ec provides ... +package ec + +import ( + "math/big" + "github.com/WaykiChain/wicc-wallet-utils-go/commons/bytes" +) + +const PrivKeyBytesLen = 32 + +type PrivateKey struct { + PubKey PublicKey + D *big.Int +} + +func PrivKeyFromBytes(bytes []byte) (*PrivateKey, *PublicKey) { + x, y := secp256k1.ScalarBaseMult(bytes) + privKey := &PrivateKey{ + PubKey: PublicKey{ + X: x, + Y: y, + }, + D: new(big.Int).SetBytes(bytes), + } + return privKey, &privKey.PubKey +} + +func (privKey *PrivateKey) Serialize() []byte { + return bytes.PaddedBytes(32, privKey.D.Bytes()) +} diff --git a/commons/ec/pubkey.go b/commons/ec/pubkey.go new file mode 100644 index 0000000..685169b --- /dev/null +++ b/commons/ec/pubkey.go @@ -0,0 +1,133 @@ +// Package ec provides ... +package ec + +import ( + "errors" + "fmt" + "math/big" + "github.com/WaykiChain/wicc-wallet-utils-go/commons/bytes" +) + +const ( + PubKeyBytesLenCompressed = 33 + PubKeyBytesLenUncompressed = 65 + PubKeyBytesLenHybrid = 65 + + pubKeyCompressed byte = 0x2 + pubKeyUncompressed byte = 0x4 + pubKeyHybrid byte = 0x6 +) + +// PublicKey struct +type PublicKey struct { + X, Y *big.Int +} + +func isOdd(a *big.Int) bool { + return a.Bit(0) == 1 +} + +// decompressPoint decompresses a point on the given curve given the X point and +// the solution to use. +func decompressPoint(x *big.Int, ybit bool) (*big.Int, error) { + // TODO: This will probably only work for secp256k1 due to + // optimizations. + + // Y = +-sqrt(x^3 + B) + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + x3.Add(x3, secp256k1.Params().B) + x3.Mod(x3, secp256k1.Params().P) + + // Now calculate sqrt mod p of x^3 + B + // This code used to do a full sqrt based on tonelli/shanks, + // but this was replaced by the algorithms referenced in + // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 + y := new(big.Int).Exp(x3, secp256k1.QPlus1Div4(), secp256k1.Params().P) + + if ybit != isOdd(y) { + y.Sub(secp256k1.Params().P, y) + } + + // Check that y is a square root of x^3 + B. + y2 := new(big.Int).Mul(y, y) + y2.Mod(y2, secp256k1.Params().P) + if y2.Cmp(x3) != 0 { + return nil, fmt.Errorf("invalid square root") + } + + // Verify that y-coord has expected parity. + if ybit != isOdd(y) { + return nil, fmt.Errorf("ybit doesn't match oddness") + } + + return y, nil +} + +// ParsePubKey parses a public key for a koblitz curve from a bytestring into a +// ecdsa.Publickey, verifying that it is valid. It supports compressed, +// uncompressed and hybrid signature formats. +func ParsePubKey(pubKeyStr []byte) (key *PublicKey, err error) { + pubkey := PublicKey{} + + if len(pubKeyStr) == 0 { + return nil, errors.New("pubkey string is empty") + } + + format := pubKeyStr[0] + ybit := (format & 0x1) == 0x1 + format &= ^byte(0x1) + + switch len(pubKeyStr) { + case PubKeyBytesLenUncompressed: + if format != pubKeyUncompressed && format != pubKeyHybrid { + return nil, fmt.Errorf("invalid magic in pubkey str: "+ + "%d", pubKeyStr[0]) + } + + pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) + pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:]) + // hybrid keys have extra information, make use of it. + if format == pubKeyHybrid && ybit != isOdd(pubkey.Y) { + return nil, fmt.Errorf("ybit doesn't match oddness") + } + case PubKeyBytesLenCompressed: + // format is 0x2 | solution, + // solution determines which solution of the curve we use. + /// y^2 = x^3 + Curve.B + if format != pubKeyCompressed { + return nil, fmt.Errorf("invalid magic in compressed "+ + "pubkey string: %d", pubKeyStr[0]) + } + pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) + pubkey.Y, err = decompressPoint(pubkey.X, ybit) + if err != nil { + return nil, err + } + default: // wrong! + return nil, fmt.Errorf("invalid pub key length %d", + len(pubKeyStr)) + } + + if pubkey.X.Cmp(secp256k1.Params().P) >= 0 { + return nil, fmt.Errorf("pubkey X parameter is >= to P") + } + if pubkey.Y.Cmp(secp256k1.Params().P) >= 0 { + return nil, fmt.Errorf("pubkey Y parameter is >= to P") + } + if !secp256k1.IsOnCurve(pubkey.X, pubkey.Y) { + return nil, fmt.Errorf("pubkey isn't on secp256k1 curve") + } + return &pubkey, nil +} + +// SerializeCompressed serializes a public key 33-byte compressed format +func (key *PublicKey) SerializeCompressed() []byte { + b := make([]byte, 0, PubKeyBytesLenCompressed) + format := pubKeyCompressed + if isOdd(key.Y) { + format |= 0x1 + } + b = append(b, format) + return bytes.PaddedAppend(b, 32, key.X.Bytes()) +} diff --git a/commons/ec/secp256k1.go b/commons/ec/secp256k1.go new file mode 100644 index 0000000..35f1080 --- /dev/null +++ b/commons/ec/secp256k1.go @@ -0,0 +1,232 @@ +package ec + +import ( + "crypto/elliptic" + "math/big" +) + +// Koblitz curve math +// http://www.secg.org/sec2-v2.pdf 2.4.1 +// https://github.com/mndrix/btcutil/blob/master/secp256k1.go +// https://github.com/btcsuite/btcd/blob/master/btcec/btcec.go + +// KoblitzCurve A Koblitz Curve with a=0. +type KoblitzCurve struct { + *elliptic.CurveParams + q *big.Int +} + +var secp256k1 *KoblitzCurve + +// Secp265k1 return Curve +func Secp265k1() *KoblitzCurve { + return secp256k1 +} + +func init() { + secp256k1 = new(KoblitzCurve) + secp256k1.CurveParams = new(elliptic.CurveParams) + secp256k1.Name = "secp256k1" + secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16) + secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) + secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16) + secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) + secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) + secp256k1.BitSize = 256 + + secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P, + big.NewInt(1)), big.NewInt(4)) +} + +// Params returns the parameters for the curve +func (curve *KoblitzCurve) Params() *elliptic.CurveParams { + return secp256k1.CurveParams +} + +// IsOnCurve reports whether the given (x,y) lies on the curve. +func (curve *KoblitzCurve) IsOnCurve(x *big.Int, y *big.Int) bool { + // y^2 mod p = ( x^3 + b) mod p + y2 := new(big.Int).Mul(y, y) + y2.Mod(y2, curve.P) + + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + + x3.Add(x3, curve.B) + x3.Mod(x3, curve.P) + return x3.Cmp(y2) == 0 +} + +func (curve *KoblitzCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + zinv := new(big.Int).ModInverse(z, curve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, curve.P) + + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, curve.P) + return +} + +// Add returns the sum of (x1,y1) and (x2,y2) +func (curve *KoblitzCurve) Add(x1 *big.Int, y1 *big.Int, x2 *big.Int, y2 *big.Int) (x *big.Int, y *big.Int) { + z := new(big.Int).SetInt64(1) + return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z)) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, curve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, curve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, curve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, curve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, curve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, curve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, curve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, curve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, curve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, curve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, curve.P) + + return x3, y3, z3 +} + +// Double returns 2*(x,y) +func (curve *KoblitzCurve) Double(x1 *big.Int, y1 *big.Int) (x *big.Int, y *big.Int) { + z1 := new(big.Int).SetInt64(1) + return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (curve *KoblitzCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + a := new(big.Int).Mul(x, x) //X1² + b := new(big.Int).Mul(y, y) //Y1² + c := new(big.Int).Mul(b, b) //B² + + d := new(big.Int).Add(x, b) //X1+B + d.Mul(d, d) //(X1+B)² + d.Sub(d, a) //(X1+B)²-A + d.Sub(d, c) //(X1+B)²-A-C + d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C) + + e := new(big.Int).Mul(big.NewInt(3), a) //3*A + f := new(big.Int).Mul(e, e) //E² + + x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D + x3.Sub(f, x3) //F-2*D + x3.Mod(x3, curve.P) + + y3 := new(big.Int).Sub(d, x3) //D-X3 + y3.Mul(e, y3) //E*(D-X3) + y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C + y3.Mod(y3, curve.P) + + z3 := new(big.Int).Mul(y, z) //Y1*Z1 + z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 + z3.Mod(z3, curve.P) + + return x3, y3, z3 +} + +// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. +func (curve *KoblitzCurve) ScalarMult(x1 *big.Int, y1 *big.Int, k []byte) (*big.Int, *big.Int) { + // We have a slight problem in that the identity of the group (the + // point at infinity) cannot be represented in (x, y) form on a finite + // machine. Thus the standard add/double algorithm has to be tweaked + // slightly: our initial state is not the identity, but x, and we + // ignore the first true bit in |k|. If we don't find any true bits in + // |k|, then we return nil, nil, because we cannot return the identity + // element. + Bz := new(big.Int).SetInt64(1) + x := x1 + y := y1 + z := Bz + + seenFirstTrue := false + for _, byte := range k { + for bitNum := 0; bitNum < 8; bitNum++ { + if seenFirstTrue { + x, y, z = curve.doubleJacobian(x, y, z) + } + if byte&0x80 == 0x80 { + if !seenFirstTrue { + seenFirstTrue = true + } else { + x, y, z = curve.addJacobian(x1, y1, Bz, x, y, z) + } + } + byte <<= 1 + } + } + + if !seenFirstTrue { + return nil, nil + } + + return curve.affineFromJacobian(x, y, z) +} + +// ScalarBaseMult returns k*G, where G is the base point of the group +// and k is an integer in big-endian form. +func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (x *big.Int, y *big.Int) { + return curve.ScalarMult(curve.Gx, curve.Gy, k) +} + +// QPlus1Div4 returns the Q+1/4 constant for the curve for use in calculating +// square roots via exponention. +func (curve *KoblitzCurve) QPlus1Div4() *big.Int { + return curve.q +} diff --git a/commons/hash/hash.go b/commons/hash/hash.go new file mode 100644 index 0000000..40aa333 --- /dev/null +++ b/commons/hash/hash.go @@ -0,0 +1,31 @@ +// Package utils provides ... +package hash + +import "crypto/sha256" +import "golang.org/x/crypto/ripemd160" + +// Hash256 sha256(b) +func Hash256(bytes []byte) []byte { + hash := sha256.Sum256(bytes) + return hash[:] +} + +// DoubleHash256 double sha256(b) +func DoubleHash256(bytes []byte) []byte { + first := sha256.Sum256(bytes) + second := sha256.Sum256(first[:]) + return second[:] +} + +// Hash160 https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#key-identifiers +// Hash160 (RIPEMD160 after SHA256) +func Hash160(bytes []byte) []byte { + hash1 := sha256.Sum256(bytes) + return hashRipeMD160(hash1[:]) +} + +func hashRipeMD160(data []byte) []byte { + hasher := ripemd160.New() + hasher.Write(data) + return hasher.Sum(nil) +} diff --git a/commons/hash/hash_test.go b/commons/hash/hash_test.go new file mode 100644 index 0000000..0b7e75b --- /dev/null +++ b/commons/hash/hash_test.go @@ -0,0 +1,10 @@ +package hash + +import ( + "fmt" + "testing" +) + +func TestHash256(t *testing.T) { + fmt.Printf("%x\n", Hash256([]byte{0, 0})) +} diff --git a/commons/wif/wif.go b/commons/wif/wif.go new file mode 100644 index 0000000..3b59942 --- /dev/null +++ b/commons/wif/wif.go @@ -0,0 +1,46 @@ +// Package wif provides ... +package wif + +import ( + "github.com/WaykiChain/wicc-wallet-utils-go/commons/ec" + "github.com/WaykiChain/wicc-wallet-utils-go/commons/hash" + "github.com/WaykiChain/wicc-wallet-utils-go/commons/base58" +) + +type WIF struct { + privKey *ec.PrivateKey + version byte +} + +func NewWIF(privKey *ec.PrivateKey, version byte) *WIF { + return &WIF{ + privKey: privKey, + version: version, + } +} + +// Encode return WIF-uncompressed +func (wif *WIF) Encode() string { + return wif.encode(false) +} + +// EncodeCompressed return WIF-compressed +func (wif *WIF) EncodeCompressed() string { + return wif.encode(true) +} + +func (wif *WIF) encode(compressPubKey bool) string { + encodeLen := 1 + ec.PrivKeyBytesLen + 4 + if compressPubKey { + encodeLen++ + } + b := make([]byte, 0, encodeLen) + b = append(b, wif.version) + b = append(b, wif.privKey.Serialize()...) + if compressPubKey { + b = append(b, 0x01) + } + checksum := hash.DoubleHash256(b)[:4] + b = append(b, checksum...) + return base58.Encode(b) +}