[node.sh] a lot of refactoring and added getPubKeyFromFilePath

pull/3219/head
Jacky Wang 4 years ago
parent 80836edcdd
commit 40721eafd0
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 8
      cmd/harmony/blsloader/decrypter.go
  2. 12
      cmd/harmony/blsloader/helper.go
  3. 16
      cmd/harmony/blsloader/loader.go
  4. 82
      cmd/harmony/blsloader/loader_test.go
  5. 23
      cmd/harmony/blsloader/params.go
  6. 195
      cmd/harmony/blsloader/passProvider.go
  7. 33
      cmd/harmony/blsloader/utils.go

@ -0,0 +1,8 @@
package blsloader
import bls_core "github.com/harmony-one/bls/ffi/go/bls"
type decrypter interface {
validate() error
decrypt(keyFile string) (*bls_core.SecretKey, error)
}

@ -6,8 +6,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/harmony-one/harmony/crypto/bls"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/multibls"
) )
@ -21,25 +19,19 @@ type loadHelper interface {
type basicSingleBlsLoader struct { type basicSingleBlsLoader struct {
blsKeyFile string blsKeyFile string
passProviderConfig passDecrypterConfig
} }
// loadKeys load bls keys from a single bls file // loadKeys load bls keys from a single bls file
func (loader *basicSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) { func (loader *basicSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) {
fmt.Println("load keys")
providers, err := loader.getPassProviders() providers, err := loader.getPassProviders()
if err != nil { if err != nil {
fmt.Println("not loaded 1")
return multibls.PrivateKeys{}, err return multibls.PrivateKeys{}, err
} }
fmt.Println("provider got")
secretKey, err := loadBasicKey(loader.blsKeyFile, providers) secretKey, err := loadBasicKey(loader.blsKeyFile, providers)
if err != nil { if err != nil {
fmt.Println("not loaded 2")
return multibls.PrivateKeys{}, err return multibls.PrivateKeys{}, err
} }
fmt.Println("loaded secret key")
console.printf("loaded bls key %x\n", bls.WrapperFromPrivateKey(secretKey).Pub.Bytes)
return multibls.GetPrivateKeys(secretKey), nil return multibls.GetPrivateKeys(secretKey), nil
} }
@ -102,7 +94,7 @@ func (loader *kmsSingleBlsLoader) getKmsClientProvider() (kmsProvider, error) {
type blsDirLoader struct { type blsDirLoader struct {
// input fields // input fields
dirPath string dirPath string
passProviderConfig passDecrypterConfig
kmsProviderConfig kmsProviderConfig
// providers in process // providers in process

@ -9,10 +9,8 @@ import (
) )
func LoadKeys(cfg Config) (multibls.PrivateKeys, error) { func LoadKeys(cfg Config) (multibls.PrivateKeys, error) {
fmt.Println("start")
cfg.applyDefault() cfg.applyDefault()
if err := cfg.validate(); err != nil { if err := cfg.validate(); err != nil {
fmt.Println("validate")
return multibls.PrivateKeys{}, err return multibls.PrivateKeys{}, err
} }
helper, err := getHelper(cfg) helper, err := getHelper(cfg)
@ -94,8 +92,8 @@ func (cfg *Config) validate() error {
return cfg.getKmsProviderConfig().validate() return cfg.getKmsProviderConfig().validate()
} }
func (cfg *Config) getPassProviderConfig() passProviderConfig { func (cfg *Config) getPassProviderConfig() passDecrypterConfig {
return passProviderConfig{ return passDecrypterConfig{
passSrcType: cfg.PassSrcType, passSrcType: cfg.PassSrcType,
passFile: cfg.PassFile, passFile: cfg.PassFile,
persistPassphrase: cfg.PersistPassphrase, persistPassphrase: cfg.PersistPassphrase,
@ -117,8 +115,8 @@ func getHelper(cfg Config) (loadHelper, error) {
case basicKeyExt: case basicKeyExt:
fmt.Println("basic") fmt.Println("basic")
return &basicSingleBlsLoader{ return &basicSingleBlsLoader{
blsKeyFile: *cfg.BlsKeyFile, blsKeyFile: *cfg.BlsKeyFile,
passProviderConfig: cfg.getPassProviderConfig(), passDecrypterConfig: cfg.getPassProviderConfig(),
}, nil }, nil
case kmsKeyExt: case kmsKeyExt:
fmt.Println("kms") fmt.Println("kms")
@ -131,9 +129,9 @@ func getHelper(cfg Config) (loadHelper, error) {
} }
case stringIsSet(cfg.BlsDir): case stringIsSet(cfg.BlsDir):
return &blsDirLoader{ return &blsDirLoader{
dirPath: *cfg.BlsDir, dirPath: *cfg.BlsDir,
passProviderConfig: cfg.getPassProviderConfig(), passDecrypterConfig: cfg.getPassProviderConfig(),
kmsProviderConfig: cfg.getKmsProviderConfig(), kmsProviderConfig: cfg.getKmsProviderConfig(),
}, nil }, nil
default: default:
return nil, errors.New("either BlsKeyFile or BlsDir must be set") return nil, errors.New("either BlsKeyFile or BlsDir must be set")

@ -3,6 +3,8 @@ package blsloader
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"path/filepath"
"strings" "strings"
"sync" "sync"
"testing" "testing"
@ -11,8 +13,28 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/multibls"
dircopy "github.com/otiai10/copy"
) )
func init() {
// Move the test data to temp directory
os.RemoveAll(testDir)
if err := os.MkdirAll(testDir, 0777); err != nil {
fmt.Println("makedir error", err)
}
info, exist := os.Stat(testDir)
fmt.Println("testDir", info, exist)
testDataFolder := "testdata"
fmt.Println(testDir)
if err := dircopy.Copy(testDataFolder, testDir); err != nil {
panic(fmt.Sprintf("failed to copy dir: %v", err))
}
fmt.Println(testDir)
}
var testDir = filepath.Join(os.TempDir(), "harmony/BlsLoader")
type testKey struct { type testKey struct {
publicKey string publicKey string
privateKey string privateKey string
@ -28,16 +50,16 @@ var validTestKeys = []testKey{
publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500",
privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473",
passphrase: "", passphrase: "",
passFile: "testData/blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", passFile: "blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass",
path: "testData/blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", path: "blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key",
}, },
{ {
// key with non empty passphrase // key with non empty passphrase
publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083",
privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56",
passphrase: "harmony", passphrase: "harmony",
passFile: "testData/blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", passFile: "blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass",
path: "testData/blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", path: "blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key",
}, },
} }
@ -48,14 +70,14 @@ var emptyPassTestKeys = []testKey{
publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500",
privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473",
passphrase: "", passphrase: "",
path: "testData/blskey_emptypass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", path: "blskey_emptypass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key",
}, },
{ {
// key with non empty passphrase // key with non empty passphrase
publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083",
privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56",
passphrase: "harmony", passphrase: "harmony",
path: "testData/blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", path: "blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key",
}, },
} }
@ -66,16 +88,16 @@ var wrongPassTestKeys = []testKey{
publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500",
privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473",
passphrase: "evil harmony", passphrase: "evil harmony",
passFile: "testData/blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", passFile: "blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass",
path: "testData/blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", path: "blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key",
}, },
{ {
// key with non empty passphrase // key with non empty passphrase
publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083",
privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56",
passphrase: "harmony", passphrase: "harmony",
passFile: "testData/blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", passFile: "blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass",
path: "testData/blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", path: "blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key",
}, },
} }
@ -84,7 +106,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
cfg Config cfg Config
inputs []string inputs []string
expOutputs []string expOutSubs []string
expPubKeys []string expPubKeys []string
expErr error expErr error
}{ }{
@ -96,9 +118,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
}, },
inputs: []string{}, inputs: []string{},
expOutputs: []string{ expOutSubs: []string{},
fmt.Sprintf("loaded bls key %s\n", validTestKeys[0].publicKey),
},
expPubKeys: []string{validTestKeys[0].publicKey}, expPubKeys: []string{validTestKeys[0].publicKey},
}, },
{ {
@ -109,9 +129,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
}, },
inputs: []string{}, inputs: []string{},
expOutputs: []string{ expOutSubs: []string{},
fmt.Sprintf("loaded bls key %s\n", validTestKeys[1].publicKey),
},
expPubKeys: []string{validTestKeys[1].publicKey}, expPubKeys: []string{validTestKeys[1].publicKey},
}, },
{ {
@ -122,9 +140,8 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
}, },
inputs: []string{validTestKeys[1].passphrase}, inputs: []string{validTestKeys[1].passphrase},
expOutputs: []string{ expOutSubs: []string{
fmt.Sprintf("Enter passphrase for the BLS key file %s:", validTestKeys[1].path), fmt.Sprintf("Enter passphrase for the BLS key file %s:", validTestKeys[1].path),
fmt.Sprintf("loaded bls key %s\n", validTestKeys[1].publicKey),
}, },
expPubKeys: []string{validTestKeys[1].publicKey}, expPubKeys: []string{validTestKeys[1].publicKey},
}, },
@ -136,9 +153,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
}, },
inputs: []string{}, inputs: []string{},
expOutputs: []string{ expOutSubs: []string{},
fmt.Sprintf("loaded bls key %s\n", validTestKeys[1].publicKey),
},
expPubKeys: []string{validTestKeys[1].publicKey}, expPubKeys: []string{validTestKeys[1].publicKey},
}, },
{ {
@ -149,10 +164,9 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
}, },
inputs: []string{emptyPassTestKeys[1].passphrase}, inputs: []string{emptyPassTestKeys[1].passphrase},
expOutputs: []string{ expOutSubs: []string{
"unable to get passphrase from passphrase file testData/blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass: cannot open passphrase file\n", "unable to get passphrase",
fmt.Sprintf("Enter passphrase for the BLS key file %s:", emptyPassTestKeys[1].path), fmt.Sprintf("Enter passphrase for the BLS key file %s:", emptyPassTestKeys[1].path),
fmt.Sprintf("loaded bls key %s\n", emptyPassTestKeys[1].publicKey),
}, },
expPubKeys: []string{emptyPassTestKeys[1].publicKey}, expPubKeys: []string{emptyPassTestKeys[1].publicKey},
}, },
@ -161,7 +175,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) {
ts := &testSuite{ ts := &testSuite{
cfg: test.cfg, cfg: test.cfg,
inputs: test.inputs, inputs: test.inputs,
expOutputs: test.expOutputs, expOutSub: test.expOutSubs,
expErr: test.expErr, expErr: test.expErr,
expPubKeys: test.expPubKeys, expPubKeys: test.expPubKeys,
} }
@ -191,7 +205,7 @@ type testSuite struct {
cfg Config cfg Config
inputs []string inputs []string
expOutputs []string expOutSub []string
expPubKeys []string expPubKeys []string
expErr error expErr error
@ -206,7 +220,7 @@ type testSuite struct {
} }
func (ts *testSuite) init() { func (ts *testSuite) init() {
ts.gotOutputs = make([]string, 0, len(ts.expOutputs)) ts.gotOutputs = make([]string, 0, len(ts.expOutSub))
ts.console = newTestConsole() ts.console = newTestConsole()
setTestConsole(ts.console) setTestConsole(ts.console)
ts.timeout = 1 * time.Second ts.timeout = 1 * time.Second
@ -232,7 +246,7 @@ func (ts *testSuite) checkResult() error {
default: default:
} }
fmt.Println("got outputs:", ts.gotOutputs) fmt.Println("got outputs:", ts.gotOutputs)
fmt.Println("expect outputs:", ts.expOutputs) fmt.Println("expect outputs:", ts.expOutSub)
if isClean, msg := ts.console.checkClean(); !isClean { if isClean, msg := ts.console.checkClean(); !isClean {
return fmt.Errorf("console not clean: %v", msg) return fmt.Errorf("console not clean: %v", msg)
} }
@ -249,12 +263,12 @@ func (ts *testSuite) checkResult() error {
} }
func (ts *testSuite) checkOutputs() error { func (ts *testSuite) checkOutputs() error {
if len(ts.gotOutputs) != len(ts.expOutputs) { if len(ts.gotOutputs) != len(ts.expOutSub) {
return fmt.Errorf("output size not expected: %v / %v", len(ts.gotOutputs), len(ts.expOutputs)) return fmt.Errorf("output size not expected: %v / %v", len(ts.gotOutputs), len(ts.expOutSub))
} }
for i, gotOutput := range ts.gotOutputs { for i, gotOutput := range ts.gotOutputs {
expOutput := ts.expOutputs[i] expOutput := ts.expOutSub[i]
if gotOutput != expOutput { if !strings.Contains(gotOutput, expOutput) {
return fmt.Errorf("%vth output unexpected: [%v] / [%v]", i, gotOutput, expOutput) return fmt.Errorf("%vth output unexpected: [%v] / [%v]", i, gotOutput, expOutput)
} }
} }
@ -313,7 +327,7 @@ func (ts *testSuite) threadedLoadOutputs() {
var ( var (
i = 0 i = 0
) )
for i < len(ts.expOutputs) { for i < len(ts.expOutSub) {
select { select {
case got := <-ts.console.Out: case got := <-ts.console.Out:
ts.gotOutputs = append(ts.gotOutputs, got) ts.gotOutputs = append(ts.gotOutputs, got)

@ -20,29 +20,6 @@ const (
defWritePassFileMode = 0600 defWritePassFileMode = 0600
) )
// PassSrcType is the type of passphrase provider source.
// Three options available:
// PassSrcFile - Read the passphrase from file
// PassSrcPrompt - Read the passphrase from prompt
// PassSrcPrompt - First try to unlock with passphrase from file, then read passphrase from prompt
type PassSrcType uint8
const (
PassSrcNil PassSrcType = iota // place holder for nil src
PassSrcAuto // first try to unlock with pass from file, then look for prompt
PassSrcFile // provide the passphrase through a pass file
PassSrcPrompt // provide the passphrase through prompt
)
func (srcType PassSrcType) isValid() bool {
switch srcType {
case PassSrcAuto, PassSrcFile, PassSrcPrompt:
return true
default:
return false
}
}
// AwsConfigSrcType is the type of src to load aws config. Two options available // AwsConfigSrcType is the type of src to load aws config. Two options available
// AwsCfgSrcFile - Provide the aws config through a file (json). // AwsCfgSrcFile - Provide the aws config through a file (json).
// AwsCfgSrcPrompt - Provide the aws config though prompt. // AwsCfgSrcPrompt - Provide the aws config though prompt.

@ -5,76 +5,153 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"sync"
"github.com/harmony-one/harmony/crypto/bls"
bls_core "github.com/harmony-one/bls/ffi/go/bls"
) )
// passProviderConfig is the data structure of passProviders config // PassSrcType is the type of passphrase provider source.
type passProviderConfig struct { // Three options available:
// PassSrcFile - Read the passphrase from files
// PassSrcPrompt - Read the passphrase from prompt
// PassSrcAuto - First try to unlock with passphrase from file, then read passphrase from prompt
type PassSrcType uint8
const (
PassSrcNil PassSrcType = iota // place holder for nil src
PassSrcFile // provide the passphrase through pass files
PassSrcPrompt // provide the passphrase through prompt
PassSrcAuto // first try to unlock with pass from file, then look for prompt
)
func (srcType PassSrcType) isValid() bool {
switch srcType {
case PassSrcAuto, PassSrcFile, PassSrcPrompt:
return true
default:
return false
}
}
type passDecrypter struct {
pps []passProvider
}
func newPassDecrypter(cfg passDecrypterConfig) *passDecrypter {
pps := cfg.makePassProviders()
return &passDecrypter{pps}
}
func (pd *passDecrypter) decrypt(keyFile string) (*bls_core.SecretKey, error) {
for _, pp := range pd.pps {
}
}
func (pd *passDecrypter) checkDecryptResult(keyFile string, got *bls_core.SecretKey) error {
expPubKey, err := getPubKeyFromFilePath(keyFile, passExt)
if err != nil {
if err == errUnableGetPubkey {
// file name not bls pub key + .pass
return nil
}
return err
}
gotPubKey := *bls.FromLibBLSPublicKeyUnsafe(got.GetPublicKey())
if expPubKey != gotPubKey {
return errors.New("public key unexpected")
}
return nil
}
// passDecrypterConfig is the data structure of passProviders config
type passDecrypterConfig struct {
passSrcType PassSrcType passSrcType PassSrcType
passFile *string passFile *string
passDir *string
persistPassphrase bool persistPassphrase bool
} }
func (config passProviderConfig) validate() error { func (config passDecrypterConfig) validate() error {
if !config.passSrcType.isValid() { if !config.passSrcType.isValid() {
return errors.New("unknown PassSrcType") return errors.New("unknown PassSrcType")
} }
return nil return nil
} }
func (config passDecrypterConfig) makePassProviders() []passProvider {
switch config.passSrcType {
case PassSrcFile:
return []passProvider{config.getFilePassProvider()}
case PassSrcPrompt:
return []passProvider{config.getPromptPassProvider()}
case PassSrcAuto:
return []passProvider{
config.getFilePassProvider(),
config.getPromptPassProvider(),
}
}
}
func (config passDecrypterConfig) getFilePassProvider() passProvider {
switch {
case stringIsSet(config.passFile):
return newStaticPassProvider(*config.passFile)
case stringIsSet(config.passDir):
return newDirPassProvider(*config.passDir)
default:
return newDynamicPassProvider()
}
}
func (config passDecrypterConfig) getPromptPassProvider() passProvider {
return newPromptPassProvider(config.persistPassphrase)
}
// passProvider is the interface to provide the passphrase of a bls keys. // passProvider is the interface to provide the passphrase of a bls keys.
// Implemented by // Implemented by
// promptPassProvider - provide passphrase through user-interactive prompt // promptPassProvider - provide passphrase through user-interactive prompt
// filePassProvider - provide passphrase from a .pass file // staticPassProvider - provide passphrase from a static .pass file
// dirPassProvider - provide passphrase from .pass files in a directory // dynamicPassProvider - provide the passphrase based on the given key file path
// dirPassProvider - provide passphrase from .pass files in a directory
type passProvider interface { type passProvider interface {
toStr() string
getPassphrase(keyFile string) (string, error) getPassphrase(keyFile string) (string, error)
} }
// promptPassProvider provides the bls password through console prompt. // promptPassProvider provides the bls password through console prompt.
type promptPassProvider struct { type promptPassProvider struct {
// if enablePersist is true, after user enter the passphrase, the // if enablePersist is true, after user enter the passphrase, the
// passphrase is also persisted into the persistDir // passphrase is also persisted into .pass file under the same directory
// of the key file
enablePersist bool enablePersist bool
persistDir string
} }
const pwdPromptStr = "Enter passphrase for the BLS key file %s:" const pwdPromptStr = "Enter passphrase for the BLS key file %s:"
func newPromptPassProvider() *promptPassProvider { func newPromptPassProvider(enablePersist bool) *promptPassProvider {
return &promptPassProvider{} return &promptPassProvider{enablePersist: enablePersist}
}
func (provider *promptPassProvider) toStr() string {
return "prompt"
}
func (provider *promptPassProvider) setPersist(dirPath string) *promptPassProvider {
provider.enablePersist = true
os.MkdirAll(dirPath, defWritePassDirMode)
provider.persistDir = dirPath
return provider
} }
func (provider *promptPassProvider) getPassphrase(keyFile string) (string, error) { func (provider *promptPassProvider) getPassphrase(keyFile string) (string, error) {
prompt := fmt.Sprintf(pwdPromptStr, keyFile) prompt := fmt.Sprintf(pwdPromptStr, keyFile)
pass, err := promptGetPassword(prompt) pass, err := promptGetPassword(prompt)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("unable to read from prompt: %v", err)
} }
// If user set to persist the pass file, persist to .pass file
if provider.enablePersist { if provider.enablePersist {
if err := provider.persistPassphrase(keyFile, pass); err != nil { if err := provider.persistPassphrase(keyFile, pass); err != nil {
return "", err return "", fmt.Errorf("unable to save passphrase: %v", err)
} }
} }
return pass, nil return pass, nil
} }
func (provider *promptPassProvider) persistPassphrase(keyFile string, passPhrase string) error { func (provider *promptPassProvider) persistPassphrase(keyFile string, passPhrase string) error {
passFile := filepath.Join(provider.persistDir, filepath.Base(keyFile)) passFile := keyFileToPassFileFull(keyFile)
if _, err := os.Stat(passFile); err == nil { if _, err := os.Stat(passFile); err == nil {
// File exist. Prompt user to overwrite pass file // File exist. Prompt user to overwrite pass file
overwrite, err := promptYesNo(fmt.Sprintf("pass file [%v] already exist. Overwrite? ", passFile)) overwrite, err := promptYesNo(fmt.Sprintf("pass file [%v] already exist. Overwrite? ", passFile))
@ -91,37 +168,42 @@ func (provider *promptPassProvider) persistPassphrase(keyFile string, passPhrase
return ioutil.WriteFile(passFile, []byte(passPhrase), defWritePassFileMode) return ioutil.WriteFile(passFile, []byte(passPhrase), defWritePassFileMode)
} }
// filePassProvider provide the bls password from the single bls pass file // staticPassProvider provide the bls password from a static .pass file
type filePassProvider struct { type staticPassProvider struct {
fileName string fileName string
// cached field
pass string pass string
err error
once sync.Once
} }
func newFilePassProvider(fileName string) *filePassProvider { func newStaticPassProvider(fileName string) *staticPassProvider {
return &filePassProvider{fileName: fileName} return &staticPassProvider{fileName: fileName}
} }
func (provider *filePassProvider) toStr() string { func (provider *staticPassProvider) getPassphrase(keyFile string) (string, error) {
return "passphrase file " + provider.fileName provider.once.Do(func() {
provider.pass, provider.err = readPassFromFile(provider.fileName)
})
return provider.pass, provider.err
} }
func (provider *filePassProvider) getPassphrase(keyFile string) (string, error) { // dynamicPassProvider provide the passphrase based on .pass file with the given
return readPassFromFile(provider.fileName) // key file path. For example, looking for key file xxx.key will provide the
} // passphrase from xxx.pass
type dynamicPassProvider struct{}
func readPassFromFile(file string) (string, error) { func newDynamicPassProvider() passProvider {
f, err := os.Open(file) return &dynamicPassProvider{}
if err != nil { }
return "", fmt.Errorf("cannot open passphrase file")
}
defer f.Close()
b, err := ioutil.ReadAll(f) func (provider *dynamicPassProvider) getPassphrase(keyFile string) (string, error) {
if err != nil { passFile := keyFileToPassFileFull(keyFile)
return "", err if !isPassFile(passFile) {
return "", fmt.Errorf("pass file %v not exist", passFile)
} }
return strings.TrimSpace(string(b)), nil return readPassFromFile(passFile)
} }
// dirPassProvider provide the all bls password available in the directory. // dirPassProvider provide the all bls password available in the directory.
@ -138,8 +220,23 @@ func newDirPassProvider(dirPath string) *dirPassProvider {
} }
func (provider *dirPassProvider) getPassphrase(keyFile string) (string, error) { func (provider *dirPassProvider) getPassphrase(keyFile string) (string, error) {
baseName := filepath.Base(keyFile) passFile := keyFileToPassFileFull(keyFile)
passKeyBase := keyFileToPassFileBase(baseName) if !isPassFile(passFile) {
passFile := filepath.Join(provider.dirPath, passKeyBase) return "", fmt.Errorf("pass file %v not exist", passFile)
}
return readPassFromFile(passFile) return readPassFromFile(passFile)
} }
func readPassFromFile(file string) (string, error) {
f, err := os.Open(file)
if err != nil {
return "", fmt.Errorf("cannot open passphrase file: %v", err)
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
return strings.TrimSpace(string(b)), nil
}

@ -4,8 +4,13 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/crypto/bls"
"github.com/pkg/errors" "github.com/pkg/errors"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
@ -14,6 +19,7 @@ import (
var ( var (
errUnknownExtension = errors.New("unknown extension") errUnknownExtension = errors.New("unknown extension")
errUnableGetPubkey = errors.New("unable to get public key")
errNilPassProvider = errors.New("no source for password") errNilPassProvider = errors.New("no source for password")
errNilKMSClientProvider = errors.New("no source for KMS provider") errNilKMSClientProvider = errors.New("no source for KMS provider")
) )
@ -88,6 +94,14 @@ func isBasicKeyFile(path string) bool {
return filepath.Ext(path) == basicKeyExt return filepath.Ext(path) == basicKeyExt
} }
func isPassFile(path string) bool {
exist := isFile(path)
if !exist {
return false
}
return filepath.Ext(path) == passExt
}
func isKMSKeyFile(path string) bool { func isKMSKeyFile(path string) bool {
exist := isFile(path) exist := isFile(path)
if !exist { if !exist {
@ -96,6 +110,25 @@ func isKMSKeyFile(path string) bool {
return filepath.Ext(path) == kmsKeyExt return filepath.Ext(path) == kmsKeyExt
} }
var regexFmt = `^[\da-f]{96}%s$`
func getPubKeyFromFilePath(path string, ext string) (bls.SerializedPublicKey, error) {
baseName := filepath.Base(path)
re, err := regexp.Compile(fmt.Sprintf(regexFmt, ext))
if err != nil {
return bls.SerializedPublicKey{}, err
}
res := re.FindAllStringSubmatch(baseName, 1)
if len(res) == 0 {
return bls.SerializedPublicKey{}, errUnableGetPubkey
}
b := common.Hex2Bytes(res[0][1])
var pubKey bls.SerializedPublicKey
copy(pubKey[:], b)
return pubKey, nil
}
func keyFileToPassFileBase(keyFileBase string) string { func keyFileToPassFileBase(keyFileBase string) string {
return strings.Trim(keyFileBase, basicKeyExt) + passExt return strings.Trim(keyFileBase, basicKeyExt) + passExt
} }

Loading…
Cancel
Save