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