github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/crypto/keys/keybase_base.go (about) 1 package keys 2 3 import ( 4 "bufio" 5 "fmt" 6 "github.com/mitchellh/go-homedir" 7 "os" 8 "strings" 9 10 "github.com/cosmos/go-bip39" 11 tmcrypto "github.com/fibonacci-chain/fbc/libs/tendermint/crypto" 12 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/secp256k1" 13 "github.com/pkg/errors" 14 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys/hd" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 18 ) 19 20 type ( 21 kbOptions struct { 22 keygenFunc PrivKeyGenFunc 23 deriveFunc DeriveKeyFunc 24 supportedAlgos []SigningAlgo 25 supportedAlgosLedger []SigningAlgo 26 } 27 28 // baseKeybase is an auxiliary type that groups Keybase storage agnostic features 29 // together. 30 baseKeybase struct { 31 options kbOptions 32 } 33 34 keyWriter interface { 35 writeLocalKeyer 36 infoWriter 37 } 38 39 writeLocalKeyer interface { 40 writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info 41 } 42 43 infoWriter interface { 44 writeInfo(name string, info Info) 45 } 46 ) 47 48 // WithKeygenFunc applies an overridden key generation function to generate the private key. 49 func WithKeygenFunc(f PrivKeyGenFunc) KeybaseOption { 50 return func(o *kbOptions) { 51 o.keygenFunc = f 52 } 53 } 54 55 // WithDeriveFunc applies an overridden key derivation function to generate the private key. 56 func WithDeriveFunc(f DeriveKeyFunc) KeybaseOption { 57 return func(o *kbOptions) { 58 o.deriveFunc = f 59 } 60 } 61 62 // WithSupportedAlgos defines the list of accepted SigningAlgos. 63 func WithSupportedAlgos(algos []SigningAlgo) KeybaseOption { 64 return func(o *kbOptions) { 65 o.supportedAlgos = algos 66 } 67 } 68 69 // WithSupportedAlgosLedger defines the list of accepted SigningAlgos compatible with Ledger. 70 func WithSupportedAlgosLedger(algos []SigningAlgo) KeybaseOption { 71 return func(o *kbOptions) { 72 o.supportedAlgosLedger = algos 73 } 74 } 75 76 // newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type 77 func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase { 78 // Default options for keybase 79 options := kbOptions{ 80 keygenFunc: StdPrivKeyGen, 81 deriveFunc: StdDeriveKey, 82 supportedAlgos: []SigningAlgo{Secp256k1}, 83 supportedAlgosLedger: []SigningAlgo{Secp256k1}, 84 } 85 86 for _, optionFn := range optionsFns { 87 optionFn(&options) 88 } 89 90 return baseKeybase{options: options} 91 } 92 93 // StdPrivKeyGen is the default PrivKeyGen function in the keybase. 94 // For now, it only supports Secp256k1 95 func StdPrivKeyGen(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) { 96 if algo == Secp256k1 { 97 return SecpPrivKeyGen(bz), nil 98 } 99 return nil, ErrUnsupportedSigningAlgo 100 } 101 102 // SecpPrivKeyGen generates a secp256k1 private key from the given bytes 103 func SecpPrivKeyGen(bz []byte) tmcrypto.PrivKey { 104 var bzArr [32]byte 105 copy(bzArr[:], bz) 106 return secp256k1.PrivKeySecp256k1(bzArr) 107 } 108 109 // SignWithLedger signs a binary message with the ledger device referenced by an Info object 110 // and returns the signed bytes and the public key. It returns an error if the device could 111 // not be queried or it returned an error. 112 func (kb baseKeybase) SignWithLedger(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { 113 i := info.(ledgerInfo) 114 priv, err := crypto.NewPrivKeyLedgerSecp256k1Unsafe(i.Path) 115 if err != nil { 116 return 117 } 118 119 sig, err = priv.Sign(msg) 120 if err != nil { 121 return nil, nil, err 122 } 123 124 return sig, priv.PubKey(), nil 125 } 126 127 // DecodeSignature decodes a an length-prefixed binary signature from standard input 128 // and return it as a byte slice. 129 func (kb baseKeybase) DecodeSignature(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { 130 _, err = fmt.Fprintf(os.Stderr, "Message to sign:\n\n%s\n", msg) 131 if err != nil { 132 return nil, nil, err 133 } 134 135 buf := bufio.NewReader(os.Stdin) 136 _, err = fmt.Fprintf(os.Stderr, "\nEnter Amino-encoded signature:\n") 137 if err != nil { 138 return nil, nil, err 139 } 140 141 // will block until user inputs the signature 142 signed, err := buf.ReadString('\n') 143 if err != nil { 144 return nil, nil, err 145 } 146 147 if err := CryptoCdc.UnmarshalBinaryLengthPrefixed([]byte(signed), sig); err != nil { 148 return nil, nil, errors.Wrap(err, "failed to decode signature") 149 } 150 151 return sig, info.GetPubKey(), nil 152 } 153 154 // CreateAccount creates an account Info object. 155 func (kb baseKeybase) CreateAccount( 156 keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd, hdPath string, algo SigningAlgo, 157 ) (Info, error) { 158 159 var derivedPriv []byte 160 var err error 161 162 // create master key and derive first key for keyring 163 if !strings.Contains(mnemonic, " ") { 164 derivedPriv, err = deriveKeyByPrivKey(mnemonic, algo) 165 } else { 166 derivedPriv, err = kb.options.deriveFunc(mnemonic, bip39Passphrase, hdPath, algo) 167 } 168 169 if err != nil { 170 return nil, err 171 } 172 173 privKey, err := kb.options.keygenFunc(derivedPriv, algo) 174 if err != nil { 175 return nil, err 176 } 177 178 var info Info 179 180 if encryptPasswd != "" { 181 info = keyWriter.writeLocalKey(name, privKey, encryptPasswd, algo) 182 } else { 183 info = kb.writeOfflineKey(keyWriter, name, privKey.PubKey(), algo) 184 } 185 186 return info, nil 187 } 188 189 // CreateLedger creates a new reference to a Ledger key pair. It returns a public 190 // key and a derivation path. It returns an error if the device could not be queried. 191 func (kb baseKeybase) CreateLedger( 192 w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32, 193 ) (Info, error) { 194 195 if !IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) { 196 return nil, ErrUnsupportedSigningAlgo 197 } 198 199 coinType := types.GetConfig().GetCoinType() 200 hdPath := hd.NewFundraiserParams(account, coinType, index) 201 202 priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp) 203 if err != nil { 204 return nil, err 205 } 206 207 return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath, algo), nil 208 } 209 210 // CreateMnemonic generates a new key with the given algorithm and language pair. 211 func (kb baseKeybase) CreateMnemonic( 212 keyWriter keyWriter, name string, language Language, passwd string, algo SigningAlgo, mnemonicInput string, 213 ) (info Info, mnemonic string, err error) { 214 215 if language != English { 216 return nil, "", ErrUnsupportedLanguage 217 } 218 219 if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) { 220 return nil, "", ErrUnsupportedSigningAlgo 221 } 222 223 // Default number of words (24): This generates a mnemonic directly from the 224 // number of words by reading system entropy. 225 entropy, err := bip39.NewEntropy(defaultEntropySize) 226 if err != nil { 227 return nil, "", err 228 } 229 230 mnemonic, err = bip39.NewMnemonic(entropy) 231 if err != nil { 232 return nil, "", err 233 } 234 235 if len(mnemonicInput) > 0 { 236 mnemonic = mnemonicInput 237 } 238 239 info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, types.GetConfig().GetFullFundraiserPath(), algo) 240 if err != nil { 241 return nil, "", err 242 } 243 244 return info, mnemonic, err 245 } 246 247 // CreateMnemonicWithHDPath generates a new key with the given algorithm and language pair. 248 func (kb baseKeybase) CreateMnemonicWithHDPath( 249 keyWriter keyWriter, name string, language Language, passwd string, algo SigningAlgo, mnemonicInput string, hdPath string, 250 ) (info Info, mnemonic string, err error) { 251 252 if language != English { 253 return nil, "", ErrUnsupportedLanguage 254 } 255 256 if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) { 257 return nil, "", ErrUnsupportedSigningAlgo 258 } 259 260 // Default number of words (24): This generates a mnemonic directly from the 261 // number of words by reading system entropy. 262 entropy, err := bip39.NewEntropy(defaultEntropySize) 263 if err != nil { 264 return nil, "", err 265 } 266 267 mnemonic, err = bip39.NewMnemonic(entropy) 268 if err != nil { 269 return nil, "", err 270 } 271 272 if len(mnemonicInput) > 0 { 273 mnemonic = mnemonicInput 274 } 275 276 info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, hdPath, algo) 277 if err != nil { 278 return nil, "", err 279 } 280 281 return info, mnemonic, err 282 } 283 284 func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info { 285 info := newLedgerInfo(name, pub, path, algo) 286 w.writeInfo(name, info) 287 return info 288 } 289 290 func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey, algo SigningAlgo) Info { 291 info := newOfflineInfo(name, pub, algo) 292 w.writeInfo(name, info) 293 return info 294 } 295 296 func (kb baseKeybase) writeMultisigKey(w infoWriter, name string, pub tmcrypto.PubKey) Info { 297 info := NewMultiInfo(name, pub) 298 w.writeInfo(name, info) 299 return info 300 } 301 302 // StdDeriveKey is the default DeriveKey function in the keybase. 303 // For now, it only supports Secp256k1 304 func StdDeriveKey(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) { 305 if algo == Secp256k1 { 306 return SecpDeriveKey(mnemonic, bip39Passphrase, hdPath) 307 } 308 return nil, ErrUnsupportedSigningAlgo 309 } 310 311 // SecpDeriveKey derives and returns the secp256k1 private key for the given seed and HD path. 312 func SecpDeriveKey(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) { 313 seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase) 314 if err != nil { 315 return nil, err 316 } 317 318 masterPriv, ch := hd.ComputeMastersFromSeed(seed) 319 if len(hdPath) == 0 { 320 return masterPriv[:], nil 321 } 322 derivedKey, err := hd.DerivePrivateKeyForPath(masterPriv, ch, hdPath) 323 return derivedKey[:], err 324 } 325 326 // CreateHDPath returns BIP 44 object from account and index parameters. 327 func CreateHDPath(account uint32, index uint32) *hd.BIP44Params { 328 return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index) 329 } 330 331 // CreateHDPath returns BIP 44 object from account and index parameters. 332 func CreateHDPathEx(cointype, account, index uint32) *hd.BIP44Params { 333 return hd.NewFundraiserParams(account, cointype, index) 334 } 335 336 // SupportedAlgos returns a list of supported signing algorithms. 337 func (kb baseKeybase) SupportedAlgos() []SigningAlgo { 338 return kb.options.supportedAlgos 339 } 340 341 // SupportedAlgosLedger returns a list of supported ledger signing algorithms. 342 func (kb baseKeybase) SupportedAlgosLedger() []SigningAlgo { 343 return kb.options.supportedAlgosLedger 344 } 345 346 // IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms. 347 func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool { 348 for _, supportedAlgo := range supported { 349 if algo == supportedAlgo { 350 return true 351 } 352 } 353 return false 354 } 355 356 // resolvePath resolve to a absolute path 357 func resolvePath(path string) (string, error) { 358 var err error 359 // expand tilde for home directory 360 if strings.HasPrefix(path, "~") { 361 home, err := homedir.Dir() 362 if err != nil { 363 return "", err 364 } 365 path = strings.Replace(path, "~", home, 1) 366 } 367 368 stat, err := os.Stat(path) 369 if os.IsNotExist(err) { 370 err = os.MkdirAll(path, 0700) 371 } else if err != nil && !stat.IsDir() { 372 err = fmt.Errorf("%s is a file, not a directory", path) 373 } 374 return path, err 375 }