github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/keys/filesystem_key_store.go (about) 1 package keys 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path" 12 "strings" 13 "sync" 14 15 "github.com/hyperledger/burrow/crypto" 16 "github.com/tmthrgd/go-hex" 17 "golang.org/x/crypto/scrypt" 18 ) 19 20 type FilesystemKeyStore struct { 21 sync.Mutex 22 UnimplementedKeysServer 23 AllowBadFilePermissions bool 24 keysDirPath string 25 } 26 27 var _ KeyStore = &FilesystemKeyStore{} 28 var _ KeysServer = &FilesystemKeyStore{} 29 30 func NewFilesystemKeyStore(dir string, AllowBadFilePermissions bool) *FilesystemKeyStore { 31 return &FilesystemKeyStore{ 32 keysDirPath: dir, 33 AllowBadFilePermissions: AllowBadFilePermissions, 34 } 35 } 36 37 func (ks *FilesystemKeyStore) Gen(passphrase string, curveType crypto.CurveType) (key *Key, err error) { 38 defer func() { 39 if r := recover(); r != nil { 40 err = fmt.Errorf("GenerateNewKey error: %v", r) 41 } 42 }() 43 key, err = NewKey(curveType) 44 if err != nil { 45 return nil, err 46 } 47 err = ks.StoreKey(passphrase, key) 48 return key, err 49 } 50 51 func (ks *FilesystemKeyStore) GetKey(passphrase string, keyAddr []byte) (*Key, error) { 52 ks.Lock() 53 defer ks.Unlock() 54 dataDirPath, err := returnDataDir(ks.keysDirPath) 55 if err != nil { 56 return nil, err 57 } 58 fileContent, err := ks.GetKeyFile(dataDirPath, keyAddr) 59 if err != nil { 60 return nil, err 61 } 62 key := new(keyJSON) 63 if err = json.Unmarshal(fileContent, key); err != nil { 64 return nil, err 65 } 66 67 if len(key.PrivateKey.CipherText) > 0 { 68 return DecryptKey(passphrase, key) 69 } else { 70 key := new(Key) 71 err = key.UnmarshalJSON(fileContent) 72 return key, err 73 } 74 } 75 76 func (ks *FilesystemKeyStore) AllKeys() ([]*Key, error) { 77 dataDirPath, err := returnDataDir(ks.keysDirPath) 78 if err != nil { 79 return nil, err 80 } 81 addrs, err := getAllAddresses(dataDirPath) 82 if err != nil { 83 return nil, err 84 } 85 86 var list []*Key 87 88 for _, addr := range addrs { 89 addrB, err := crypto.AddressFromHexString(addr) 90 if err != nil { 91 return nil, err 92 } 93 k, err := ks.GetKey("", addrB[:]) 94 if err != nil { 95 return nil, err 96 } 97 list = append(list, k) 98 } 99 100 return list, nil 101 } 102 103 func DecryptKey(passphrase string, keyProtected *keyJSON) (*Key, error) { 104 salt := keyProtected.PrivateKey.Salt 105 nonce := keyProtected.PrivateKey.Nonce 106 cipherText := keyProtected.PrivateKey.CipherText 107 108 curveType, err := crypto.CurveTypeFromString(keyProtected.CurveType) 109 if err != nil { 110 return nil, err 111 } 112 authArray := []byte(passphrase) 113 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) 114 if err != nil { 115 return nil, err 116 } 117 aesBlock, err := aes.NewCipher(derivedKey) 118 if err != nil { 119 return nil, err 120 } 121 gcm, err := cipher.NewGCM(aesBlock) 122 if err != nil { 123 return nil, err 124 } 125 pubKey, err := hex.DecodeString(keyProtected.PublicKey) 126 if err != nil { 127 return nil, err 128 } 129 plainText, err := gcm.Open(nil, nonce, cipherText, nil) 130 if err != nil { 131 pkey, _ := NewKeyFromPub(curveType, pubKey) 132 return pkey, err 133 } 134 address, err := crypto.AddressFromHexString(keyProtected.Address) 135 if err != nil { 136 return nil, err 137 } 138 k, err := NewKeyFromPriv(curveType, plainText) 139 if err != nil { 140 return nil, err 141 } 142 if address != k.Address { 143 return nil, fmt.Errorf("address does not match") 144 } 145 return k, nil 146 } 147 148 func (ks *FilesystemKeyStore) GetAllAddresses() (addresses []string, err error) { 149 ks.Lock() 150 defer ks.Unlock() 151 152 dir, err := returnDataDir(ks.keysDirPath) 153 if err != nil { 154 return nil, err 155 } 156 return getAllAddresses(dir) 157 } 158 159 func (ks *FilesystemKeyStore) StoreKey(passphrase string, key *Key) error { 160 ks.Lock() 161 defer ks.Unlock() 162 if passphrase != "" { 163 return ks.StoreKeyEncrypted(passphrase, key) 164 } else { 165 return ks.StoreKeyPlain(key) 166 } 167 } 168 169 func (ks *FilesystemKeyStore) StoreKeyPlain(key *Key) (err error) { 170 keyJSON, err := json.Marshal(key) 171 if err != nil { 172 return err 173 } 174 dataDirPath, err := returnDataDir(ks.keysDirPath) 175 if err != nil { 176 return err 177 } 178 err = WriteKeyFile(key.Address[:], dataDirPath, keyJSON) 179 return err 180 } 181 182 func (ks *FilesystemKeyStore) StoreKeyEncrypted(passphrase string, key *Key) error { 183 authArray := []byte(passphrase) 184 salt := make([]byte, 32) 185 _, err := rand.Read(salt) 186 if err != nil { 187 return err 188 } 189 190 derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptr, scryptp, scryptdkLen) 191 if err != nil { 192 return err 193 } 194 195 toEncrypt := key.PrivateKey.RawBytes() 196 197 AES256Block, err := aes.NewCipher(derivedKey) 198 if err != nil { 199 return err 200 } 201 202 gcm, err := cipher.NewGCM(AES256Block) 203 if err != nil { 204 return err 205 } 206 207 // XXX: a GCM nonce may only be used once per key ever! 208 nonce := make([]byte, gcm.NonceSize()) 209 _, err = rand.Read(nonce) 210 if err != nil { 211 return err 212 } 213 214 // (dst, nonce, plaintext, extradata) 215 cipherText := gcm.Seal(nil, nonce, toEncrypt, nil) 216 217 cipherStruct := privateKeyJSON{ 218 Crypto: CryptoAESGCM, Salt: salt, Nonce: nonce, CipherText: cipherText, 219 } 220 keyStruct := keyJSON{ 221 CurveType: key.CurveType.String(), 222 Address: hex.EncodeUpperToString(key.Address[:]), 223 PublicKey: hex.EncodeUpperToString(key.Pubkey()), 224 AddressHash: key.PublicKey.AddressHashType(), 225 PrivateKey: cipherStruct, 226 } 227 keyJSON, err := json.Marshal(keyStruct) 228 if err != nil { 229 return err 230 } 231 dataDirPath, err := returnDataDir(ks.keysDirPath) 232 if err != nil { 233 return err 234 } 235 236 return WriteKeyFile(key.Address[:], dataDirPath, keyJSON) 237 } 238 239 func (ks *FilesystemKeyStore) DeleteKey(passphrase string, keyAddr []byte) (err error) { 240 dataDirPath, err := returnDataDir(ks.keysDirPath) 241 if err != nil { 242 return err 243 } 244 keyDirPath := path.Join(dataDirPath, strings.ToUpper(hex.EncodeToString(keyAddr))+".json") 245 return os.Remove(keyDirPath) 246 } 247 248 func (ks *FilesystemKeyStore) GetKeyFile(dataDirPath string, keyAddr []byte) (fileContent []byte, err error) { 249 filename := path.Join(dataDirPath, strings.ToUpper(hex.EncodeToString(keyAddr))+".json") 250 fileInfo, err := os.Stat(filename) 251 if err != nil { 252 return nil, err 253 } 254 if (uint32(fileInfo.Mode()) & 0077) != 0 { 255 if !ks.AllowBadFilePermissions { 256 return nil, fmt.Errorf("file %s should be accessible by user only", filename) 257 } 258 } 259 return ioutil.ReadFile(filename) 260 } 261 262 func WriteKeyFile(addr []byte, dataDirPath string, content []byte) (err error) { 263 addrHex := strings.ToUpper(hex.EncodeToString(addr)) 264 keyFilePath := path.Join(dataDirPath, addrHex+".json") 265 err = os.MkdirAll(dataDirPath, 0700) // read, write and dir search for user 266 if err != nil { 267 return err 268 } 269 return ioutil.WriteFile(keyFilePath, content, 0600) // read, write for user 270 } 271 272 func (ks *FilesystemKeyStore) GetAddressForKeyName(name string) (crypto.Address, error) { 273 const errHeader = "GetAddressForKeyName" 274 nameAddressLookup, err := coreNameList(ks.keysDirPath) 275 if err != nil { 276 return crypto.Address{}, fmt.Errorf("%s: could not get names list from filesysetm: %w", 277 errHeader, err) 278 } 279 280 addressHex, ok := nameAddressLookup[name] 281 if !ok { 282 return crypto.Address{}, fmt.Errorf("%s: could not find key named '%s'", errHeader, name) 283 } 284 285 address, err := crypto.AddressFromHexString(addressHex) 286 if err != nil { 287 return crypto.Address{}, fmt.Errorf("%s: could not parse key address: %v", errHeader, err) 288 } 289 return address, nil 290 } 291 292 func (ks *FilesystemKeyStore) GetAllNames() (map[string]string, error) { 293 return coreNameList(ks.keysDirPath) 294 } 295 296 func getAllAddresses(dataDirPath string) (addresses []string, err error) { 297 fileInfos, err := ioutil.ReadDir(dataDirPath) 298 if err != nil { 299 return nil, err 300 } 301 addresses = make([]string, len(fileInfos)) 302 for i, fileInfo := range fileInfos { 303 addr := strings.TrimSuffix(fileInfo.Name(), ".json") 304 addresses[i] = addr 305 } 306 return addresses, err 307 }