github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/accounts/keystore/key.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package keystore 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "encoding/hex" 23 "encoding/json" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "strings" 30 "time" 31 32 "github.com/ethereum/go-ethereum/accounts" 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/crypto" 35 "github.com/google/uuid" 36 ) 37 38 const ( 39 version = 3 40 ) 41 42 type Key struct { 43 Id uuid.UUID // Version 4 "random" for unique id not derived from key data 44 // to simplify lookups we also store the address 45 Address common.Address 46 // we only store privkey as pubkey/address can be derived from it 47 // privkey in this struct is always in plaintext 48 PrivateKey *ecdsa.PrivateKey 49 } 50 51 type keyStore interface { 52 // Loads and decrypts the key from disk. 53 GetKey(addr common.Address, filename string, auth string) (*Key, error) 54 // Writes and encrypts the key. 55 StoreKey(filename string, k *Key, auth string) error 56 // Joins filename with the key directory unless it is already absolute. 57 JoinPath(filename string) string 58 } 59 60 type plainKeyJSON struct { 61 Address string `json:"address"` 62 PrivateKey string `json:"privatekey"` 63 Id string `json:"id"` 64 Version int `json:"version"` 65 } 66 67 type encryptedKeyJSONV3 struct { 68 Address string `json:"address"` 69 Crypto CryptoJSON `json:"crypto"` 70 Id string `json:"id"` 71 Version int `json:"version"` 72 } 73 74 type encryptedKeyJSONV1 struct { 75 Address string `json:"address"` 76 Crypto CryptoJSON `json:"crypto"` 77 Id string `json:"id"` 78 Version string `json:"version"` 79 } 80 81 type CryptoJSON struct { 82 Cipher string `json:"cipher"` 83 CipherText string `json:"ciphertext"` 84 CipherParams cipherparamsJSON `json:"cipherparams"` 85 KDF string `json:"kdf"` 86 KDFParams map[string]interface{} `json:"kdfparams"` 87 MAC string `json:"mac"` 88 } 89 90 type cipherparamsJSON struct { 91 IV string `json:"iv"` 92 } 93 94 func (k *Key) MarshalJSON() (j []byte, err error) { 95 jStruct := plainKeyJSON{ 96 hex.EncodeToString(k.Address[:]), 97 hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), 98 k.Id.String(), 99 version, 100 } 101 j, err = json.Marshal(jStruct) 102 return j, err 103 } 104 105 func (k *Key) UnmarshalJSON(j []byte) (err error) { 106 keyJSON := new(plainKeyJSON) 107 err = json.Unmarshal(j, &keyJSON) 108 if err != nil { 109 return err 110 } 111 112 u := new(uuid.UUID) 113 *u, err = uuid.Parse(keyJSON.Id) 114 if err != nil { 115 return err 116 } 117 k.Id = *u 118 addr, err := hex.DecodeString(keyJSON.Address) 119 if err != nil { 120 return err 121 } 122 privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey) 123 if err != nil { 124 return err 125 } 126 127 k.Address = common.BytesToAddress(addr) 128 k.PrivateKey = privkey 129 130 return nil 131 } 132 133 func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { 134 id, err := uuid.NewRandom() 135 if err != nil { 136 panic(fmt.Sprintf("Could not create random uuid: %v", err)) 137 } 138 key := &Key{ 139 Id: id, 140 Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), 141 PrivateKey: privateKeyECDSA, 142 } 143 return key 144 } 145 146 // NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit 147 // into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we 148 // retry until the first byte is 0. 149 func NewKeyForDirectICAP(rand io.Reader) *Key { 150 randBytes := make([]byte, 64) 151 _, err := rand.Read(randBytes) 152 if err != nil { 153 panic("key generation: could not read from random source: " + err.Error()) 154 } 155 reader := bytes.NewReader(randBytes) 156 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) 157 if err != nil { 158 panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) 159 } 160 key := newKeyFromECDSA(privateKeyECDSA) 161 if !strings.HasPrefix(key.Address.Hex(), "0x00") { 162 return NewKeyForDirectICAP(rand) 163 } 164 return key 165 } 166 167 func newKey(rand io.Reader) (*Key, error) { 168 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) 169 if err != nil { 170 return nil, err 171 } 172 return newKeyFromECDSA(privateKeyECDSA), nil 173 } 174 175 func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) { 176 key, err := newKey(rand) 177 if err != nil { 178 return nil, accounts.Account{}, err 179 } 180 a := accounts.Account{ 181 Address: key.Address, 182 URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}, 183 } 184 if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { 185 zeroKey(key.PrivateKey) 186 return nil, a, err 187 } 188 return key, a, err 189 } 190 191 func writeTemporaryKeyFile(file string, content []byte) (string, error) { 192 // Create the keystore directory with appropriate permissions 193 // in case it is not present yet. 194 const dirPerm = 0700 195 if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { 196 return "", err 197 } 198 // Atomic write: create a temporary hidden file first 199 // then move it into place. TempFile assigns mode 0600. 200 f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") 201 if err != nil { 202 return "", err 203 } 204 if _, err := f.Write(content); err != nil { 205 f.Close() 206 os.Remove(f.Name()) 207 return "", err 208 } 209 f.Close() 210 return f.Name(), nil 211 } 212 213 func writeKeyFile(file string, content []byte) error { 214 name, err := writeTemporaryKeyFile(file, content) 215 if err != nil { 216 return err 217 } 218 return os.Rename(name, file) 219 } 220 221 // keyFileName implements the naming convention for keyfiles: 222 // UTC--<created_at UTC ISO8601>-<address hex> 223 func keyFileName(keyAddr common.Address) string { 224 ts := time.Now().UTC() 225 return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:])) 226 } 227 228 func toISO8601(t time.Time) string { 229 var tz string 230 name, offset := t.Zone() 231 if name == "UTC" { 232 tz = "Z" 233 } else { 234 tz = fmt.Sprintf("%03d00", offset/3600) 235 } 236 return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", 237 t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) 238 }