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