mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 07:58:22 -05:00
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:
committed by
Preston Van Loon
parent
5b89da1ce2
commit
fe247f6cc5
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user