github.com/core-coin/go-core/v2@v2.1.9/accounts/keystore/key.go (about) 1 // Copyright 2014 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package keystore 18 19 import ( 20 "encoding/hex" 21 "encoding/json" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "time" 28 29 "github.com/pborman/uuid" 30 31 "github.com/core-coin/go-core/v2/accounts" 32 "github.com/core-coin/go-core/v2/common" 33 "github.com/core-coin/go-core/v2/crypto" 34 ) 35 36 const ( 37 version = 3 38 ) 39 40 type Key struct { 41 Id uuid.UUID // Version 4 "random" for unique id not derived from key data 42 // to simplify lookups we also store the address 43 Address common.Address 44 // we only store privkey as pubkey/address can be derived from it 45 // privkey in this struct is always in plaintext 46 PrivateKey *crypto.PrivateKey 47 } 48 49 type keyStore interface { 50 // Loads and decrypts the key from disk. 51 GetKey(addr common.Address, filename string, auth string) (*Key, error) 52 // Writes and encrypts the key. 53 StoreKey(filename string, k *Key, auth string) error 54 // Joins filename with the key directory unless it is already absolute. 55 JoinPath(filename string) string 56 } 57 58 type plainKeyJSON struct { 59 Address string `json:"address"` 60 PrivateKey string `json:"privatekey"` 61 Id string `json:"id"` 62 Version int `json:"version"` 63 } 64 65 type encryptedKeyJSONV3 struct { 66 Address string `json:"address"` 67 Crypto CryptoJSON `json:"crypto"` 68 Id string `json:"id"` 69 Version int `json:"version"` 70 } 71 72 type CryptoJSON struct { 73 Cipher string `json:"cipher"` 74 CipherText string `json:"ciphertext"` 75 CipherParams cipherparamsJSON `json:"cipherparams"` 76 KDF string `json:"kdf"` 77 KDFParams map[string]interface{} `json:"kdfparams"` 78 MAC string `json:"mac"` 79 } 80 81 type cipherparamsJSON struct { 82 IV string `json:"iv"` 83 } 84 85 func (k *Key) MarshalJSON() (j []byte, err error) { 86 jStruct := plainKeyJSON{ 87 hex.EncodeToString(k.Address[:]), 88 hex.EncodeToString(crypto.MarshalPrivateKey(k.PrivateKey)), 89 k.Id.String(), 90 version, 91 } 92 j, err = json.Marshal(jStruct) 93 return j, err 94 } 95 96 func (k *Key) UnmarshalJSON(j []byte) (err error) { 97 keyJSON := new(plainKeyJSON) 98 err = json.Unmarshal(j, &keyJSON) 99 if err != nil { 100 return err 101 } 102 103 u := new(uuid.UUID) 104 *u = uuid.Parse(keyJSON.Id) 105 k.Id = *u 106 addr, err := hex.DecodeString(keyJSON.Address) 107 if err != nil { 108 return err 109 } 110 privkey, err := crypto.UnmarshalPrivateKeyHex(keyJSON.PrivateKey) 111 if err != nil { 112 return err 113 } 114 115 k.Address = common.BytesToAddress(addr) 116 k.PrivateKey = privkey 117 118 return nil 119 } 120 121 func newKeyFromEDDSA(privateKeyEDDSA *crypto.PrivateKey) *Key { 122 id := uuid.NewRandom() 123 key := &Key{ 124 Id: id, 125 Address: privateKeyEDDSA.Address(), 126 PrivateKey: privateKeyEDDSA, 127 } 128 return key 129 } 130 131 func newKey(rand io.Reader) (*Key, error) { 132 privateKeyEDDSA, err := crypto.GenerateKey(rand) 133 if err != nil { 134 return nil, err 135 } 136 return newKeyFromEDDSA(privateKeyEDDSA), nil 137 } 138 139 func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) { 140 key, err := newKey(rand) 141 if err != nil { 142 return nil, accounts.Account{}, err 143 } 144 a := accounts.Account{ 145 Address: key.Address, 146 URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}, 147 } 148 if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { 149 zeroKey(key.PrivateKey) 150 return nil, a, err 151 } 152 return key, a, err 153 } 154 155 func writeTemporaryKeyFile(file string, content []byte) (string, error) { 156 // Create the keystore directory with appropriate permissions 157 // in case it is not present yet. 158 const dirPerm = 0700 159 if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { 160 return "", err 161 } 162 // Atomic write: create a temporary hidden file first 163 // then move it into place. TempFile assigns mode 0600. 164 f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") 165 if err != nil { 166 return "", err 167 } 168 if _, err := f.Write(content); err != nil { 169 f.Close() 170 os.Remove(f.Name()) 171 return "", err 172 } 173 f.Close() 174 return f.Name(), nil 175 } 176 177 func writeKeyFile(file string, content []byte) error { 178 name, err := writeTemporaryKeyFile(file, content) 179 if err != nil { 180 return err 181 } 182 return os.Rename(name, file) 183 } 184 185 // keyFileName implements the naming convention for keyfiles: 186 // UTC--<created_at UTC ISO8601>-<address hex> 187 func keyFileName(keyAddr common.Address) string { 188 ts := time.Now().UTC() 189 return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:])) 190 } 191 192 func toISO8601(t time.Time) string { 193 var tz string 194 name, offset := t.Zone() 195 if name == "UTC" { 196 tz = "Z" 197 } else { 198 tz = fmt.Sprintf("%03d00", offset/3600) 199 } 200 return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", 201 t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) 202 }