keystore and account support for multi key (#2054)

* keystore and account support for multi key

* fix service issues

* fix Potential file inclusion

* fix Potential file inclusion try2

* Update shared/keystore/keystore.go

remove security detection for file read

Co-Authored-By: shayzluf <thezluf@gmail.com>

* getkeys uses map to pt multiple copies of the same key

* use 12 char of public key to differentiate file names

* use map in test

* fix changes from 2069 into here

* add // #nosec G304
This commit is contained in:
shayzluf
2019-03-30 03:56:41 +05:30
committed by Preston Van Loon
parent 5b89da1ce2
commit fe247f6cc5
5 changed files with 94 additions and 10 deletions

View File

@@ -30,6 +30,7 @@ import (
"io"
"io/ioutil"
"path/filepath"
"strings"
"github.com/pborman/uuid"
"github.com/prysmaticlabs/prysm/shared/bls"
@@ -80,6 +81,37 @@ func (ks Store) GetKey(filename, password string) (*Key, error) {
return DecryptKey(keyjson, password)
}
// GetKeys from directory using the prefix to filter relevant files
// and a decryption password.
func (ks Store) GetKeys(directory, fileprefix, password string) (map[string]*Key, error) {
// Load the key from the keystore and decrypt its contents
// #nosec G304
files, err := ioutil.ReadDir(directory)
if err != nil {
return nil, err
}
keys := make(map[string]*Key)
for _, f := range files {
n := f.Name()
filePath := filepath.Join(directory, n)
filePath = filepath.Clean(filePath)
cp := strings.Contains(n, strings.TrimPrefix(fileprefix, "/"))
if f.Mode().IsRegular() && cp {
// #nosec G304
keyjson, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
key, err := DecryptKey(keyjson, password)
if err != nil {
return nil, err
}
keys[hex.EncodeToString(key.PublicKey.Marshal())] = key
}
}
return keys, nil
}
// StoreKey in filepath and encrypt it with a password.
func (ks Store) StoreKey(filename string, key *Key, auth string) error {
keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)

View File

@@ -43,6 +43,50 @@ func TestStoreAndGetKey(t *testing.T) {
t.Errorf("unable to remove temporary files %v", err)
}
}
func TestStoreAndGetKeys(t *testing.T) {
tmpdir := testutil.TempDir()
filePrefix := "/keystore"
ks := &Store{
keysDirPath: tmpdir,
scryptN: LightScryptN,
scryptP: LightScryptP,
}
key, err := NewKey(rand.Reader)
if err != nil {
t.Fatalf("key generation failed %v", err)
}
if err := ks.StoreKey(tmpdir+filePrefix+"/test-1", key, "password"); err != nil {
t.Fatalf("unable to store key %v", err)
}
key2, err := NewKey(rand.Reader)
if err != nil {
t.Fatalf("key generation failed %v", err)
}
if err := ks.StoreKey(tmpdir+filePrefix+"/test-2", key2, "password"); err != nil {
t.Fatalf("unable to store key %v", err)
}
newkeys, err := ks.GetKeys(tmpdir+filePrefix, "test", "password")
if err != nil {
t.Fatalf("unable to get key %v", err)
}
for _, s := range newkeys {
if !bytes.Equal(s.SecretKey.Marshal(), key.SecretKey.Marshal()) && !bytes.Equal(s.SecretKey.Marshal(), key2.SecretKey.Marshal()) {
t.Fatalf("retrieved secret keys are not equal %v ", s.SecretKey.Marshal())
}
}
if err := os.RemoveAll(tmpdir + filePrefix + "-2"); err != nil {
t.Errorf("unable to remove temporary files %v", err)
}
if err := os.RemoveAll(tmpdir + filePrefix + "-1"); err != nil {
t.Errorf("unable to remove temporary files %v", err)
}
}
func TestEncryptDecryptKey(t *testing.T) {
newID := uuid.NewRandom()
b := []byte("hi")

View File

@@ -3,6 +3,7 @@ package accounts
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
@@ -39,11 +40,6 @@ func VerifyAccountNotExists(directory string, password string) error {
// generates a BLS private and public key, and then logs the serialized deposit input hex string
// to be used in an ETH1.0 transaction by the validator.
func NewValidatorAccount(directory string, password string) error {
// First, if the keystore already exists, throws an error as there can only be
// one keystore per validator client.
if err := VerifyAccountNotExists(directory, password); err != nil {
return fmt.Errorf("validator account exists: %v", err)
}
shardWithdrawalKeyFile := directory + params.BeaconConfig().WithdrawalPrivkeyFileName
validatorKeyFile := directory + params.BeaconConfig().ValidatorPrivkeyFileName
ks := keystore.NewKeystore(directory)
@@ -52,6 +48,7 @@ func NewValidatorAccount(directory string, password string) error {
if err != nil {
return err
}
shardWithdrawalKeyFile = shardWithdrawalKeyFile + hex.EncodeToString(shardWithdrawalKey.PublicKey.Marshal())[:12]
if err := ks.StoreKey(shardWithdrawalKeyFile, shardWithdrawalKey, password); err != nil {
return fmt.Errorf("unable to store key %v", err)
}
@@ -63,6 +60,7 @@ func NewValidatorAccount(directory string, password string) error {
if err != nil {
return err
}
validatorKeyFile = validatorKeyFile + hex.EncodeToString(validatorKey.PublicKey.Marshal())[:12]
if err := ks.StoreKey(validatorKeyFile, validatorKey, password); err != nil {
return fmt.Errorf("unable to store key %v", err)
}

View File

@@ -2,6 +2,7 @@ package accounts
import (
"crypto/rand"
"io/ioutil"
"os"
"testing"
@@ -21,8 +22,15 @@ func TestNewValidatorAccount_AccountExists(t *testing.T) {
if err := ks.StoreKey(directory+params.BeaconConfig().ValidatorPrivkeyFileName, validatorKey, ""); err != nil {
t.Fatalf("Unable to store key %v", err)
}
if err := NewValidatorAccount(directory, ""); err == nil {
t.Error("Expected new validator account to throw error, received nil")
if err := NewValidatorAccount(directory, ""); err != nil {
t.Errorf("Should support multiple keys: %v", err)
}
files, _ := ioutil.ReadDir(directory)
if len(files) != 3 {
t.Errorf("multiple validators wasn't created only: %v files in directory", len(files))
for _, f := range files {
t.Errorf("%v\n", f.Name())
}
}
if err := os.RemoveAll(directory); err != nil {
t.Fatalf("Could not remove directory: %v", err)

View File

@@ -26,6 +26,7 @@ type ValidatorService struct {
endpoint string
withCert string
key *keystore.Key
keys map[string]*keystore.Key
}
// Config for the validator service.
@@ -40,9 +41,10 @@ type Config struct {
// registry.
func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, error) {
ctx, cancel := context.WithCancel(ctx)
validatorKeyFile := cfg.KeystorePath + params.BeaconConfig().ValidatorPrivkeyFileName
validatorFolder := cfg.KeystorePath
validatorPrefix := params.BeaconConfig().ValidatorPrivkeyFileName
ks := keystore.NewKeystore(cfg.KeystorePath)
key, err := ks.GetKey(validatorKeyFile, cfg.Password)
keys, err := ks.GetKeys(validatorFolder, validatorPrefix, cfg.Password)
if err != nil {
return nil, fmt.Errorf("could not get private key: %v", err)
}
@@ -51,7 +53,7 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e
cancel: cancel,
endpoint: cfg.Endpoint,
withCert: cfg.CertFlag,
key: key,
keys: keys,
}, nil
}