diff --git a/cmd/harmony/blsloader/kmsProvider.go b/cmd/harmony/blsloader/kmsProvider.go index 2ef733628..a7cf1ac29 100644 --- a/cmd/harmony/blsloader/kmsProvider.go +++ b/cmd/harmony/blsloader/kmsProvider.go @@ -125,9 +125,6 @@ type promptKMSProvider struct { } func newPromptKMSProvider(timeout time.Duration) *promptKMSProvider { - if timeout == 0 { - timeout = defPromptTimeout - } return &promptKMSProvider{ baseKMSProvider: baseKMSProvider{}, timeout: timeout, @@ -157,6 +154,7 @@ func (provider *promptKMSProvider) getAWSConfig() (*AwsConfig, error) { } // prompt prompt the user to input a string for a certain field with timeout. +// TODO(Jacky): refactor this into a prompt structure func (provider *promptKMSProvider) prompt(hint string) (string, error) { var ( res string diff --git a/cmd/harmony/blsloader/loader.go b/cmd/harmony/blsloader/loader.go index 6cf0f1ba5..b07e6ee12 100644 --- a/cmd/harmony/blsloader/loader.go +++ b/cmd/harmony/blsloader/loader.go @@ -1,4 +1,123 @@ package blsloader +import ( + "errors" + + ffibls "github.com/harmony-one/bls/ffi/go/bls" + "github.com/harmony-one/harmony/multibls" +) + type Loader struct { + BlsKeyFile *string + AwsBlsKey *string + BlsDir *string + BlsPassFile *string + AwsConfigFile *string + UsePromptAwsConfig bool + UsePromptPassword bool + PersistPassphrase bool +} + +func (loader *Loader) LoadKeys() (multibls.PrivateKey, error) { + switch { + case stringIsSet(loader.BlsKeyFile): + return loader.loadSingleBasicBlsKey() + case stringIsSet(loader.AwsBlsKey): + return loader.loadSingleKmsBlsKey() + case stringIsSet(loader.BlsDir): + return loader.loadDirBlsKeys() + default: + return multibls.PrivateKey{}, errors.New("empty bls key data source") + } +} + +func (loader *Loader) loadSingleBasicBlsKey() (multibls.PrivateKey, error) { + provider := loader.getPassProviderSingleBasic() + secretKey, err := loadBasicKeyFromFile(*loader.BlsPassFile, provider) + if err != nil { + return multibls.PrivateKey{}, err + } + return secretKeyToMultiPrivateKey(secretKey), nil +} + +func (loader *Loader) getPassProviderSingleBasic() passProvider { + switch { + case stringIsSet(loader.BlsPassFile): + return newFilePassProvider(*loader.BlsPassFile) + default: + // TODO(Jacky): multi pass provider + return newPromptPassProvider() + } +} + +func (loader *Loader) loadSingleKmsBlsKey() (multibls.PrivateKey, error) { + provider := loader.getKmsProviderSingleKms() + secretKey, err := loadKMSKeyFromFile(*loader.AwsBlsKey, provider) + if err != nil { + return multibls.PrivateKey{}, err + } + return secretKeyToMultiPrivateKey(secretKey), nil +} + +func (loader *Loader) getKmsProviderSingleKms() kmsClientProvider { + switch { + case loader.UsePromptAwsConfig: + return newPromptKMSProvider(defKmsPromptTimeout) + case stringIsSet(loader.AwsConfigFile): + return newFileKMSProvider(*loader.AwsConfigFile) + default: + return newSharedKMSProvider() + } +} + +func (loader *Loader) loadDirBlsKeys() (multibls.PrivateKey, error) { + pp := loader.getPassProviderDirKeys() + kcp := loader.getKmsClientProviderDirKeys() + + helper := &blsDirLoadHelper{ + dirPath: *loader.BlsDir, + pp: pp, + kcp: kcp, + } + return helper.getKeyFilesFromDir() +} + +func (loader *Loader) getPassProviderDirKeys() passProvider { + switch { + case stringIsSet(loader.BlsPassFile): + return newFilePassProvider(*loader.BlsPassFile) + case loader.UsePromptPassword: + return newPromptPassProvider() + default: + if loader.PersistPassphrase { + return multiPassProvider{ + newDirPassProvider(*loader.BlsDir), + newPromptPassProvider(). + setPersist(*loader.BlsDir, defWritePassFileMode), + } + } + return multiPassProvider{ + newDirPassProvider(*loader.BlsDir), + newPromptPassProvider(), + } + } +} + +func (loader *Loader) getKmsClientProviderDirKeys() kmsClientProvider { + switch { + case stringIsSet(loader.AwsConfigFile): + return newFileKMSProvider(*loader.AwsConfigFile) + case loader.UsePromptAwsConfig: + return newPromptKMSProvider(defKmsPromptTimeout) + default: + return newSharedKMSProvider() + } +} + +func secretKeyToMultiPrivateKey(secretKeys ...*ffibls.SecretKey) multibls.PrivateKey { + return multibls.PrivateKey{PrivateKey: secretKeys} +} + +func stringIsSet(val *string) bool { + return val != nil && *val != "" } diff --git a/cmd/harmony/blsloader/params.go b/cmd/harmony/blsloader/params.go index 8f7923651..c6bc8ec98 100644 --- a/cmd/harmony/blsloader/params.go +++ b/cmd/harmony/blsloader/params.go @@ -9,7 +9,8 @@ const ( ) const ( - defPromptTimeout = 1 * time.Second + defKmsPromptTimeout = 1 * time.Second + defPassPromptTimeout = 10 * time.Second ) const ( diff --git a/cmd/harmony/blsloader/passProvider.go b/cmd/harmony/blsloader/passProvider.go index e67a9258a..6330cd56f 100644 --- a/cmd/harmony/blsloader/passProvider.go +++ b/cmd/harmony/blsloader/passProvider.go @@ -54,14 +54,11 @@ func (provider *promptPassProvider) getPassphrase(keyFile string) (string, error } } return pass, nil - } func (provider *promptPassProvider) persistPassphrase(keyFile string, passPhrase string) error { passFile := filepath.Join(provider.persistDir, filepath.Base(keyFile)) - if _, err := os.Stat(passFile); os.IsNotExist(err) { - // file not exist. Go on create the file - } else if err == nil { + 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)) if err != nil { @@ -70,6 +67,9 @@ func (provider *promptPassProvider) persistPassphrase(keyFile string, passPhrase if !overwrite { return nil } + } else if !os.IsNotExist(err) { + // Unknown error. Directly return + return err } return ioutil.WriteFile(passFile, []byte(passPhrase), defWritePassFileMode) } @@ -108,6 +108,10 @@ type dirPassProvider struct { dirPath string } +func newDirPassProvider(dirPath string) *dirPassProvider { + return &dirPassProvider{dirPath: } +} + func (provider *dirPassProvider) getPassphrase(keyFile string) (string, error) { baseName := filepath.Base(keyFile) passKeyBase := keyFileToPassFile(baseName) diff --git a/cmd/harmony/blsloader/helper.go b/cmd/harmony/blsloader/utils.go similarity index 77% rename from cmd/harmony/blsloader/helper.go rename to cmd/harmony/blsloader/utils.go index 31def26ef..5425b2381 100644 --- a/cmd/harmony/blsloader/helper.go +++ b/cmd/harmony/blsloader/utils.go @@ -3,18 +3,16 @@ package blsloader import ( "bufio" "fmt" - "io" "os" "path/filepath" "strings" "syscall" - "golang.org/x/crypto/ssh/terminal" - ffibls "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/internal/blsgen" "github.com/harmony-one/harmony/multibls" "github.com/pkg/errors" + "golang.org/x/crypto/ssh/terminal" ) var ( @@ -55,55 +53,56 @@ func loadKMSKeyFromFile(blsKeyFile string, kcp kmsClientProvider) (*ffibls.Secre return secretKey, nil } -// blsDirLoader is the helper structure -type blsDirLoader struct { - dirPath string - pp passProvider - kcp kmsClientProvider - resWriter io.Writer - +// blsDirLoadHelper is the helper structure +type blsDirLoadHelper struct { + dirPath string + pp passProvider + kcp kmsClientProvider + // result field secretKeys []*ffibls.SecretKey } -func (loader *blsDirLoader) getKeyFilesFromDir(dir string) (multibls.PrivateKey, error) { - err := filepath.Walk(dir, loader.processFileWalk) +func (helper *blsDirLoadHelper) getKeyFilesFromDir() (multibls.PrivateKey, error) { + err := filepath.Walk(helper.dirPath, helper.processFileWalk) if err != nil { return multibls.PrivateKey{}, err } - return multibls.PrivateKey{PrivateKey: loader.secretKeys}, nil + return multibls.PrivateKey{PrivateKey: helper.secretKeys}, nil } -// error types to be neglected for directory bls loading -var skippingErrs = []error{ - errUnknownExtension, - errNilPassProvider, - errNilKMSClientProvider, -} - -func (loader *blsDirLoader) processFileWalk(path string, info os.FileInfo, err error) error { - key, err := loader.loadKeyFromFile(path, info) +func (helper *blsDirLoadHelper) processFileWalk(path string, info os.FileInfo, err error) error { + key, err := helper.loadKeyFromFile(path, info) if err != nil { - if errIsErrors(err, skippingErrs) { + if errIsErrors(err, helper.skippingErrors()) { skipStr := fmt.Sprintf("Skipping [%s]: %v\n", path, err) - loader.resWriter.Write([]byte(skipStr)) + fmt.Println(skipStr) return nil } return err } - loader.secretKeys = append(loader.secretKeys, key) + helper.secretKeys = append(helper.secretKeys, key) return nil } -func (loader *blsDirLoader) loadKeyFromFile(path string, info os.FileInfo) (*ffibls.SecretKey, error) { +// errors to be neglected for directory bls loading +func (helper *blsDirLoadHelper) skippingErrors() []error { + return []error{ + errUnknownExtension, + errNilPassProvider, + errNilKMSClientProvider, + } +} + +func (helper *blsDirLoadHelper) loadKeyFromFile(path string, info os.FileInfo) (*ffibls.SecretKey, error) { var ( key *ffibls.SecretKey err error ) switch { case isBasicKeyFile(info): - key, err = loadBasicKeyFromFile(path, loader.pp) + key, err = loadBasicKeyFromFile(path, helper.pp) case isKMSKeyFile(info): - key, err = loadKMSKeyFromFile(path, loader.kcp) + key, err = loadKMSKeyFromFile(path, helper.kcp) default: err = errUnknownExtension }