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