diff --git a/cmd/harmony/blsloader/decrypter.go b/cmd/harmony/blsloader/decrypter.go new file mode 100644 index 000000000..724c3b907 --- /dev/null +++ b/cmd/harmony/blsloader/decrypter.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) +} diff --git a/cmd/harmony/blsloader/helper.go b/cmd/harmony/blsloader/helper.go index c5b300c71..8f89012ea 100644 --- a/cmd/harmony/blsloader/helper.go +++ b/cmd/harmony/blsloader/helper.go @@ -6,8 +6,6 @@ import ( "os" "path/filepath" - "github.com/harmony-one/harmony/crypto/bls" - bls_core "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/multibls" ) @@ -21,25 +19,19 @@ type loadHelper interface { type basicSingleBlsLoader struct { blsKeyFile string - passProviderConfig + passDecrypterConfig } // loadKeys load bls keys from a single bls file func (loader *basicSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) { - fmt.Println("load keys") providers, err := loader.getPassProviders() if err != nil { - fmt.Println("not loaded 1") return multibls.PrivateKeys{}, err } - fmt.Println("provider got") secretKey, err := loadBasicKey(loader.blsKeyFile, providers) if err != nil { - fmt.Println("not loaded 2") 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 } @@ -102,7 +94,7 @@ func (loader *kmsSingleBlsLoader) getKmsClientProvider() (kmsProvider, error) { type blsDirLoader struct { // input fields dirPath string - passProviderConfig + passDecrypterConfig kmsProviderConfig // providers in process diff --git a/cmd/harmony/blsloader/loader.go b/cmd/harmony/blsloader/loader.go index 563bb1bbf..daa9d1c15 100644 --- a/cmd/harmony/blsloader/loader.go +++ b/cmd/harmony/blsloader/loader.go @@ -9,10 +9,8 @@ import ( ) func LoadKeys(cfg Config) (multibls.PrivateKeys, error) { - fmt.Println("start") cfg.applyDefault() if err := cfg.validate(); err != nil { - fmt.Println("validate") return multibls.PrivateKeys{}, err } helper, err := getHelper(cfg) @@ -94,8 +92,8 @@ func (cfg *Config) validate() error { return cfg.getKmsProviderConfig().validate() } -func (cfg *Config) getPassProviderConfig() passProviderConfig { - return passProviderConfig{ +func (cfg *Config) getPassProviderConfig() passDecrypterConfig { + return passDecrypterConfig{ passSrcType: cfg.PassSrcType, passFile: cfg.PassFile, persistPassphrase: cfg.PersistPassphrase, @@ -117,8 +115,8 @@ func getHelper(cfg Config) (loadHelper, error) { case basicKeyExt: fmt.Println("basic") return &basicSingleBlsLoader{ - blsKeyFile: *cfg.BlsKeyFile, - passProviderConfig: cfg.getPassProviderConfig(), + blsKeyFile: *cfg.BlsKeyFile, + passDecrypterConfig: cfg.getPassProviderConfig(), }, nil case kmsKeyExt: fmt.Println("kms") @@ -131,9 +129,9 @@ func getHelper(cfg Config) (loadHelper, error) { } case stringIsSet(cfg.BlsDir): return &blsDirLoader{ - dirPath: *cfg.BlsDir, - passProviderConfig: cfg.getPassProviderConfig(), - kmsProviderConfig: cfg.getKmsProviderConfig(), + dirPath: *cfg.BlsDir, + passDecrypterConfig: cfg.getPassProviderConfig(), + kmsProviderConfig: cfg.getKmsProviderConfig(), }, nil default: return nil, errors.New("either BlsKeyFile or BlsDir must be set") diff --git a/cmd/harmony/blsloader/loader_test.go b/cmd/harmony/blsloader/loader_test.go index 2ee0240a1..0070a04ad 100644 --- a/cmd/harmony/blsloader/loader_test.go +++ b/cmd/harmony/blsloader/loader_test.go @@ -3,6 +3,8 @@ package blsloader import ( "errors" "fmt" + "os" + "path/filepath" "strings" "sync" "testing" @@ -11,8 +13,28 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/harmony-one/harmony/crypto/bls" "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 { publicKey string privateKey string @@ -28,16 +50,16 @@ var validTestKeys = []testKey{ publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", passphrase: "", - passFile: "testData/blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", - path: "testData/blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", + passFile: "blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", + path: "blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", }, { // key with non empty passphrase publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", passphrase: "harmony", - passFile: "testData/blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", - path: "testData/blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", + passFile: "blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", + path: "blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", }, } @@ -48,14 +70,14 @@ var emptyPassTestKeys = []testKey{ publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", passphrase: "", - path: "testData/blskey_emptypass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", + path: "blskey_emptypass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", }, { // key with non empty passphrase publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", passphrase: "harmony", - path: "testData/blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", + path: "blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", }, } @@ -66,16 +88,16 @@ var wrongPassTestKeys = []testKey{ publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", passphrase: "evil harmony", - passFile: "testData/blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", - path: "testData/blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", + passFile: "blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", + path: "blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", }, { // key with non empty passphrase publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", passphrase: "harmony", - passFile: "testData/blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", - path: "testData/blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", + passFile: "blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", + path: "blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", }, } @@ -84,7 +106,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { cfg Config inputs []string - expOutputs []string + expOutSubs []string expPubKeys []string expErr error }{ @@ -96,9 +118,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { }, inputs: []string{}, - expOutputs: []string{ - fmt.Sprintf("loaded bls key %s\n", validTestKeys[0].publicKey), - }, + expOutSubs: []string{}, expPubKeys: []string{validTestKeys[0].publicKey}, }, { @@ -109,9 +129,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { }, inputs: []string{}, - expOutputs: []string{ - fmt.Sprintf("loaded bls key %s\n", validTestKeys[1].publicKey), - }, + expOutSubs: []string{}, expPubKeys: []string{validTestKeys[1].publicKey}, }, { @@ -122,9 +140,8 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { }, inputs: []string{validTestKeys[1].passphrase}, - expOutputs: []string{ + expOutSubs: []string{ 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}, }, @@ -136,9 +153,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { }, inputs: []string{}, - expOutputs: []string{ - fmt.Sprintf("loaded bls key %s\n", validTestKeys[1].publicKey), - }, + expOutSubs: []string{}, expPubKeys: []string{validTestKeys[1].publicKey}, }, { @@ -149,10 +164,9 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { }, inputs: []string{emptyPassTestKeys[1].passphrase}, - expOutputs: []string{ - "unable to get passphrase from passphrase file testData/blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass: cannot open passphrase file\n", + expOutSubs: []string{ + "unable to get passphrase", 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}, }, @@ -161,7 +175,7 @@ func TestLoadKeys_SingleBls_File(t *testing.T) { ts := &testSuite{ cfg: test.cfg, inputs: test.inputs, - expOutputs: test.expOutputs, + expOutSub: test.expOutSubs, expErr: test.expErr, expPubKeys: test.expPubKeys, } @@ -191,7 +205,7 @@ type testSuite struct { cfg Config inputs []string - expOutputs []string + expOutSub []string expPubKeys []string expErr error @@ -206,7 +220,7 @@ type testSuite struct { } func (ts *testSuite) init() { - ts.gotOutputs = make([]string, 0, len(ts.expOutputs)) + ts.gotOutputs = make([]string, 0, len(ts.expOutSub)) ts.console = newTestConsole() setTestConsole(ts.console) ts.timeout = 1 * time.Second @@ -232,7 +246,7 @@ func (ts *testSuite) checkResult() error { default: } 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 { return fmt.Errorf("console not clean: %v", msg) } @@ -249,12 +263,12 @@ func (ts *testSuite) checkResult() error { } func (ts *testSuite) checkOutputs() error { - if len(ts.gotOutputs) != len(ts.expOutputs) { - return fmt.Errorf("output size not expected: %v / %v", 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.expOutSub)) } for i, gotOutput := range ts.gotOutputs { - expOutput := ts.expOutputs[i] - if gotOutput != expOutput { + expOutput := ts.expOutSub[i] + if !strings.Contains(gotOutput, expOutput) { return fmt.Errorf("%vth output unexpected: [%v] / [%v]", i, gotOutput, expOutput) } } @@ -313,7 +327,7 @@ func (ts *testSuite) threadedLoadOutputs() { var ( i = 0 ) - for i < len(ts.expOutputs) { + for i < len(ts.expOutSub) { select { case got := <-ts.console.Out: ts.gotOutputs = append(ts.gotOutputs, got) diff --git a/cmd/harmony/blsloader/params.go b/cmd/harmony/blsloader/params.go index 841ed0859..653330bfe 100644 --- a/cmd/harmony/blsloader/params.go +++ b/cmd/harmony/blsloader/params.go @@ -20,29 +20,6 @@ const ( 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 // AwsCfgSrcFile - Provide the aws config through a file (json). // AwsCfgSrcPrompt - Provide the aws config though prompt. diff --git a/cmd/harmony/blsloader/passProvider.go b/cmd/harmony/blsloader/passProvider.go index 5eacca149..62a735114 100644 --- a/cmd/harmony/blsloader/passProvider.go +++ b/cmd/harmony/blsloader/passProvider.go @@ -5,76 +5,153 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "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 -type passProviderConfig struct { +// PassSrcType is the type of passphrase provider source. +// 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 passFile *string + passDir *string persistPassphrase bool } -func (config passProviderConfig) validate() error { +func (config passDecrypterConfig) validate() error { if !config.passSrcType.isValid() { return errors.New("unknown PassSrcType") } 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. // Implemented by -// promptPassProvider - provide passphrase through user-interactive prompt -// filePassProvider - provide passphrase from a .pass file -// dirPassProvider - provide passphrase from .pass files in a directory +// promptPassProvider - provide passphrase through user-interactive prompt +// staticPassProvider - provide passphrase from a static .pass file +// dynamicPassProvider - provide the passphrase based on the given key file path +// dirPassProvider - provide passphrase from .pass files in a directory type passProvider interface { - toStr() string getPassphrase(keyFile string) (string, error) } // promptPassProvider provides the bls password through console prompt. type promptPassProvider struct { // 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 - persistDir string } const pwdPromptStr = "Enter passphrase for the BLS key file %s:" -func newPromptPassProvider() *promptPassProvider { - return &promptPassProvider{} -} - -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 newPromptPassProvider(enablePersist bool) *promptPassProvider { + return &promptPassProvider{enablePersist: enablePersist} } func (provider *promptPassProvider) getPassphrase(keyFile string) (string, error) { prompt := fmt.Sprintf(pwdPromptStr, keyFile) pass, err := promptGetPassword(prompt) 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 err := provider.persistPassphrase(keyFile, pass); err != nil { - return "", err + return "", fmt.Errorf("unable to save passphrase: %v", err) } } return pass, nil } 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 { // File exist. Prompt user to overwrite pass file 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) } -// filePassProvider provide the bls password from the single bls pass file -type filePassProvider struct { +// staticPassProvider provide the bls password from a static .pass file +type staticPassProvider struct { fileName string + // cached field pass string + err error + once sync.Once } -func newFilePassProvider(fileName string) *filePassProvider { - return &filePassProvider{fileName: fileName} +func newStaticPassProvider(fileName string) *staticPassProvider { + return &staticPassProvider{fileName: fileName} } -func (provider *filePassProvider) toStr() string { - return "passphrase file " + provider.fileName +func (provider *staticPassProvider) getPassphrase(keyFile string) (string, error) { + provider.once.Do(func() { + provider.pass, provider.err = readPassFromFile(provider.fileName) + }) + return provider.pass, provider.err } -func (provider *filePassProvider) getPassphrase(keyFile string) (string, error) { - return readPassFromFile(provider.fileName) -} +// dynamicPassProvider provide the passphrase based on .pass file with the given +// 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) { - f, err := os.Open(file) - if err != nil { - return "", fmt.Errorf("cannot open passphrase file") - } - defer f.Close() +func newDynamicPassProvider() passProvider { + return &dynamicPassProvider{} +} - b, err := ioutil.ReadAll(f) - if err != nil { - return "", err +func (provider *dynamicPassProvider) getPassphrase(keyFile string) (string, error) { + passFile := keyFileToPassFileFull(keyFile) + 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. @@ -138,8 +220,23 @@ func newDirPassProvider(dirPath string) *dirPassProvider { } func (provider *dirPassProvider) getPassphrase(keyFile string) (string, error) { - baseName := filepath.Base(keyFile) - passKeyBase := keyFileToPassFileBase(baseName) - passFile := filepath.Join(provider.dirPath, passKeyBase) + passFile := keyFileToPassFileFull(keyFile) + if !isPassFile(passFile) { + return "", fmt.Errorf("pass file %v not exist", 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 +} diff --git a/cmd/harmony/blsloader/utils.go b/cmd/harmony/blsloader/utils.go index 9e97344f2..85a786d28 100644 --- a/cmd/harmony/blsloader/utils.go +++ b/cmd/harmony/blsloader/utils.go @@ -4,8 +4,13 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strings" + "github.com/ethereum/go-ethereum/common" + + "github.com/harmony-one/harmony/crypto/bls" + "github.com/pkg/errors" bls_core "github.com/harmony-one/bls/ffi/go/bls" @@ -14,6 +19,7 @@ import ( var ( errUnknownExtension = errors.New("unknown extension") + errUnableGetPubkey = errors.New("unable to get public key") errNilPassProvider = errors.New("no source for password") errNilKMSClientProvider = errors.New("no source for KMS provider") ) @@ -88,6 +94,14 @@ func isBasicKeyFile(path string) bool { 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 { exist := isFile(path) if !exist { @@ -96,6 +110,25 @@ func isKMSKeyFile(path string) bool { 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 { return strings.Trim(keyFileBase, basicKeyExt) + passExt }