mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Tool for Decrypting/Encrypting EIP-2335 Keystores (#6900)
* begin keystore tool * add bazel * keystores tool * fully decrypt * implement encrypt * complete encrypt and decrypt implementations * finalize tool * gaz * fix viz * help Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
43
tools/keystores/BUILD.bazel
Normal file
43
tools/keystores/BUILD.bazel
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test")
|
||||||
|
load("@prysm//tools/go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["main.go"],
|
||||||
|
importpath = "github.com/prysmaticlabs/prysm/tools/keystores",
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
"//shared/bls:go_default_library",
|
||||||
|
"//shared/params:go_default_library",
|
||||||
|
"//shared/promptutil:go_default_library",
|
||||||
|
"//validator/keymanager/v2:go_default_library",
|
||||||
|
"@com_github_google_uuid//:go_default_library",
|
||||||
|
"@com_github_logrusorgru_aurora//:go_default_library",
|
||||||
|
"@com_github_pkg_errors//:go_default_library",
|
||||||
|
"@com_github_urfave_cli_v2//:go_default_library",
|
||||||
|
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "keystores",
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["main_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//shared/bls:go_default_library",
|
||||||
|
"//shared/params:go_default_library",
|
||||||
|
"//shared/testutil:go_default_library",
|
||||||
|
"//shared/testutil/assert:go_default_library",
|
||||||
|
"//shared/testutil/require:go_default_library",
|
||||||
|
"//validator/keymanager/v2:go_default_library",
|
||||||
|
"@com_github_google_uuid//:go_default_library",
|
||||||
|
"@com_github_urfave_cli_v2//:go_default_library",
|
||||||
|
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
308
tools/keystores/main.go
Normal file
308
tools/keystores/main.go
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
// This tool allows for simple encrypting and decrypting of EIP-2335 compliant, BLS12-381
|
||||||
|
// keystore.json files which as password protected. This is helpful in development to inspect
|
||||||
|
// the contents of keystores created by eth2 wallets or to easily produce keystores from a
|
||||||
|
// specified secret to move them around in a standard format between eth2 clients.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/logrusorgru/aurora"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/promptutil"
|
||||||
|
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
keystoresFlag = &cli.StringFlag{
|
||||||
|
Name: "keystores",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Path to a file or directory containing keystore files",
|
||||||
|
Required: true,
|
||||||
|
}
|
||||||
|
passwordFlag = &cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Password for the keystore(s)",
|
||||||
|
}
|
||||||
|
privateKeyFlag = &cli.StringFlag{
|
||||||
|
Name: "private-key",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Hex string for the BLS12-381 private key you wish encrypt into a keystore file",
|
||||||
|
Required: true,
|
||||||
|
}
|
||||||
|
outputPathFlag = &cli.StringFlag{
|
||||||
|
Name: "output-path",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Output path to write the newly encrypted keystore file",
|
||||||
|
Required: true,
|
||||||
|
}
|
||||||
|
au = aurora.NewAurora(true /* enable colors */)
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := &cli.App{
|
||||||
|
Name: "Keystore utility",
|
||||||
|
Description: "Utility to encrypt and decrypt EIP-2335 compliant keystore.json files for BLS12-381 private keys",
|
||||||
|
Usage: "",
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "decrypt",
|
||||||
|
Usage: "decrypt a specified keystore file or directory containing keystore files",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
keystoresFlag,
|
||||||
|
passwordFlag,
|
||||||
|
},
|
||||||
|
Action: decrypt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "encrypt",
|
||||||
|
Usage: "encrypt a specified hex value of a BLS12-381 private key into a keystore file",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
passwordFlag,
|
||||||
|
privateKeyFlag,
|
||||||
|
outputPathFlag,
|
||||||
|
},
|
||||||
|
Action: encrypt,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := app.Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decrypt(cliCtx *cli.Context) error {
|
||||||
|
keystorePath := cliCtx.String(keystoresFlag.Name)
|
||||||
|
if keystorePath == "" {
|
||||||
|
return errors.New("--keystore must be set")
|
||||||
|
}
|
||||||
|
fullPath, err := expandPath(keystorePath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not expand path: %s", keystorePath)
|
||||||
|
}
|
||||||
|
password := cliCtx.String(passwordFlag.Name)
|
||||||
|
isPasswordSet := cliCtx.IsSet(passwordFlag.Name)
|
||||||
|
if !isPasswordSet {
|
||||||
|
password, err = promptutil.PasswordPrompt("Input the keystore(s) password", func(s string) error {
|
||||||
|
// Any password is valid.
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDir, err := hasDir(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not check if path exists: %s", fullPath)
|
||||||
|
}
|
||||||
|
if isDir {
|
||||||
|
files, err := ioutil.ReadDir(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not read directory: %s", fullPath)
|
||||||
|
}
|
||||||
|
for _, f := range files {
|
||||||
|
if f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keystorePath := filepath.Join(fullPath, f.Name())
|
||||||
|
if err := readAndDecryptKeystore(keystorePath, password); err != nil {
|
||||||
|
fmt.Printf("could not read nor decrypt keystore at path %s: %v\n", keystorePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return readAndDecryptKeystore(fullPath, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempts to encrypt a passed-in BLS12-3381 private key into the EIP-2335
|
||||||
|
// keystore.json format. If a file at the specified output path exists, asks the user
|
||||||
|
// to confirm overwriting its contents. If the value passed in is not a valid BLS12-381
|
||||||
|
// private key, the function will fail.
|
||||||
|
func encrypt(cliCtx *cli.Context) error {
|
||||||
|
var err error
|
||||||
|
password := cliCtx.String(passwordFlag.Name)
|
||||||
|
isPasswordSet := cliCtx.IsSet(passwordFlag.Name)
|
||||||
|
if !isPasswordSet {
|
||||||
|
password, err = promptutil.PasswordPrompt("Input the keystore(s) password", func(s string) error {
|
||||||
|
// Any password is valid.
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
privateKeyString := cliCtx.String(privateKeyFlag.Name)
|
||||||
|
if privateKeyString == "" {
|
||||||
|
return errors.New("--private-key must not be empty")
|
||||||
|
}
|
||||||
|
outputPath := cliCtx.String(outputPathFlag.Name)
|
||||||
|
if outputPath == "" {
|
||||||
|
return errors.New("--output-path must be set")
|
||||||
|
}
|
||||||
|
fullPath, err := expandPath(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not expand path: %s", outputPath)
|
||||||
|
}
|
||||||
|
exists, err := fileExists(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not check if file exists: %s", fullPath)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
response, err := promptutil.ValidatePrompt(
|
||||||
|
fmt.Sprintf("file at path %s already exists, are you sure you want to overwrite it? [y/n]", fullPath),
|
||||||
|
func(s string) error {
|
||||||
|
input := strings.ToLower(s)
|
||||||
|
if input != "y" && input != "n" {
|
||||||
|
return errors.New("please confirm the above text")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not validate prompt confirmation")
|
||||||
|
}
|
||||||
|
if response == "n" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(privateKeyString) > 2 && strings.Contains(privateKeyString, "0x") {
|
||||||
|
privateKeyString = privateKeyString[2:] // Strip the 0x prefix, if any.
|
||||||
|
}
|
||||||
|
bytesValue, err := hex.DecodeString(privateKeyString)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not decode as hex string: %s", privateKeyString)
|
||||||
|
}
|
||||||
|
privKey, err := bls.SecretKeyFromBytes(bytesValue)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "not a valid BLS12-381 private key")
|
||||||
|
}
|
||||||
|
pubKey := fmt.Sprintf("%x", privKey.PublicKey().Marshal())
|
||||||
|
encryptor := keystorev4.New()
|
||||||
|
id, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not generate new random uuid")
|
||||||
|
}
|
||||||
|
cryptoFields, err := encryptor.Encrypt(bytesValue, password)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not encrypt into new keystore")
|
||||||
|
}
|
||||||
|
item := &v2keymanager.Keystore{
|
||||||
|
Crypto: cryptoFields,
|
||||||
|
ID: id.String(),
|
||||||
|
Version: encryptor.Version(),
|
||||||
|
Pubkey: pubKey,
|
||||||
|
Name: encryptor.Name(),
|
||||||
|
}
|
||||||
|
encodedFile, err := json.MarshalIndent(item, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not json marshal keystore")
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(fullPath, encodedFile, params.BeaconIoConfig().ReadWritePermissions); err != nil {
|
||||||
|
return errors.Wrapf(err, "could not write file at path: %s", fullPath)
|
||||||
|
}
|
||||||
|
fmt.Printf(
|
||||||
|
"\nWrote encrypted keystore file at path %s\n",
|
||||||
|
au.BrightMagenta(fullPath),
|
||||||
|
)
|
||||||
|
fmt.Printf("Pubkey: %s\n", au.BrightGreen(
|
||||||
|
fmt.Sprintf("%#x", privKey.PublicKey().Marshal()),
|
||||||
|
))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the keystore file at the provided path and attempts
|
||||||
|
// to decrypt it with the specified passwords.
|
||||||
|
func readAndDecryptKeystore(fullPath string, password string) error {
|
||||||
|
file, err := ioutil.ReadFile(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not read file at path: %s", fullPath)
|
||||||
|
}
|
||||||
|
decryptor := keystorev4.New()
|
||||||
|
keystoreFile := &v2keymanager.Keystore{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(file, keystoreFile); err != nil {
|
||||||
|
return errors.Wrap(err, "could not JSON unmarshal keystore file")
|
||||||
|
}
|
||||||
|
// We extract the validator signing private key from the keystore
|
||||||
|
// by utilizing the password.
|
||||||
|
privKeyBytes, err := decryptor.Decrypt(keystoreFile.Crypto, password)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "invalid checksum") {
|
||||||
|
return fmt.Errorf("incorrect password for keystore at path: %s", fullPath)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
publicKeyBytes, err := hex.DecodeString(keystoreFile.Pubkey)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not parse public key for keystore at path: %s", fullPath)
|
||||||
|
}
|
||||||
|
fmt.Printf("\nDecrypted keystore %s\n", au.BrightMagenta(fullPath))
|
||||||
|
fmt.Printf("Privkey: %#x\n", au.BrightGreen(privKeyBytes))
|
||||||
|
fmt.Printf("Pubkey: %#x\n", au.BrightGreen(publicKeyBytes))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the item at the specified path exists and is a directory.
|
||||||
|
func hasDir(dirPath string) (bool, error) {
|
||||||
|
fullPath, err := expandPath(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
info, err := os.Stat(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return info.IsDir(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a file at the specified path exists.
|
||||||
|
func fileExists(filePath string) (bool, error) {
|
||||||
|
fullPath, err := expandPath(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(fullPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expands a file path
|
||||||
|
// 1. replace tilde with users home dir
|
||||||
|
// 2. expands embedded environment variables
|
||||||
|
// 3. cleans the path, e.g. /a/b/../c -> /a/c
|
||||||
|
// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
|
||||||
|
func expandPath(p string) (string, error) {
|
||||||
|
if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
|
||||||
|
if home := homeDir(); home != "" {
|
||||||
|
p = home + p[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filepath.Abs(path.Clean(os.ExpandEnv(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func homeDir() string {
|
||||||
|
if home := os.Getenv("HOME"); home != "" {
|
||||||
|
return home
|
||||||
|
}
|
||||||
|
if usr, err := user.Current(); err == nil {
|
||||||
|
return usr.HomeDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
151
tools/keystores/main_test.go
Normal file
151
tools/keystores/main_test.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/params"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||||
|
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||||
|
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cliConfig struct {
|
||||||
|
keystoresPath string
|
||||||
|
password string
|
||||||
|
privateKey string
|
||||||
|
outputPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupCliContext(
|
||||||
|
tb testing.TB,
|
||||||
|
conf *cliConfig,
|
||||||
|
) *cli.Context {
|
||||||
|
app := cli.App{}
|
||||||
|
set := flag.NewFlagSet("test", 0)
|
||||||
|
set.String(keystoresFlag.Name, conf.keystoresPath, "")
|
||||||
|
set.String(passwordFlag.Name, conf.password, "")
|
||||||
|
set.String(privateKeyFlag.Name, conf.privateKey, "")
|
||||||
|
set.String(outputPathFlag.Name, conf.outputPath, "")
|
||||||
|
assert.NoError(tb, set.Set(keystoresFlag.Name, conf.keystoresPath))
|
||||||
|
assert.NoError(tb, set.Set(passwordFlag.Name, conf.password))
|
||||||
|
assert.NoError(tb, set.Set(privateKeyFlag.Name, conf.privateKey))
|
||||||
|
assert.NoError(tb, set.Set(outputPathFlag.Name, conf.outputPath))
|
||||||
|
return cli.NewContext(&app, set, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRandomKeystore(t testing.TB, password string) (*v2keymanager.Keystore, bls.SecretKey) {
|
||||||
|
encryptor := keystorev4.New()
|
||||||
|
id, err := uuid.NewRandom()
|
||||||
|
require.NoError(t, err)
|
||||||
|
validatingKey := bls.RandKey()
|
||||||
|
pubKey := validatingKey.PublicKey().Marshal()
|
||||||
|
cryptoFields, err := encryptor.Encrypt(validatingKey.Marshal(), password)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return &v2keymanager.Keystore{
|
||||||
|
Crypto: cryptoFields,
|
||||||
|
Pubkey: fmt.Sprintf("%x", pubKey),
|
||||||
|
ID: id.String(),
|
||||||
|
Version: encryptor.Version(),
|
||||||
|
Name: encryptor.Name(),
|
||||||
|
}, validatingKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRandomDir(t testing.TB) string {
|
||||||
|
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||||
|
require.NoError(t, err)
|
||||||
|
randDir := filepath.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||||
|
require.NoError(t, os.MkdirAll(randDir, os.ModePerm))
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, os.RemoveAll(randDir), "Failed to remove directory")
|
||||||
|
})
|
||||||
|
return randDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecrypt(t *testing.T) {
|
||||||
|
keystoresDir := setupRandomDir(t)
|
||||||
|
password := "secretPassw0rd$1999"
|
||||||
|
keystore, privKey := createRandomKeystore(t, password)
|
||||||
|
// We write a random keystore to a keystores directory.
|
||||||
|
encodedKeystore, err := json.MarshalIndent(keystore, "", "\t")
|
||||||
|
require.NoError(t, err)
|
||||||
|
keystoreFilePath := filepath.Join(keystoresDir, "keystore.json")
|
||||||
|
require.NoError(t, ioutil.WriteFile(
|
||||||
|
keystoreFilePath, encodedKeystore, params.BeaconIoConfig().ReadWritePermissions),
|
||||||
|
)
|
||||||
|
|
||||||
|
cliCtx := setupCliContext(t, &cliConfig{
|
||||||
|
keystoresPath: keystoreFilePath,
|
||||||
|
password: password,
|
||||||
|
})
|
||||||
|
|
||||||
|
rescueStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
// We attempt to decrypt the keystore file we just wrote to disk.
|
||||||
|
require.NoError(t, decrypt(cliCtx))
|
||||||
|
|
||||||
|
require.NoError(t, w.Close())
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// We capture output from stdout.
|
||||||
|
os.Stdout = rescueStdout
|
||||||
|
stringOutput := string(out)
|
||||||
|
|
||||||
|
// We capture the results of stdout to check the public key and private keys
|
||||||
|
// were both printed to stdout.
|
||||||
|
assert.Equal(t, strings.Contains(stringOutput, keystore.Pubkey), true)
|
||||||
|
assert.Equal(t, strings.Contains(stringOutput, fmt.Sprintf("%#x", privKey.Marshal())), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncrypt(t *testing.T) {
|
||||||
|
keystoresDir := setupRandomDir(t)
|
||||||
|
password := "secretPassw0rd$1999"
|
||||||
|
keystoreFilePath := filepath.Join(keystoresDir, "keystore.json")
|
||||||
|
privKey := bls.RandKey()
|
||||||
|
|
||||||
|
cliCtx := setupCliContext(t, &cliConfig{
|
||||||
|
outputPath: keystoreFilePath,
|
||||||
|
password: password,
|
||||||
|
privateKey: fmt.Sprintf("%#x", privKey.Marshal()),
|
||||||
|
})
|
||||||
|
|
||||||
|
rescueStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
// We attempt to encrypt the secret key and save it to the output path.
|
||||||
|
require.NoError(t, encrypt(cliCtx))
|
||||||
|
|
||||||
|
require.NoError(t, w.Close())
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// We capture output from stdout.
|
||||||
|
os.Stdout = rescueStdout
|
||||||
|
stringOutput := string(out)
|
||||||
|
|
||||||
|
// We capture the results of stdout to check the public key was printed to stdout.
|
||||||
|
assert.Equal(
|
||||||
|
t,
|
||||||
|
strings.Contains(stringOutput, fmt.Sprintf("%x", privKey.PublicKey().Marshal())),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ go_library(
|
|||||||
visibility = [
|
visibility = [
|
||||||
"//validator:__pkg__",
|
"//validator:__pkg__",
|
||||||
"//validator:__subpackages__",
|
"//validator:__subpackages__",
|
||||||
|
"//tools/keystores:__pkg__",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//proto/validator/accounts/v2:go_default_library",
|
"//proto/validator/accounts/v2:go_default_library",
|
||||||
|
|||||||
Reference in New Issue
Block a user