github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/accounts/keystore/key.go (about) 1 package keystore 2 3 import ( 4 "bytes" 5 "crypto/ecdsa" 6 "encoding/hex" 7 "encoding/json" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "github.com/neatlab/neatio/chain/accounts" 17 "github.com/neatlab/neatio/utilities/common" 18 "github.com/neatlab/neatio/utilities/crypto" 19 "github.com/pborman/uuid" 20 ) 21 22 const ( 23 version = 3 24 ) 25 26 type Key struct { 27 Id uuid.UUID 28 29 Address common.Address 30 31 PrivateKey *ecdsa.PrivateKey 32 } 33 34 type keyStore interface { 35 GetKey(addr common.Address, filename string, auth string) (*Key, error) 36 37 StoreKey(filename string, k *Key, auth string) error 38 39 JoinPath(filename string) string 40 } 41 42 type plainKeyJSON struct { 43 Address string `json:"address"` 44 PrivateKey string `json:"privatekey"` 45 Id string `json:"id"` 46 Version int `json:"version"` 47 } 48 49 type encryptedKeyJSONV3 struct { 50 Address string `json:"address"` 51 Crypto cryptoJSON `json:"crypto"` 52 Id string `json:"id"` 53 Version int `json:"version"` 54 } 55 56 type encryptedKeyJSONV1 struct { 57 Address string `json:"address"` 58 Crypto cryptoJSON `json:"crypto"` 59 Id string `json:"id"` 60 Version string `json:"version"` 61 } 62 63 type cryptoJSON struct { 64 Cipher string `json:"cipher"` 65 CipherText string `json:"ciphertext"` 66 CipherParams cipherparamsJSON `json:"cipherparams"` 67 KDF string `json:"kdf"` 68 KDFParams map[string]interface{} `json:"kdfparams"` 69 MAC string `json:"mac"` 70 } 71 72 type cipherparamsJSON struct { 73 IV string `json:"iv"` 74 } 75 76 func (k *Key) MarshalJSON() (j []byte, err error) { 77 jStruct := plainKeyJSON{ 78 hex.EncodeToString(k.Address[:]), 79 hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)), 80 k.Id.String(), 81 version, 82 } 83 j, err = json.Marshal(jStruct) 84 return j, err 85 } 86 87 func (k *Key) UnmarshalJSON(j []byte) (err error) { 88 keyJSON := new(plainKeyJSON) 89 err = json.Unmarshal(j, &keyJSON) 90 if err != nil { 91 return err 92 } 93 94 u := new(uuid.UUID) 95 *u = uuid.Parse(keyJSON.Id) 96 k.Id = *u 97 addr, err := hex.DecodeString(keyJSON.Address) 98 if err != nil { 99 return err 100 } 101 privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey) 102 if err != nil { 103 return err 104 } 105 106 k.Address = common.BytesToAddress(addr) 107 k.PrivateKey = privkey 108 109 return nil 110 } 111 112 func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key { 113 id := uuid.NewRandom() 114 key := &Key{ 115 Id: id, 116 Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), 117 PrivateKey: privateKeyECDSA, 118 } 119 return key 120 } 121 122 func NewKeyForDirectICAP(rand io.Reader) *Key { 123 randBytes := make([]byte, 64) 124 _, err := rand.Read(randBytes) 125 if err != nil { 126 panic("key generation: could not read from random source: " + err.Error()) 127 } 128 reader := bytes.NewReader(randBytes) 129 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) 130 if err != nil { 131 panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) 132 } 133 key := newKeyFromECDSA(privateKeyECDSA) 134 if !strings.HasPrefix(key.Address.Hex(), "0x00") { 135 return NewKeyForDirectICAP(rand) 136 } 137 return key 138 } 139 140 func newKey(rand io.Reader) (*Key, error) { 141 privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) 142 if err != nil { 143 return nil, err 144 } 145 return newKeyFromECDSA(privateKeyECDSA), nil 146 } 147 148 func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) { 149 key, err := newKey(rand) 150 if err != nil { 151 return nil, accounts.Account{}, err 152 } 153 154 a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}} 155 if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { 156 zeroKey(key.PrivateKey) 157 return nil, a, err 158 } 159 return key, a, err 160 } 161 162 func writeKeyFile(file string, content []byte) error { 163 164 const dirPerm = 0700 165 if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { 166 return err 167 } 168 169 f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") 170 if err != nil { 171 return err 172 } 173 if _, err := f.Write(content); err != nil { 174 f.Close() 175 os.Remove(f.Name()) 176 return err 177 } 178 f.Close() 179 return os.Rename(f.Name(), file) 180 } 181 182 func keyFileName(keyAddr common.Address) string { 183 ts := time.Now().UTC() 184 185 return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), keyAddr.String()) 186 } 187 188 func toISO8601(t time.Time) string { 189 var tz string 190 name, offset := t.Zone() 191 if name == "UTC" { 192 tz = "Z" 193 } else { 194 tz = fmt.Sprintf("%03d00", offset/3600) 195 } 196 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) 197 }