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