From 1aeb45a0b8faadac8ad98ca3343d4535365b328f Mon Sep 17 00:00:00 2001 From: fudongbai <296179868@qq.com> Date: Tue, 12 Feb 2019 18:35:54 +0800 Subject: [PATCH] add export interface to keymanager --- common/common.go | 16 +++ common/uuid/codec.go | 185 +++++++++++++++++++++++++++++ common/uuid/generator.go | 244 +++++++++++++++++++++++++++++++++++++++ common/uuid/uuid.go | 137 ++++++++++++++++++++++ e2e/e2e_test.go | 4 +- keys/keys.go | 112 +++++++++++++++++- keys/keys_test.go | 64 ++++++++++ 7 files changed, 754 insertions(+), 8 deletions(-) create mode 100644 common/uuid/codec.go create mode 100644 common/uuid/generator.go create mode 100644 common/uuid/uuid.go diff --git a/common/common.go b/common/common.go index 2e794ed6..2649426e 100644 --- a/common/common.go +++ b/common/common.go @@ -1,6 +1,7 @@ package common import ( + "crypto/rand" "encoding/json" "fmt" ) @@ -21,3 +22,18 @@ func QueryParamToMap(qp interface{}) (map[string]string, error) { func CombineSymbol(baseAssetSymbol, quoteAssetSymbol string) string { return fmt.Sprintf("%s_%s", baseAssetSymbol, quoteAssetSymbol) } + +// GenerateRandomBytes returns securely generated random bytes. +// It will return an error if the system's secure random +// number generator fails to function correctly, in which +// case the caller should not continue. +func GenerateRandomBytes(n int) ([]byte, error) { + b := make([]byte, n) + _, err := rand.Read(b) + // Note that err == nil only if we read len(b) bytes. + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/common/uuid/codec.go b/common/uuid/codec.go new file mode 100644 index 00000000..9bbc5e73 --- /dev/null +++ b/common/uuid/codec.go @@ -0,0 +1,185 @@ +package uuid + +import ( + "bytes" + "encoding/hex" + "fmt" +) + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromBytesOrNil returns UUID converted from raw byte slice input. +// Same behavior as FromBytes, but returns a Nil UUID on error. +func FromBytesOrNil(input []byte) UUID { + uuid, err := FromBytes(input) + if err != nil { + return Nil + } + return uuid +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// FromStringOrNil returns UUID parsed from string input. +// Same behavior as FromString, but returns a Nil UUID on error. +func FromStringOrNil(input string) UUID { + uuid, err := FromString(input) + if err != nil { + return Nil + } + return uuid +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +// "6ba7b8109dad11d180b400c04fd430c8" +// ABNF for supported UUID text representation follows: +// uuid := canonical | hashlike | braced | urn +// plain := canonical | hashlike +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct +// hashlike := 12hexoct +// braced := '{' plain '}' +// urn := URN ':' UUID-NID ':' plain +// URN := 'urn' +// UUID-NID := 'uuid' +// 12hexoct := 6hexoct 6hexoct +// 6hexoct := 4hexoct 2hexoct +// 4hexoct := 2hexoct 2hexoct +// 2hexoct := hexoct hexoct +// hexoct := hexdig hexdig +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' +func (u *UUID) UnmarshalText(text []byte) (err error) { + switch len(text) { + case 32: + return u.decodeHashLike(text) + case 36: + return u.decodeCanonical(text) + case 38: + return u.decodeBraced(text) + case 41: + fallthrough + case 45: + return u.decodeURN(text) + default: + return fmt.Errorf("uuid: incorrect UUID length: %s", text) + } +} + +// decodeCanonical decodes UUID string in format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". +func (u *UUID) decodeCanonical(t []byte) (err error) { + if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + src := t[:] + dst := u[:] + + for i, byteGroup := range byteGroups { + if i > 0 { + src = src[1:] // skip dash + } + _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup]) + if err != nil { + return + } + src = src[byteGroup:] + dst = dst[byteGroup/2:] + } + + return +} + +// decodeHashLike decodes UUID string in format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeHashLike(t []byte) (err error) { + src := t[:] + dst := u[:] + + if _, err = hex.Decode(dst, src); err != nil { + return err + } + return +} + +// decodeBraced decodes UUID string in format +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format +// "{6ba7b8109dad11d180b400c04fd430c8}". +func (u *UUID) decodeBraced(t []byte) (err error) { + l := len(t) + + if t[0] != '{' || t[l-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + return u.decodePlain(t[1 : l-1]) +} + +// decodeURN decodes UUID string in format +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeURN(t []byte) (err error) { + total := len(t) + + urn_uuid_prefix := t[:9] + + if !bytes.Equal(urn_uuid_prefix, urnPrefix) { + return fmt.Errorf("uuid: incorrect UUID format: %s", t) + } + + return u.decodePlain(t[9:total]) +} + +// decodePlain decodes UUID string in canonical format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodePlain(t []byte) (err error) { + switch len(t) { + case 32: + return u.decodeHashLike(t) + case 36: + return u.decodeCanonical(t) + default: + return fmt.Errorf("uuid: incorrrect UUID length: %s", t) + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != Size { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} diff --git a/common/uuid/generator.go b/common/uuid/generator.go new file mode 100644 index 00000000..3e8532f4 --- /dev/null +++ b/common/uuid/generator.go @@ -0,0 +1,244 @@ +package uuid + +import ( + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "fmt" + "hash" + "io" + "net" + "os" + "sync" + "time" +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +type epochFunc func() time.Time +type hwAddrFunc func() (net.HardwareAddr, error) + +var ( + global = newRFC4122Generator() + + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() (UUID, error) { + return global.NewV1() +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) (UUID, error) { + return global.NewV2(domain) +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + return global.NewV3(ns, name) +} + +// NewV4 returns random generated UUID. +func NewV4() (UUID, error) { + return global.NewV4() +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + return global.NewV5(ns, name) +} + +// Generator provides interface for generating UUIDs. +type Generator interface { + NewV1() (UUID, error) + NewV2(domain byte) (UUID, error) + NewV3(ns UUID, name string) UUID + NewV4() (UUID, error) + NewV5(ns UUID, name string) UUID +} + +// Default generator implementation. +type rfc4122Generator struct { + clockSequenceOnce sync.Once + hardwareAddrOnce sync.Once + storageMutex sync.Mutex + + rand io.Reader + + epochFunc epochFunc + hwAddrFunc hwAddrFunc + lastTime uint64 + clockSequence uint16 + hardwareAddr [6]byte +} + +func newRFC4122Generator() Generator { + return &rfc4122Generator{ + epochFunc: time.Now, + hwAddrFunc: defaultHWAddrFunc, + rand: rand.Reader, + } +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func (g *rfc4122Generator) NewV1() (UUID, error) { + u := UUID{} + + timeNow, clockSeq, err := g.getClockSequence() + if err != nil { + return Nil, err + } + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + hardwareAddr, err := g.getHardwareAddr() + if err != nil { + return Nil, err + } + copy(u[10:], hardwareAddr) + + u.SetVersion(V1) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func (g *rfc4122Generator) NewV2(domain byte) (UUID, error) { + u, err := g.NewV1() + if err != nil { + return Nil, err + } + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[:], posixGID) + } + + u[9] = domain + + u.SetVersion(V2) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func (g *rfc4122Generator) NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(V3) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV4 returns random generated UUID. +func (g *rfc4122Generator) NewV4() (UUID, error) { + u := UUID{} + if _, err := io.ReadFull(g.rand, u[:]); err != nil { + return Nil, err + } + u.SetVersion(V4) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func (g *rfc4122Generator) NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(V5) + u.SetVariant(VariantRFC4122) + + return u +} + +// Returns epoch and clock sequence. +func (g *rfc4122Generator) getClockSequence() (uint64, uint16, error) { + var err error + g.clockSequenceOnce.Do(func() { + buf := make([]byte, 2) + if _, err = io.ReadFull(g.rand, buf); err != nil { + return + } + g.clockSequence = binary.BigEndian.Uint16(buf) + }) + if err != nil { + return 0, 0, err + } + + g.storageMutex.Lock() + defer g.storageMutex.Unlock() + + timeNow := g.getEpoch() + // Clock didn't change since last UUID generation. + // Should increase clock sequence. + if timeNow <= g.lastTime { + g.clockSequence++ + } + g.lastTime = timeNow + + return timeNow, g.clockSequence, nil +} + +// Returns hardware address. +func (g *rfc4122Generator) getHardwareAddr() ([]byte, error) { + var err error + g.hardwareAddrOnce.Do(func() { + if hwAddr, err := g.hwAddrFunc(); err == nil { + copy(g.hardwareAddr[:], hwAddr) + return + } + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence. + if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil { + return + } + // Set multicast bit as recommended by RFC 4122 + g.hardwareAddr[0] |= 0x01 + }) + if err != nil { + return []byte{}, err + } + return g.hardwareAddr[:], nil +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +func (g *rfc4122Generator) getEpoch() uint64 { + return epochStart + uint64(g.epochFunc().UnixNano()/100) +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} + +// Returns hardware address. +func defaultHWAddrFunc() (net.HardwareAddr, error) { + ifaces, err := net.Interfaces() + if err != nil { + return []byte{}, err + } + for _, iface := range ifaces { + if len(iface.HardwareAddr) >= 6 { + return iface.HardwareAddr, nil + } + } + return []byte{}, fmt.Errorf("uuid: no HW address found") +} diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go new file mode 100644 index 00000000..4e39f779 --- /dev/null +++ b/common/uuid/uuid.go @@ -0,0 +1,137 @@ +package uuid + +import ( + "bytes" + "encoding/hex" +) + +// Size of a UUID in bytes. +const Size = 16 + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [Size]byte + +// UUID versions +const ( + _ byte = iota + V1 + V2 + V3 + V4 + V5 +) + +// UUID layout variants. +const ( + VariantNCS byte = iota + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// UUID DCE domains. +const ( + DomainPerson = iota + DomainGroup + DomainOrg +) + +// String parse helpers. +var ( + urnPrefix = []byte("urn:uuid:") + byteGroups = []int{8, 4, 4, 4, 12} +) + +// Nil is special form of UUID that is specified to have all +// 128 bits set to zero. +var Nil = UUID{} + +// Predefined namespace UUIDs. +var ( + NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) +) + +// Equal returns true if u1 and u2 equals, otherwise returns false. +func Equal(u1 UUID, u2 UUID) bool { + return bytes.Equal(u1[:], u2[:]) +} + +// Version returns algorithm version used to generate UUID. +func (u UUID) Version() byte { + return u[6] >> 4 +} + +// Variant returns UUID layout variant. +func (u UUID) Variant() byte { + switch { + case (u[8] >> 7) == 0x00: + return VariantNCS + case (u[8] >> 6) == 0x02: + return VariantRFC4122 + case (u[8] >> 5) == 0x06: + return VariantMicrosoft + case (u[8] >> 5) == 0x07: + fallthrough + default: + return VariantFuture + } +} + +// Bytes returns bytes slice representation of UUID. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Returns canonical string representation of UUID: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = '-' + hex.Encode(buf[9:13], u[4:6]) + buf[13] = '-' + hex.Encode(buf[14:18], u[6:8]) + buf[18] = '-' + hex.Encode(buf[19:23], u[8:10]) + buf[23] = '-' + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} + +// SetVersion sets version bits. +func (u *UUID) SetVersion(v byte) { + u[6] = (u[6] & 0x0f) | (v << 4) +} + +// SetVariant sets variant bits. +func (u *UUID) SetVariant(v byte) { + switch v { + case VariantNCS: + u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) + case VariantRFC4122: + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + case VariantMicrosoft: + u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) + case VariantFuture: + fallthrough + default: + u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) + } +} + +// Must is a helper that wraps a call to a function returning (UUID, error) +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")); +func Must(u UUID, err error) UUID { + if err != nil { + panic(err) + } + return u +} diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index fa236139..0ee92451 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -2,6 +2,8 @@ package e2e import ( "fmt" + "github.com/binance-chain/go-sdk/common/crypto" + "github.com/binance-chain/go-sdk/common/crypto/secp256k1" "testing" time2 "time" @@ -10,8 +12,6 @@ import ( sdk "github.com/binance-chain/go-sdk/client" "github.com/binance-chain/go-sdk/client/query" "github.com/binance-chain/go-sdk/common" - "github.com/binance-chain/go-sdk/common/crypto" - "github.com/binance-chain/go-sdk/common/crypto/secp256k1" "github.com/binance-chain/go-sdk/keys" "github.com/binance-chain/go-sdk/types" "github.com/binance-chain/go-sdk/types/msg" diff --git a/keys/keys.go b/keys/keys.go index e4b7e8d8..44b21a35 100644 --- a/keys/keys.go +++ b/keys/keys.go @@ -1,17 +1,22 @@ package keys import ( + "crypto/sha256" "encoding/hex" "encoding/json" "fmt" - "github.com/binance-chain/go-sdk/types" + "io/ioutil" "strings" "github.com/cosmos/go-bip39" + "golang.org/x/crypto/pbkdf2" + "github.com/binance-chain/go-sdk/common" "github.com/binance-chain/go-sdk/common/crypto" "github.com/binance-chain/go-sdk/common/crypto/secp256k1" + "github.com/binance-chain/go-sdk/common/uuid" + "github.com/binance-chain/go-sdk/types" "github.com/binance-chain/go-sdk/types/tx" ) @@ -19,10 +24,20 @@ const ( defaultBIP39Passphrase = "" ) +const ( + Save = iota + DomainGroup + DomainOrg +) + type KeyManager interface { Sign(tx.StdSignMsg) ([]byte, error) GetPrivKey() crypto.PrivKey GetAddr() types.AccAddress + + ExportAsMnemonic() (string, error) + ExportAsPrivateKey() (string, error) + ExportAsKeyStore(password string) (*EncryptedKeyJSONV1, error) } func NewMnemonicKeyManager(mnemonic string) (KeyManager, error) { @@ -37,16 +52,47 @@ func NewKeyStoreKeyManager(file string, auth string) (KeyManager, error) { return &k, err } -func NewPrivateKeyManager(wifKey string) (KeyManager, error) { +func NewPrivateKeyManager(priKey string) (KeyManager, error) { k := keyManager{} - err := k.recoveryFromPrivateKey(wifKey) + err := k.recoveryFromPrivateKey(priKey) return &k, err } type keyManager struct { - recoverType string - privKey crypto.PrivKey - addr types.AccAddress + privKey crypto.PrivKey + addr types.AccAddress + mnemonic string +} + +func (m *keyManager) ExportAsMnemonic() (string, error) { + if m.mnemonic == "" { + return "", fmt.Errorf("This key manager is not recover from mnemonic or anto generated ") + } + return m.mnemonic, nil +} + +func (m *keyManager) ExportAsPrivateKey() (string, error) { + secpPrivateKey, ok := m.privKey.(secp256k1.PrivKeySecp256k1) + if !ok { + return "", fmt.Errorf(" Only PrivKeySecp256k1 key is supported ") + } + return hex.EncodeToString(secpPrivateKey[:]), nil +} + +func (m *keyManager) ExportAsKeyStore(password string) (*EncryptedKeyJSONV1, error) { + return generateKeyStore(m.GetPrivKey(), password) +} + +func NewRawKeyManager() (KeyManager, error) { + entropy, err := bip39.NewEntropy(256) + if err != nil { + return nil, err + } + mnemonic, err := bip39.NewMnemonic(entropy) + if err != nil { + return nil, err + } + return NewMnemonicKeyManager(mnemonic) } func (m *keyManager) recoveryFromKMnemonic(mnemonic string) error { @@ -71,6 +117,7 @@ func (m *keyManager) recoveryFromKMnemonic(mnemonic string) error { } m.addr = addr m.privKey = priKey + m.mnemonic = mnemonic return nil } @@ -158,3 +205,56 @@ func (m *keyManager) makeSignature(msg tx.StdSignMsg) (sig tx.StdSignature, err Signature: sigBytes, }, nil } + +func generateKeyStore(privateKey crypto.PrivKey, password string) (*EncryptedKeyJSONV1, error) { + addr := types.AccAddress(privateKey.PubKey().Address()) + salt, err := common.GenerateRandomBytes(32) + if err != nil { + return nil, err + } + iv, err := common.GenerateRandomBytes(16) + if err != nil { + return nil, err + } + scryptParamsJSON := make(map[string]interface{}, 4) + scryptParamsJSON["prf"] = "hmac-sha256" + scryptParamsJSON["dklen"] = 32 + scryptParamsJSON["salt"] = hex.EncodeToString(salt) + scryptParamsJSON["c"] = 262144 + + cipherParamsJSON := cipherparamsJSON{IV: hex.EncodeToString(iv)} + derivedKey := pbkdf2.Key([]byte(password), salt, 262144, 32, sha256.New) + encryptKey := derivedKey[:16] + secpPrivateKey, ok := privateKey.(secp256k1.PrivKeySecp256k1) + if !ok { + return nil, fmt.Errorf(" Only PrivKeySecp256k1 key is supported ") + } + cipherText, err := aesCTRXOR(encryptKey, secpPrivateKey[:], iv) + if err != nil { + return nil, err + } + + hasher := sha256.New() + hasher.Write(derivedKey[16:32]) + hasher.Write(cipherText) + mac := hasher.Sum(nil) + + id, err := uuid.NewV4() + if err != nil { + return nil, err + } + cryptoStruct := CryptoJSON{ + Cipher: "aes-256-ctr", + CipherText: hex.EncodeToString(cipherText), + CipherParams: cipherParamsJSON, + KDF: "pbkdf2", + KDFParams: scryptParamsJSON, + MAC: hex.EncodeToString(mac), + } + return &EncryptedKeyJSONV1{ + Address: addr.String(), + Crypto: cryptoStruct, + Id: id.String(), + Version: "1", + }, nil +} diff --git a/keys/keys_test.go b/keys/keys_test.go index 10759183..c43eef12 100644 --- a/keys/keys_test.go +++ b/keys/keys_test.go @@ -2,6 +2,9 @@ package keys import ( "bytes" + "encoding/json" + "io/ioutil" + "os" "testing" "github.com/binance-chain/go-sdk/types" @@ -169,3 +172,64 @@ func TestSignTxNoError(t *testing.T) { assert.True(t, bytes.Equal(signResult, []byte(expectHexTx)), c.errMsg) } } + +func TestExportAsKeyStoreNoError(t *testing.T) { + defer os.Remove("TestGenerateKeyStoreNoError.json") + km, err := NewRawKeyManager() + assert.NoError(t, err) + encryPlain1, err := km.GetPrivKey().Sign([]byte("test plain")) + assert.NoError(t, err) + keyJSONV1, err := km.ExportAsKeyStore("testpassword") + assert.NoError(t, err) + bz, err := json.Marshal(keyJSONV1) + assert.NoError(t, err) + err = ioutil.WriteFile("TestGenerateKeyStoreNoError.json", bz, 0660) + assert.NoError(t, err) + newkm, err := NewKeyStoreKeyManager("TestGenerateKeyStoreNoError.json", "testpassword") + assert.NoError(t, err) + encryPlain2, err := newkm.GetPrivKey().Sign([]byte("test plain")) + assert.NoError(t, err) + assert.True(t, bytes.Equal(encryPlain1, encryPlain2)) +} + +func TestExportAsMnemonicNoError(t *testing.T) { + km, err := NewRawKeyManager() + assert.NoError(t, err) + encryPlain1, err := km.GetPrivKey().Sign([]byte("test plain")) + assert.NoError(t, err) + mnemonic, err := km.ExportAsMnemonic() + assert.NoError(t, err) + newkm, err := NewMnemonicKeyManager(mnemonic) + assert.NoError(t, err) + encryPlain2, err := newkm.GetPrivKey().Sign([]byte("test plain")) + assert.NoError(t, err) + assert.True(t, bytes.Equal(encryPlain1, encryPlain2)) + _, err = newkm.ExportAsMnemonic() + assert.NoError(t, err) +} + +func TestExportAsPrivateKeyNoError(t *testing.T) { + km, err := NewRawKeyManager() + assert.NoError(t, err) + encryPlain1, err := km.GetPrivKey().Sign([]byte("test plain")) + assert.NoError(t, err) + pk, err := km.ExportAsPrivateKey() + assert.NoError(t, err) + newkm, err := NewPrivateKeyManager(pk) + assert.NoError(t, err) + encryPlain2, err := newkm.GetPrivKey().Sign([]byte("test plain")) + assert.NoError(t, err) + assert.True(t, bytes.Equal(encryPlain1, encryPlain2)) +} + +func TestExportAsMnemonicyError(t *testing.T) { + km, err := NewPrivateKeyManager("9579fff0cab07a4379e845a890105004ba4c8276f8ad9d22082b2acbf02d884b") + assert.NoError(t, err) + _, err = km.ExportAsMnemonic() + assert.Error(t, err) + file := "testkeystore.json" + km, err = NewKeyStoreKeyManager(file, "Zjubfd@123") + assert.NoError(t, err) + _, err = km.ExportAsMnemonic() + assert.Error(t, err) +}