// Copyright Fuzamei Corp. 2018 All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package wallet import ( "bytes" "crypto/aes" "crypto/cipher" "github.com/33cn/chain33/wallet/bipwallet" // "crypto/rand" "encoding/hex" "encoding/json" "fmt" // "math/big" "strings" log "github.com/33cn/chain33/common/log/log15" sccrypto "github.com/NebulousLabs/Sia/crypto" "github.com/NebulousLabs/Sia/modules" // "github.com/piotrnar/gocoin/lib/btc" "github.com/33cn/chain33/common" dbm "github.com/33cn/chain33/common/db" "github.com/33cn/chain33/types" ) var ( // SeedLong 随机种子的长度 SeedLong = 15 // SaveSeedLong 保存的随机种子个数 SaveSeedLong = 12 // WalletSeed 钱包种子前缀 WalletSeed = []byte("walletseed") seedlog = log.New("module", "wallet") // ChineseSeedCache 中文种子缓存映射 ChineseSeedCache = make(map[string]string) // EnglishSeedCache 英文种子缓存映射 EnglishSeedCache = make(map[string]string) ) // BACKUPKEYINDEX 备份索引Key值 const BACKUPKEYINDEX = "backupkeyindex" // CreateSeed 通过指定语言类型生成seed种子,传入语言类型以及 //lang = 0 通过英语单词生成种子 //lang = 1 通过中文生成种子 //bitsize=128 返回12个单词或者汉子,bitsize+32=160 返回15个单词或者汉子,bitszie=256 返回24个单词或者汉子 func CreateSeed(folderpath string, lang int32) (string, error) { mnem, err := bipwallet.NewMnemonicString(int(lang), 160) if err != nil { seedlog.Error("CreateSeed", "NewMnemonicString err", err) return "", err } return mnem, nil } // InitSeedLibrary 初始化seed标准库的单词到map中,方便seed单词的校验 func InitSeedLibrary() { //首先将标准seed库转换成字符串数组 englieshstrs := strings.Split(englishText, " ") chinesestrs := strings.Split(chineseText, " ") //中引文标准seed库保存到map中 for _, wordstr := range chinesestrs { ChineseSeedCache[wordstr] = wordstr } for _, wordstr := range englieshstrs { EnglishSeedCache[wordstr] = wordstr } } // VerifySeed 校验输入的seed字符串数是否合法,通过助记词能否生成钱包来判断合法性 func VerifySeed(seed string) (bool, error) { _, err := bipwallet.NewWalletFromMnemonic(bipwallet.TypeBty, seed) if err != nil { seedlog.Error("VerifySeed NewWalletFromMnemonic", "err", err) return false, err } return true, nil } // SaveSeed 使用password加密seed存储到db中 func SaveSeed(db dbm.DB, seed string, password string) (bool, error) { if len(seed) == 0 || len(password) == 0 { return false, types.ErrInvalidParam } Encrypted, err := AesgcmEncrypter([]byte(password), []byte(seed)) if err != nil { seedlog.Error("SaveSeed", "AesgcmEncrypter err", err) return false, err } err = db.SetSync(WalletSeed, Encrypted) if err != nil { return false, err } return true, nil } // SaveSeedInBatch 保存种子数据到数据库 func SaveSeedInBatch(db dbm.DB, seed string, password string, batch dbm.Batch) (bool, error) { if len(seed) == 0 || len(password) == 0 { return false, types.ErrInvalidParam } Encrypted, err := AesgcmEncrypter([]byte(password), []byte(seed)) if err != nil { seedlog.Error("SaveSeed", "AesgcmEncrypter err", err) return false, err } batch.Set(WalletSeed, Encrypted) //seedlog.Info("SaveSeed ok", "Encryptedseed", Encryptedseed) return true, nil } //GetSeed 使用password解密seed上报给上层 func GetSeed(db dbm.DB, password string) (string, error) { if len(password) == 0 { return "", types.ErrInvalidParam } Encryptedseed, err := db.Get(WalletSeed) if err != nil { return "", err } if len(Encryptedseed) == 0 { return "", types.ErrSeedNotExist } seed, err := AesgcmDecrypter([]byte(password), Encryptedseed) if err != nil { return "", err } return string(seed), nil } //GetPrivkeyBySeed 通过seed生成子私钥十六进制字符串 func GetPrivkeyBySeed(db dbm.DB, seed string) (string, error) { var backupindex uint32 var Hexsubprivkey string var err error var index uint32 //通过主私钥随机生成child私钥十六进制字符串 backuppubkeyindex, err := db.Get([]byte(BACKUPKEYINDEX)) if backuppubkeyindex == nil || err != nil { index = 0 } else { if err = json.Unmarshal(backuppubkeyindex, &backupindex); err != nil { return "", err } index = backupindex + 1 } //secp256k1 if SignType == 1 { wallet, err := bipwallet.NewWalletFromMnemonic(bipwallet.TypeBty, seed) if err != nil { seedlog.Error("GetPrivkeyBySeed NewWalletFromMnemonic", "err", err) wallet, err = bipwallet.NewWalletFromSeed(bipwallet.TypeBty, []byte(seed)) if err != nil { seedlog.Error("GetPrivkeyBySeed NewWalletFromSeed", "err", err) return "", types.ErrNewWalletFromSeed } } //通过索引生成Key pair priv, pub, err := wallet.NewKeyPair(index) if err != nil { seedlog.Error("GetPrivkeyBySeed NewKeyPair", "err", err) return "", types.ErrNewKeyPair } Hexsubprivkey = hex.EncodeToString(priv) public, err := bipwallet.PrivkeyToPub(bipwallet.TypeBty, priv) if err != nil { seedlog.Error("GetPrivkeyBySeed PrivkeyToPub", "err", err) return "", types.ErrPrivkeyToPub } if !bytes.Equal(pub, public) { seedlog.Error("GetPrivkeyBySeed NewKeyPair pub != PrivkeyToPub", "err", err) return "", types.ErrSubPubKeyVerifyFail } } else if SignType == 2 { //ed25519 //通过助记词形式的seed生成私钥和公钥,一个seed根据不同的index可以生成许多组密钥 //字符串形式的助记词(英语单词)通过计算一次hash转成字节形式的seed var Seed modules.Seed hash := common.Sha256([]byte(seed)) copy(Seed[:], hash) sk, _ := sccrypto.GenerateKeyPairDeterministic(sccrypto.HashAll(Seed, index)) secretKey := fmt.Sprintf("%x", sk) //publicKey := fmt.Sprintf("%x", pk) //seedlog.Error("GetPrivkeyBySeed", "index", index, "secretKey", secretKey, "publicKey", publicKey) Hexsubprivkey = secretKey } else if SignType == 3 { //sm2 return "", types.ErrNotSupport } else { return "", types.ErrNotSupport } // back up index in db var pubkeyindex []byte pubkeyindex, err = json.Marshal(index) if err != nil { seedlog.Error("GetPrivkeyBySeed", "Marshal err ", err) return "", types.ErrMarshal } db.SetSync([]byte(BACKUPKEYINDEX), pubkeyindex) //seedlog.Info("GetPrivkeyBySeed", "Hexsubprivkey", Hexsubprivkey, "index", index) return Hexsubprivkey, nil } //AesgcmEncrypter 使用钱包的password对seed进行aesgcm加密,返回加密后的seed func AesgcmEncrypter(password []byte, seed []byte) ([]byte, error) { key := make([]byte, 32) if len(password) > 32 { key = password[0:32] } else { copy(key, password) } block, err := aes.NewCipher(key) if err != nil { seedlog.Error("AesgcmEncrypter NewCipher err", "err", err) return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { seedlog.Error("AesgcmEncrypter NewGCM err", "err", err) return nil, err } Encrypted := aesgcm.Seal(nil, key[:12], seed, nil) //seedlog.Info("AesgcmEncrypter Seal", "seed", seed, "key", key, "Encrypted", Encrypted) return Encrypted, nil } //AesgcmDecrypter 使用钱包的password对seed进行aesgcm解密,返回解密后的seed func AesgcmDecrypter(password []byte, seed []byte) ([]byte, error) { key := make([]byte, 32) if len(password) > 32 { key = password[0:32] } else { copy(key, password) } block, err := aes.NewCipher(key) if err != nil { seedlog.Error("AesgcmDecrypter", "NewCipher err", err) return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { seedlog.Error("AesgcmDecrypter", "NewGCM err", err) return nil, err } decryptered, err := aesgcm.Open(nil, key[:12], seed, nil) if err != nil { seedlog.Error("AesgcmDecrypter", "aesgcm Open err", err) return nil, err } //seedlog.Info("AesgcmDecrypter", "password", string(password), "seed", seed, "decryptered", string(decryptered)) return decryptered, nil }