github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/store/secret.go (about) 1 /* 2 * Copyright (c) 2018-2020 vChain, Inc. All Rights Reserved. 3 * This software is released under GPL3. 4 * The full license information can be found under: 5 * https://www.gnu.org/licenses/gpl-3.0.en.html 6 * 7 */ 8 9 package store 10 11 import ( 12 "crypto/ecdsa" 13 "fmt" 14 "io" 15 "os" 16 "path/filepath" 17 "strings" 18 19 "github.com/ethereum/go-ethereum/accounts" 20 "github.com/ethereum/go-ethereum/accounts/keystore" 21 "github.com/ethereum/go-ethereum/crypto" 22 ) 23 24 func (u *User) keystore() (ks *keystore.KeyStore, err error) { 25 if u == nil || u.Email == "" { 26 return nil, fmt.Errorf("user has not been initialized") 27 } 28 29 if u.KeyStore == "" { 30 u.KeyStore = filepath.Join(dir, "u", u.Email, "k") 31 } 32 33 path, err := filepath.Abs(u.KeyStore) 34 if err != nil { 35 return 36 } 37 38 if err = ensureDir(path); err != nil { 39 return 40 } 41 u.KeyStore = path 42 43 return keystore.NewKeyStore(path, keystore.StandardScryptN, keystore.StandardScryptP), nil 44 } 45 46 func (u *User) defaultAccount() (acc *accounts.Account, err error) { 47 ks, err := u.keystore() 48 if err != nil { 49 return 50 } 51 52 accs := ks.Accounts() 53 if len(accs) == 0 { 54 return 55 } 56 57 d := u.defaultSecretFilepath() 58 for _, a := range accs { 59 if a.URL.Path == d { 60 return &a, nil 61 } 62 } 63 64 return &accs[0], nil 65 } 66 67 func (u User) defaultSecretFilepath() string { 68 return filepath.Join(u.KeyStore, defaultSecretFile) 69 } 70 71 // SignerIDFromSecret returns the public address for the User's secret, if any, otherwise an empty string. 72 func (u *User) SignerIDFromSecret() string { 73 if acc, _ := u.defaultAccount(); acc != nil { 74 return strings.ToLower(acc.Address.Hex()) 75 } 76 return "" 77 } 78 79 // OpenSecret opens the user's Web3 Secret Storage JSON file for reading. 80 func (u *User) OpenSecret() (io.Reader, error) { 81 acc, err := u.defaultAccount() 82 if err != nil { 83 return nil, fmt.Errorf("cannot open the secret storage: %s", err) 84 } 85 if acc == nil { 86 return nil, fmt.Errorf("no secret found") 87 } 88 return os.Open(acc.URL.Path) 89 } 90 91 // WriteSecret reads a Web3 Secret Storage JSON file from src and writes to disk into the user's secret storage path. 92 func (u *User) WriteSecret(src io.Reader) error { 93 // ensure keystore is ready 94 _, err := u.keystore() 95 if err != nil { 96 return fmt.Errorf("cannot open the keystore: %s", err) 97 } 98 99 dst, err := os.Create(u.defaultSecretFilepath()) 100 if err != nil { 101 return err 102 } 103 if _, err := io.Copy(dst, src); err != nil { 104 return err 105 } 106 107 return nil 108 } 109 110 // ImportSecret imports the user's secret from a ECDSA private key and, 111 // if successful, any pre-existing secret will be removed. 112 // The provided passphrase is used to encrypt the secret. 113 func (u *User) ImportSecret(privateKey ecdsa.PrivateKey, passphrase string) error { 114 ks, err := u.keystore() 115 if err != nil { 116 return fmt.Errorf("cannot open the keystore: %s", err) 117 } 118 119 // todo(leogr): go-ethereum does not allow to overwrite an existing account, 120 // so we need to remove the stored secret if matches. 121 // But the previous stored account could be lost, although it can be still recovered 122 // by using the provided privateKey. 123 acc, err := ks.Find(accounts.Account{ 124 Address: crypto.PubkeyToAddress(privateKey.PublicKey), 125 }) 126 if err == nil { 127 if err := os.Remove(acc.URL.Path); err != nil { 128 return err 129 } 130 ks, _ = u.keystore() // reloads the keystore from disk 131 } 132 133 account, err := ks.ImportECDSA(&privateKey, passphrase) 134 if err != nil { 135 return err 136 } 137 138 // Cleanup other secrets, if any 139 for _, a := range ks.Accounts() { 140 if a.URL.Path == account.URL.Path { 141 continue 142 } 143 if err := os.Remove(a.URL.Path); err != nil { 144 return err 145 } 146 } 147 return nil 148 }