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