github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/keybase.go (about) 1 package keys 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/gnolang/gno/tm2/pkg/crypto" 9 "github.com/gnolang/gno/tm2/pkg/crypto/bip39" 10 "github.com/gnolang/gno/tm2/pkg/crypto/hd" 11 "github.com/gnolang/gno/tm2/pkg/crypto/keys/armor" 12 "github.com/gnolang/gno/tm2/pkg/crypto/keys/keyerror" 13 "github.com/gnolang/gno/tm2/pkg/crypto/ledger" 14 "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" 15 dbm "github.com/gnolang/gno/tm2/pkg/db" 16 "github.com/gnolang/gno/tm2/pkg/db/memdb" 17 "github.com/gnolang/gno/tm2/pkg/errors" 18 ) 19 20 var _ Keybase = dbKeybase{} 21 22 // Language is a language to create the BIP 39 mnemonic in. 23 // Currently, only english is supported though. 24 // Find a list of all supported languages in the BIP 39 spec (word lists). 25 type Language int 26 27 // noinspection ALL 28 const ( 29 // English is the default language to create a mnemonic. 30 // It is the only supported language by this package. 31 English Language = iota + 1 32 // Japanese is currently not supported. 33 Japanese 34 // Korean is currently not supported. 35 Korean 36 // Spanish is currently not supported. 37 Spanish 38 // ChineseSimplified is currently not supported. 39 ChineseSimplified 40 // ChineseTraditional is currently not supported. 41 ChineseTraditional 42 // French is currently not supported. 43 French 44 // Italian is currently not supported. 45 Italian 46 ) 47 48 const ( 49 addressSuffix = "address" 50 infoSuffix = "info" 51 ) 52 53 var ( 54 // ErrUnsupportedSigningAlgo is raised when the caller tries to use a 55 // different signing scheme than secp256k1. 56 ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo: only secp256k1 is supported") 57 58 // ErrUnsupportedLanguage is raised when the caller tries to use a 59 // different language than english for creating a mnemonic sentence. 60 ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported") 61 ) 62 63 // dbKeybase combines encryption and storage implementation to provide 64 // a full-featured key manager 65 type dbKeybase struct { 66 db dbm.DB 67 } 68 69 // NewDBKeybase creates a new keybase instance using the passed DB for reading and writing keys. 70 func NewDBKeybase(db dbm.DB) Keybase { 71 return dbKeybase{ 72 db: db, 73 } 74 } 75 76 // NewInMemory creates a transient keybase on top of in-memory storage 77 // instance useful for testing purposes and on-the-fly key generation. 78 func NewInMemory() Keybase { return dbKeybase{memdb.NewMemDB()} } 79 80 // CreateAccount converts a mnemonic to a private key and persists it, encrypted with the given password. 81 // XXX Info could include the separately derived ed25519 key, 82 // XXX and a signature from the sec2561key as certificate. 83 // XXX NOTE: we are not saving the derivation path. 84 // XXX but this doesn't help encrypted communication. 85 // XXX also there is no document structure. 86 func (kb dbKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) { 87 coinType := crypto.CoinType 88 hdPath := hd.NewFundraiserParams(account, coinType, index) 89 return kb.CreateAccountBip44(name, mnemonic, bip39Passwd, encryptPasswd, *hdPath) 90 } 91 92 func (kb dbKeybase) CreateAccountBip44(name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params) (info Info, err error) { 93 seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase) 94 if err != nil { 95 return 96 } 97 98 info, err = kb.persistDerivedKey(seed, encryptPasswd, name, params.String()) 99 return 100 } 101 102 // CreateLedger creates a new locally-stored reference to a Ledger keypair 103 // It returns the created key info and an error if the Ledger could not be queried 104 func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (Info, error) { 105 if algo != Secp256k1 { 106 return nil, ErrUnsupportedSigningAlgo 107 } 108 109 coinType := crypto.CoinType 110 hdPath := hd.NewFundraiserParams(account, coinType, index) 111 priv, _, err := ledger.NewPrivKeyLedgerSecp256k1(*hdPath, hrp) 112 if err != nil { 113 return nil, err 114 } 115 pub := priv.PubKey() 116 117 // Note: Once Cosmos App v1.3.1 is compulsory, it could be possible to check that pubkey and addr match 118 return kb.writeLedgerKey(name, pub, *hdPath), nil 119 } 120 121 // CreateOffline creates a new reference to an offline keypair. It returns the 122 // created key info. 123 func (kb dbKeybase) CreateOffline(name string, pub crypto.PubKey) (Info, error) { 124 return kb.writeOfflineKey(name, pub), nil 125 } 126 127 // CreateMulti creates a new reference to a multisig (offline) keypair. It 128 // returns the created key info. 129 func (kb dbKeybase) CreateMulti(name string, pub crypto.PubKey) (Info, error) { 130 return kb.writeMultisigKey(name, pub), nil 131 } 132 133 func (kb *dbKeybase) persistDerivedKey(seed []byte, passwd, name, fullHdPath string) (info Info, err error) { 134 // create master key and derive first key: 135 masterPriv, ch := hd.ComputeMastersFromSeed(seed) 136 derivedPriv, err := hd.DerivePrivateKeyForPath(masterPriv, ch, fullHdPath) 137 if err != nil { 138 return 139 } 140 141 // use possibly blank password to encrypt the private 142 // key and store it. User must enforce good passwords. 143 info = kb.writeLocalKey(name, secp256k1.PrivKeySecp256k1(derivedPriv), passwd) 144 return 145 } 146 147 // List returns the keys from storage in alphabetical order. 148 func (kb dbKeybase) List() ([]Info, error) { 149 var res []Info 150 iter := kb.db.Iterator(nil, nil) 151 defer iter.Close() 152 for ; iter.Valid(); iter.Next() { 153 key := string(iter.Key()) 154 155 // need to include only keys in storage that have an info suffix 156 if strings.HasSuffix(key, infoSuffix) { 157 info, err := readInfo(iter.Value()) 158 if err != nil { 159 return nil, err 160 } 161 res = append(res, info) 162 } 163 } 164 return res, nil 165 } 166 167 // HasByNameOrAddress checks if a key with the name or bech32 string address is in the keybase. 168 func (kb dbKeybase) HasByNameOrAddress(nameOrBech32 string) (bool, error) { 169 address, err := crypto.AddressFromBech32(nameOrBech32) 170 if err != nil { 171 return kb.HasByName(nameOrBech32) 172 } 173 return kb.HasByAddress(address) 174 } 175 176 // HasByName checks if a key with the name is in the keybase. 177 func (kb dbKeybase) HasByName(name string) (bool, error) { 178 return kb.db.Has(infoKey(name)), nil 179 } 180 181 // HasByAddress checks if a key with the address is in the keybase. 182 func (kb dbKeybase) HasByAddress(address crypto.Address) (bool, error) { 183 return kb.db.Has(addrKey(address)), nil 184 } 185 186 // Get returns the public information about one key. 187 func (kb dbKeybase) GetByNameOrAddress(nameOrBech32 string) (Info, error) { 188 addr, err := crypto.AddressFromBech32(nameOrBech32) 189 if err != nil { 190 return kb.GetByName(nameOrBech32) 191 } 192 return kb.GetByAddress(addr) 193 } 194 195 func (kb dbKeybase) GetByName(name string) (Info, error) { 196 bs := kb.db.Get(infoKey(name)) 197 if len(bs) == 0 { 198 return nil, keyerror.NewErrKeyNotFound(name) 199 } 200 return readInfo(bs) 201 } 202 203 func (kb dbKeybase) GetByAddress(address crypto.Address) (Info, error) { 204 ik := kb.db.Get(addrKey(address)) 205 if len(ik) == 0 { 206 return nil, keyerror.NewErrKeyNotFound(fmt.Sprintf("key with address %s not found", address)) 207 } 208 bs := kb.db.Get(ik) 209 return readInfo(bs) 210 } 211 212 // Sign signs the msg with the named key. 213 // It returns an error if the key doesn't exist or the decryption fails. 214 func (kb dbKeybase) Sign(nameOrBech32, passphrase string, msg []byte) (sig []byte, pub crypto.PubKey, err error) { 215 info, err := kb.GetByNameOrAddress(nameOrBech32) 216 if err != nil { 217 return 218 } 219 220 var priv crypto.PrivKey 221 222 switch info.(type) { 223 case localInfo: 224 linfo := info.(localInfo) 225 if linfo.PrivKeyArmor == "" { 226 err = fmt.Errorf("private key not available") 227 return 228 } 229 230 priv, err = armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase) 231 if err != nil { 232 return nil, nil, err 233 } 234 235 case ledgerInfo: 236 linfo := info.(ledgerInfo) 237 priv, err = ledger.NewPrivKeyLedgerSecp256k1Unsafe(linfo.Path) 238 if err != nil { 239 return 240 } 241 242 case offlineInfo, multiInfo: 243 err = fmt.Errorf("cannot sign with key or addr %s", nameOrBech32) 244 return 245 } 246 247 sig, err = priv.Sign(msg) 248 if err != nil { 249 return nil, nil, err 250 } 251 252 pub = priv.PubKey() 253 return sig, pub, nil 254 } 255 256 // Verify verifies the sig+msg with the named key. 257 // It returns an error if the key doesn't exist or verification fails. 258 func (kb dbKeybase) Verify(nameOrBech32 string, msg []byte, sig []byte) (err error) { 259 info, err := kb.GetByNameOrAddress(nameOrBech32) 260 if err != nil { 261 return 262 } 263 264 var pub crypto.PubKey 265 pub = info.GetPubKey() 266 if !pub.VerifyBytes(msg, sig) { 267 return errors.New("invalid signature") 268 } 269 return nil 270 } 271 272 func (kb dbKeybase) ExportPrivateKeyObject(nameOrBech32 string, passphrase string) (crypto.PrivKey, error) { 273 info, err := kb.GetByNameOrAddress(nameOrBech32) 274 if err != nil { 275 return nil, err 276 } 277 278 var priv crypto.PrivKey 279 280 switch info.(type) { 281 case localInfo: 282 linfo := info.(localInfo) 283 if linfo.PrivKeyArmor == "" { 284 err = fmt.Errorf("private key not available") 285 return nil, err 286 } 287 priv, err = armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase) 288 if err != nil { 289 return nil, err 290 } 291 292 case ledgerInfo, offlineInfo, multiInfo: 293 return nil, errors.New("only works on local private keys") 294 } 295 296 return priv, nil 297 } 298 299 func (kb dbKeybase) Export(nameOrBech32 string) (astr string, err error) { 300 info, err := kb.GetByNameOrAddress(nameOrBech32) 301 if err != nil { 302 return "", errors.Wrap(err, "getting info for name %s", nameOrBech32) 303 } 304 bz := kb.db.Get(infoKey(info.GetName())) 305 if bz == nil { 306 return "", fmt.Errorf("no key to export with name %s", nameOrBech32) 307 } 308 return armor.ArmorInfoBytes(bz), nil 309 } 310 311 // ExportPubKey returns public keys in ASCII armored format. 312 // Retrieve a Info object by its name and return the public key in 313 // a portable format. 314 func (kb dbKeybase) ExportPubKey(nameOrBech32 string) (astr string, err error) { 315 info, err := kb.GetByNameOrAddress(nameOrBech32) 316 if err != nil { 317 return "", errors.Wrap(err, "getting info for name %s", nameOrBech32) 318 } 319 return armor.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil 320 } 321 322 // ExportPrivKey returns a private key in ASCII armored format. 323 // It returns an error if the key does not exist or a wrong encryption passphrase is supplied. 324 func (kb dbKeybase) ExportPrivKey( 325 name, 326 decryptPassphrase, 327 encryptPassphrase string, 328 ) (astr string, err error) { 329 priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase) 330 if err != nil { 331 return "", err 332 } 333 334 return armor.EncryptArmorPrivKey(priv, encryptPassphrase), nil 335 } 336 337 // ExportPrivKeyUnsafe returns a private key in ASCII armored format. 338 // The returned armor is unencrypted. 339 // It returns an error if the key does not exist 340 func (kb dbKeybase) ExportPrivKeyUnsafe( 341 name, 342 decryptPassphrase string, 343 ) (string, error) { 344 priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase) 345 if err != nil { 346 return "", err 347 } 348 349 return armor.ArmorPrivateKey(priv), nil 350 } 351 352 // ImportPrivKey imports a private key in ASCII armor format. 353 // It returns an error if a key with the same name exists or a wrong encryption passphrase is 354 // supplied. 355 func (kb dbKeybase) ImportPrivKey( 356 name, 357 astr, 358 decryptPassphrase, 359 encryptPassphrase string, 360 ) error { 361 if _, err := kb.GetByNameOrAddress(name); err == nil { 362 return errors.New("Cannot overwrite key " + name) 363 } 364 privKey, err := armor.UnarmorDecryptPrivKey(astr, decryptPassphrase) 365 if err != nil { 366 return errors.Wrap(err, "couldn't import private key") 367 } 368 369 kb.writeLocalKey(name, privKey, encryptPassphrase) 370 return nil 371 } 372 373 func (kb dbKeybase) ImportPrivKeyUnsafe( 374 name, 375 armorStr, 376 encryptPassphrase string, 377 ) error { 378 if _, err := kb.GetByNameOrAddress(name); err == nil { 379 return fmt.Errorf("cannot overwrite key %s", name) 380 } 381 382 privKey, err := armor.UnarmorPrivateKey(armorStr) 383 if err != nil { 384 return errors.Wrap(err, "couldn't import private key") 385 } 386 387 kb.writeLocalKey(name, privKey, encryptPassphrase) 388 return nil 389 } 390 391 func (kb dbKeybase) Import(name, astr string) (err error) { 392 if _, err := kb.GetByNameOrAddress(name); err == nil { 393 return errors.New("Cannot overwrite key " + name) 394 } 395 infoBytes, err := armor.UnarmorInfoBytes(astr) 396 if err != nil { 397 return 398 } 399 kb.db.Set(infoKey(name), infoBytes) 400 return nil 401 } 402 403 // ImportPubKey imports ASCII-armored public keys. 404 // Store a new Info object holding a public key only, i.e. it will 405 // not be possible to sign with it as it lacks the secret key. 406 func (kb dbKeybase) ImportPubKey(name, astr string) (err error) { 407 if _, err := kb.GetByNameOrAddress(name); err == nil { 408 return errors.New("Cannot overwrite data for name " + name) 409 } 410 pubBytes, err := armor.UnarmorPubKeyBytes(astr) 411 if err != nil { 412 return 413 } 414 pubKey, err := crypto.PubKeyFromBytes(pubBytes) 415 if err != nil { 416 return 417 } 418 kb.writeOfflineKey(name, pubKey) 419 return 420 } 421 422 // Delete removes key forever, but we must present the 423 // proper passphrase before deleting it (for security). 424 // It returns an error if the key doesn't exist or 425 // passphrases don't match. 426 // Passphrase is ignored when deleting references to 427 // offline and Ledger / HW wallet keys. 428 func (kb dbKeybase) Delete(nameOrBech32, passphrase string, skipPass bool) error { 429 // verify we have the proper password before deleting 430 info, err := kb.GetByNameOrAddress(nameOrBech32) 431 if err != nil { 432 return err 433 } 434 if linfo, ok := info.(localInfo); ok && !skipPass { 435 if _, err = armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil { 436 return err 437 } 438 } 439 kb.db.DeleteSync(addrKey(info.GetAddress())) 440 kb.db.DeleteSync(infoKey(info.GetName())) 441 return nil 442 } 443 444 // Update changes the passphrase with which an already stored key is 445 // encrypted. 446 // 447 // oldpass must be the current passphrase used for encryption, 448 // getNewpass is a function to get the passphrase to permanently replace 449 // the current passphrase 450 func (kb dbKeybase) Update(nameOrBech32, oldpass string, getNewpass func() (string, error)) error { 451 info, err := kb.GetByNameOrAddress(nameOrBech32) 452 if err != nil { 453 return err 454 } 455 switch info.(type) { 456 case localInfo: 457 linfo := info.(localInfo) 458 key, err := armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass) 459 if err != nil { 460 return err 461 } 462 newpass, err := getNewpass() 463 if err != nil { 464 return err 465 } 466 kb.writeLocalKey(info.GetName(), key, newpass) 467 return nil 468 default: 469 return fmt.Errorf("locally stored key required. Received: %v", reflect.TypeOf(info).String()) 470 } 471 } 472 473 // CloseDB releases the lock and closes the storage backend. 474 func (kb dbKeybase) CloseDB() { 475 kb.db.Close() 476 } 477 478 func (kb dbKeybase) writeLocalKey(name string, priv crypto.PrivKey, passphrase string) Info { 479 // encrypt private key using passphrase 480 privArmor := armor.EncryptArmorPrivKey(priv, passphrase) 481 // make Info 482 pub := priv.PubKey() 483 info := newLocalInfo(name, pub, privArmor) 484 kb.writeInfo(name, info) 485 return info 486 } 487 488 func (kb dbKeybase) writeLedgerKey(name string, pub crypto.PubKey, path hd.BIP44Params) Info { 489 info := newLedgerInfo(name, pub, path) 490 kb.writeInfo(name, info) 491 return info 492 } 493 494 func (kb dbKeybase) writeOfflineKey(name string, pub crypto.PubKey) Info { 495 info := newOfflineInfo(name, pub) 496 kb.writeInfo(name, info) 497 return info 498 } 499 500 func (kb dbKeybase) writeMultisigKey(name string, pub crypto.PubKey) Info { 501 info := NewMultiInfo(name, pub) 502 kb.writeInfo(name, info) 503 return info 504 } 505 506 func (kb dbKeybase) writeInfo(name string, info Info) { 507 // write the info by key 508 key := infoKey(name) 509 serializedInfo := writeInfo(info) 510 kb.db.SetSync(key, serializedInfo) 511 // store a pointer to the infokey by address for fast lookup 512 kb.db.SetSync(addrKey(info.GetAddress()), key) 513 } 514 515 func addrKey(address crypto.Address) []byte { 516 return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix)) 517 } 518 519 func infoKey(name string) []byte { 520 return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) 521 }