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  }