[node.sh] changed public signature to config based. Do a lot of refactoring

pull/3219/head
Jacky Wang 4 years ago
parent 6d2fa888fc
commit cc600f9879
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 235
      cmd/harmony/blsloader/helper.go
  2. 55
      cmd/harmony/blsloader/kmsProvider.go
  3. 329
      cmd/harmony/blsloader/loader.go
  4. 59
      cmd/harmony/blsloader/params.go
  5. 15
      cmd/harmony/blsloader/passProvider.go
  6. 47
      cmd/harmony/blsloader/utils.go

@ -0,0 +1,235 @@
package blsloader
import (
"errors"
"fmt"
"os"
"path/filepath"
bls_core "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/multibls"
)
// loadHelper defines the interface to help load bls keys
type loadHelper interface {
loadKeys() (multibls.PrivateKeys, error)
}
// basicSingleBlsLoader loads a single bls key file with passphrase
type basicSingleBlsLoader struct {
blsKeyFile string
passProviderConfig
}
// loadKeys load bls keys from a single bls file
func (loader *basicSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) {
providers, err := loader.getPassProviders()
if err != nil {
return multibls.PrivateKeys{}, err
}
secretKey, err := loadBasicKey(loader.blsKeyFile, providers)
if err != nil {
return multibls.PrivateKeys{}, err
}
return multibls.GetPrivateKeys(secretKey), nil
}
func (loader *basicSingleBlsLoader) getPassProviders() ([]passProvider, error) {
switch loader.passSrcType {
case PassSrcFile:
return []passProvider{loader.getFilePassProvider()}, nil
case PassSrcPrompt:
return []passProvider{loader.getPromptPassProvider()}, nil
case PassSrcAuto:
return []passProvider{
loader.getFilePassProvider(),
loader.getPromptPassProvider(),
}, nil
default:
return nil, errors.New("unknown passphrase source type")
}
}
func (loader *basicSingleBlsLoader) getFilePassProvider() passProvider {
if stringIsSet(loader.passFile) {
return newFilePassProvider(*loader.passFile)
}
passFile := keyFileToPassFileFull(loader.blsKeyFile)
return newFilePassProvider(passFile)
}
func (loader *basicSingleBlsLoader) getPromptPassProvider() passProvider {
provider := newPromptPassProvider()
if loader.persistPassphrase {
provider.setPersist(filepath.Dir(loader.blsKeyFile))
}
return provider
}
type kmsSingleBlsLoader struct {
blsKeyFile string
kmsProviderConfig
}
// loadKeys load a single kms key file
func (loader *kmsSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) {
provider, err := loader.getKmsClientProvider()
if err != nil {
return multibls.PrivateKeys{}, err
}
secretKey, err := loadKmsKeyFromFile(loader.blsKeyFile, provider)
if err != nil {
return multibls.PrivateKeys{}, err
}
return multibls.GetPrivateKeys(secretKey), nil
}
func (loader *kmsSingleBlsLoader) getKmsClientProvider() (kmsClientProvider, error) {
switch loader.awsCfgSrcType {
case AwsCfgSrcFile:
if stringIsSet(loader.awsConfigFile) {
return newFileKmsProvider(*loader.awsConfigFile), nil
}
return newSharedKmsProvider(), nil
case AwsCfgSrcPrompt:
return newPromptKmsProvider(defKmsPromptTimeout), nil
case AwsCfgSrcShared:
return newSharedKmsProvider(), nil
default:
return nil, errors.New("unknown aws config source type")
}
}
// blsDirLoader is the helper structure for loading bls keys in a directory
type blsDirLoader struct {
// input fields
dirPath string
passProviderConfig
kmsProviderConfig
// providers in process
pps []passProvider
kcp kmsClientProvider
// result field
secretKeys []*bls_core.SecretKey
}
// loadKeys load all keys from the directory
func (loader *blsDirLoader) loadKeys() (multibls.PrivateKeys, error) {
var err error
if loader.pps, err = loader.getPassProviders(); err != nil {
return multibls.PrivateKeys{}, err
}
if loader.kcp, err = loader.getKmsClientProvider(); err != nil {
return multibls.PrivateKeys{}, err
}
return loader.loadKeyFiles()
}
func (loader *blsDirLoader) getPassProviders() ([]passProvider, error) {
switch loader.passSrcType {
case PassSrcFile:
return []passProvider{loader.getFilePassProvider()}, nil
case PassSrcPrompt:
return []passProvider{loader.getPromptPassProvider()}, nil
case PassSrcAuto:
return []passProvider{
loader.getFilePassProvider(),
loader.getPromptPassProvider(),
}, nil
default:
return nil, errors.New("unknown pass source type")
}
}
func (loader *blsDirLoader) getFilePassProvider() passProvider {
if stringIsSet(loader.passFile) {
return newFilePassProvider(*loader.passFile)
}
return newDirPassProvider(loader.dirPath)
}
func (loader *blsDirLoader) getPromptPassProvider() passProvider {
provider := newPromptPassProvider()
if loader.persistPassphrase {
provider.setPersist(loader.dirPath)
}
return provider
}
func (loader *blsDirLoader) getKmsClientProvider() (kmsClientProvider, error) {
switch loader.awsCfgSrcType {
case AwsCfgSrcFile:
if stringIsSet(loader.awsConfigFile) {
return newFileKmsProvider(*loader.awsConfigFile), nil
}
return newSharedKmsProvider(), nil
case AwsCfgSrcPrompt:
return newPromptKmsProvider(defKmsPromptTimeout), nil
case AwsCfgSrcShared:
return newSharedKmsProvider(), nil
default:
return nil, errors.New("unknown aws config source type")
}
}
func (loader *blsDirLoader) loadKeyFiles() (multibls.PrivateKeys, error) {
err := filepath.Walk(loader.dirPath, loader.processFileWalk)
if err != nil {
return multibls.PrivateKeys{}, err
}
return multibls.GetPrivateKeys(loader.secretKeys...), nil
}
func (loader *blsDirLoader) processFileWalk(path string, info os.FileInfo, err error) error {
key, err := loader.loadKeyFromFile(path, info)
if err != nil {
if !errIsErrors(err, loader.skippingErrors()) {
// unexpected error, return the error and break the file walk loop
return err
}
// expected error. Skipping these files
skipStr := fmt.Sprintf("Skipping [%s]: %v\n", path, err)
console.println(skipStr)
return nil
}
loader.secretKeys = append(loader.secretKeys, key)
return nil
}
// errors to be neglected for directory bls loading
func (loader *blsDirLoader) skippingErrors() []error {
return []error{
errUnknownExtension,
errNilPassProvider,
errNilKMSClientProvider,
}
}
func (loader *blsDirLoader) loadKeyFromFile(path string, info os.FileInfo) (*bls_core.SecretKey, error) {
var (
key *bls_core.SecretKey
err error
)
switch {
case isBasicKeyFile(path):
key, err = loadBasicKey(path, loader.pps)
case isKMSKeyFile(path):
key, err = loadKmsKeyFromFile(path, loader.kcp)
default:
err = errUnknownExtension
}
return key, err
}
// errIsErrors return whether the err is one of the errs
func errIsErrors(err error, errs []error) bool {
for _, targetErr := range errs {
if errors.Is(err, targetErr) {
return true
}
}
return false
}

@ -14,6 +14,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// AwsConfig is the config data structure for credentials and region. Used for AWS KMS
// decryption.
type AwsConfig struct { type AwsConfig struct {
AccessKey string `json:"aws-access-key-id"` AccessKey string `json:"aws-access-key-id"`
SecretKey string `json:"aws-secret-access-key"` SecretKey string `json:"aws-secret-access-key"`
@ -21,7 +23,7 @@ type AwsConfig struct {
Token string `json:"aws-token,omitempty"` Token string `json:"aws-token,omitempty"`
} }
func (cfg *AwsConfig) toAws() *aws.Config { func (cfg AwsConfig) toAws() *aws.Config {
cred := credentials.NewStaticCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token) cred := credentials.NewStaticCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token)
return &aws.Config{ return &aws.Config{
Region: aws.String(cfg.Region), Region: aws.String(cfg.Region),
@ -29,6 +31,27 @@ func (cfg *AwsConfig) toAws() *aws.Config {
} }
} }
// kmsProviderConfig is the data structure of kmsClientProvider config
type kmsProviderConfig struct {
awsCfgSrcType AwsCfgSrcType
awsConfigFile *string
}
func (cfg kmsProviderConfig) validate() error {
if !cfg.awsCfgSrcType.isValid() {
return errors.New("unknown AwsCfgSrcType")
}
if cfg.awsCfgSrcType == AwsCfgSrcFile {
if !stringIsSet(cfg.awsConfigFile) {
return errors.New("config field AwsConfig file must set for AwsCfgSrcFile")
}
if !isFile(*cfg.awsConfigFile) {
return fmt.Errorf("aws config file not exist %v", *cfg.awsConfigFile)
}
}
return nil
}
// kmsClientProvider provides the kms client. Implemented by // kmsClientProvider provides the kms client. Implemented by
// baseKMSProvider - abstract implementation // baseKMSProvider - abstract implementation
// sharedKMSProvider - provide the client with default .aws folder // sharedKMSProvider - provide the client with default .aws folder
@ -38,19 +61,20 @@ type kmsClientProvider interface {
// getKMSClient returns the KMSClient of the kmsClientProvider with lazy loading. // getKMSClient returns the KMSClient of the kmsClientProvider with lazy loading.
getKMSClient() (*kms.KMS, error) getKMSClient() (*kms.KMS, error)
// getAWSConfig returns the AwsConfig for different implementations
getAWSConfig() (*AwsConfig, error)
// toStr return the string presentation of kmsClientProvider // toStr return the string presentation of kmsClientProvider
toStr() string toStr() string
} }
type getAwsCfgFunc func() (*AwsConfig, error)
// baseKMSProvider provide the kms client with singleton initialization through // baseKMSProvider provide the kms client with singleton initialization through
// function getConfig for aws credential and regions loading. // function getConfig for aws credential and regions loading.
type baseKMSProvider struct { type baseKMSProvider struct {
client *kms.KMS client *kms.KMS
err error err error
once sync.Once once sync.Once
getAWSConfig getAwsCfgFunc
} }
func (provider *baseKMSProvider) getKMSClient() (*kms.KMS, error) { func (provider *baseKMSProvider) getKMSClient() (*kms.KMS, error) {
@ -68,10 +92,6 @@ func (provider *baseKMSProvider) getKMSClient() (*kms.KMS, error) {
return provider.client, nil return provider.client, nil
} }
func (provider *baseKMSProvider) getAWSConfig() (*AwsConfig, error) {
return nil, errors.New("not implemented")
}
func (provider *baseKMSProvider) toStr() string { func (provider *baseKMSProvider) toStr() string {
return "not implemented" return "not implemented"
} }
@ -83,11 +103,12 @@ type sharedKMSProvider struct {
} }
func newSharedKmsProvider() *sharedKMSProvider { func newSharedKmsProvider() *sharedKMSProvider {
return &sharedKMSProvider{ provider := &sharedKMSProvider{baseKMSProvider{}}
baseKMSProvider{}, provider.baseKMSProvider.getAWSConfig = provider.getAWSConfig
} return provider
} }
// TODO(Jacky): set getAwsConfig into a function, not bind with structure
func (provider *sharedKMSProvider) getAWSConfig() (*AwsConfig, error) { func (provider *sharedKMSProvider) getAWSConfig() (*AwsConfig, error) {
return nil, nil return nil, nil
} }
@ -105,10 +126,12 @@ type fileKMSProvider struct {
} }
func newFileKmsProvider(file string) *fileKMSProvider { func newFileKmsProvider(file string) *fileKMSProvider {
return &fileKMSProvider{ provider := &fileKMSProvider{
baseKMSProvider: baseKMSProvider{}, baseKMSProvider: baseKMSProvider{},
file: file, file: file,
} }
provider.baseKMSProvider.getAWSConfig = provider.getAWSConfig
return provider
} }
func (provider *fileKMSProvider) getAWSConfig() (*AwsConfig, error) { func (provider *fileKMSProvider) getAWSConfig() (*AwsConfig, error) {
@ -138,10 +161,12 @@ type promptKMSProvider struct {
} }
func newPromptKmsProvider(timeout time.Duration) *promptKMSProvider { func newPromptKmsProvider(timeout time.Duration) *promptKMSProvider {
return &promptKMSProvider{ provider := &promptKMSProvider{
baseKMSProvider: baseKMSProvider{}, baseKMSProvider: baseKMSProvider{},
timeout: timeout, timeout: timeout,
} }
provider.baseKMSProvider.getAWSConfig = provider.getAWSConfig
return provider
} }
func (provider *promptKMSProvider) getAWSConfig() (*AwsConfig, error) { func (provider *promptKMSProvider) getAWSConfig() (*AwsConfig, error) {
@ -204,7 +229,7 @@ func kmsClientWithConfig(config *AwsConfig) (*kms.KMS, error) {
if config == nil { if config == nil {
return getSharedKMSClient() return getSharedKMSClient()
} }
return getKMSClientFromConfig(config) return getKMSClientFromConfig(*config)
} }
func getSharedKMSClient() (*kms.KMS, error) { func getSharedKMSClient() (*kms.KMS, error) {
@ -217,7 +242,7 @@ func getSharedKMSClient() (*kms.KMS, error) {
return kms.New(sess), err return kms.New(sess), err
} }
func getKMSClientFromConfig(config *AwsConfig) (*kms.KMS, error) { func getKMSClientFromConfig(config AwsConfig) (*kms.KMS, error) {
sess, err := session.NewSession(config.toAws()) sess, err := session.NewSession(config.toAws())
if err != nil { if err != nil {
return nil, err return nil, err

@ -3,289 +3,134 @@ package blsloader
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/multibls"
) )
type Loader struct { func LoadKeys(cfg Config) (multibls.PrivateKeys, error) {
BlsKeyFile *string cfg.applyDefault()
AwsBlsKey *string if err := cfg.validate(); err != nil {
BlsDir *string
PassSrcType PassSrcType
PassFile *string
PersistPassphrase bool
AwsCfgSrcType AwsCfgSrcType
AwsConfigFile *string
}
// LoadKeys load all keys from the input fields provided
func (loader *Loader) LoadKeys() (multibls.PrivateKeys, error) {
helper, err := loader.getHelper()
if err != nil {
return multibls.PrivateKeys{}, err return multibls.PrivateKeys{}, err
} }
helper, err := getHelper(cfg)
if err != nil {
return nil, err
}
return helper.loadKeys() return helper.loadKeys()
} }
// getHelper parse the Loader structure to helper for further computation func getHelper(cfg Config) (loadHelper, error) {
func (loader *Loader) getHelper() (loadHelper, error) {
switch { switch {
case stringIsSet(loader.BlsKeyFile): case stringIsSet(cfg.BlsKeyFile):
switch filepath.Ext(*cfg.BlsKeyFile) {
case basicKeyExt:
return &basicSingleBlsLoader{ return &basicSingleBlsLoader{
blsKeyFile: *loader.BlsKeyFile, blsKeyFile: *cfg.BlsKeyFile,
passFile: loader.PassFile, passProviderConfig: cfg.getPassProviderConfig(),
passSrcType: loader.PassSrcType,
persistPassphrase: loader.PersistPassphrase,
}, nil }, nil
case stringIsSet(loader.AwsBlsKey): case kmsKeyExt:
return &kmsSingleBlsLoader{ return &kmsSingleBlsLoader{
awsBlsKey: *loader.AwsBlsKey, blsKeyFile: *cfg.BlsKeyFile,
awsCfgSrcType: loader.AwsCfgSrcType, kmsProviderConfig: cfg.getKmsProviderConfig(),
awsConfigFile: loader.AwsConfigFile,
}, nil
case stringIsSet(loader.BlsDir):
return &blsDirLoader{
dirPath: *loader.BlsDir,
pst: loader.PassSrcType,
passFile: loader.PassFile,
persistPassphrase: loader.PersistPassphrase,
act: loader.AwsCfgSrcType,
awsConfigFile: loader.AwsConfigFile,
}, nil }, nil
default: default:
return nil, errors.New("empty bls key data source") return nil, errors.New("unknown extension")
} }
} case stringIsSet(cfg.BlsDir):
return &blsDirLoader{
// loadHelper defines the interface to help load bls keys dirPath: *cfg.BlsDir,
type loadHelper interface { passProviderConfig: cfg.getPassProviderConfig(),
loadKeys() (multibls.PrivateKeys, error) kmsProviderConfig: cfg.getKmsProviderConfig(),
}
// basicSingleBlsLoader loads a single bls key file with passphrase
type basicSingleBlsLoader struct {
blsKeyFile string
passSrcType PassSrcType
passFile *string
persistPassphrase bool
}
func (loader *basicSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) {
providers, err := loader.getPassProviders()
if err != nil {
return multibls.PrivateKeys{}, err
}
secretKey, err := loadBasicKey(loader.blsKeyFile, providers)
if err != nil {
return multibls.PrivateKeys{}, err
}
return secretKeyToMultiPrivateKey(secretKey), nil
}
func (loader *basicSingleBlsLoader) getPassProviders() ([]passProvider, error) {
switch loader.passSrcType {
case PassSrcFile:
return []passProvider{loader.getFilePassProvider()}, nil
case PassSrcPrompt:
return []passProvider{loader.getPromptPassProvider()}, nil
case PassSrcAuto:
return []passProvider{
loader.getFilePassProvider(),
loader.getPromptPassProvider(),
}, nil }, nil
default: default:
return nil, errors.New("unknown passphrase source type") return nil, errors.New("either BlsKeyFile or BlsDir must be set")
} }
} }
func (loader *basicSingleBlsLoader) getFilePassProvider() passProvider { // Loader is the structure to load bls keys.
if stringIsSet(loader.passFile) { type Config struct {
return newFilePassProvider(*loader.passFile) // source for bls key loading. At least one of the BlsKeyFile and BlsDir
} // need to be provided.
passFile := keyFileToPassFileFull(loader.blsKeyFile) //
return newFilePassProvider(passFile) // BlsKeyFile defines a single key file to load from. Based on the file
} // extension, decryption with passphrase or aws kms will be used.
BlsKeyFile *string
func (loader *basicSingleBlsLoader) getPromptPassProvider() passProvider { // BlsDir defines a file directory to load keys from.
provider := newPromptPassProvider() BlsDir *string
if loader.persistPassphrase {
provider.setPersist(filepath.Dir(loader.blsKeyFile))
}
return provider
}
type kmsSingleBlsLoader struct {
awsBlsKey string
awsCfgSrcType AwsCfgSrcType // Passphrase related settings. Used for passphrase encrypted key files.
awsConfigFile *string //
} // PassSrcType defines the source to get passphrase. Three source types are available
// PassSrcFile - get passphrase from a .pass file
// PassSrcPrompt - get passphrase from prompt
// PassSrcAuto - try to unlock with .pass file. If not success, ask user with prompt
// Value is default to PassSrcAuto.
PassSrcType PassSrcType
// PassFile specifies the .pass file to be used when loading passphrase from file.
// If not set, default to the .pass file in the same directory as the key file.
PassFile *string
// PersistPassphrase set whether to persist the passphrase to a .pass file when
// prompt the user for password. Persisted pass file is a file with .pass extension
// under the same directory as the key file.
PersistPassphrase bool
func (loader *kmsSingleBlsLoader) loadKeys() (multibls.PrivateKeys, error) { // Aws configuration related settings, including AWS credentials and region info.
provider, err := loader.getKmsClientProvider() // Used for KMS encrypted passphrase files.
if err != nil { //
return multibls.PrivateKeys{}, err // AwsCfgSrcType defines the source to get aws config. Three types available:
} // AwsCfgSrcFile - get AWS config through a json file. See AwsConfig for content fields.
secretKey, err := loadKmsKeyFromFile(loader.awsBlsKey, provider) // AwsCfgSrcPrompt - get AWS config through prompt.
if err != nil { // AwsCfgSrcShared - Use the default AWS config settings (from env and $HOME/.aws/config)
return multibls.PrivateKeys{}, err // Default to AwsCfgSrcShared.
} AwsCfgSrcType AwsCfgSrcType
return secretKeyToMultiPrivateKey(secretKey), nil // AwsConfigFile set the json file to load aws config.
AwsConfigFile *string
} }
func (loader *kmsSingleBlsLoader) getKmsClientProvider() (kmsClientProvider, error) { func (cfg *Config) applyDefault() {
switch loader.awsCfgSrcType { if cfg.PassSrcType == PassSrcNil {
case AwsCfgSrcFile: cfg.PassSrcType = PassSrcAuto
if stringIsSet(loader.awsConfigFile) {
return newFileKmsProvider(*loader.awsConfigFile), nil
} }
return newSharedKmsProvider(), nil if cfg.AwsCfgSrcType == AwsCfgSrcNil {
case AwsCfgSrcPrompt: cfg.AwsCfgSrcType = AwsCfgSrcShared
return newPromptKmsProvider(defKmsPromptTimeout), nil
case AwsCfgSrcShared:
return newSharedKmsProvider(), nil
default:
return nil, errors.New("unknown aws config source type")
} }
} }
// blsDirLoader is the helper structure for loading bls keys in a directory func (cfg *Config) validate() error {
type blsDirLoader struct { if stringIsSet(cfg.BlsKeyFile) {
// input fields if !isFile(*cfg.BlsKeyFile) {
dirPath string return fmt.Errorf("key file not exist %v", *cfg.BlsKeyFile)
// pass provider fields
pst PassSrcType
passFile *string
persistPassphrase bool
// kms provider fields
act AwsCfgSrcType
awsConfigFile *string
// providers in process
pps []passProvider
kcp kmsClientProvider
// result field
secretKeys []*bls.SecretKey
}
func (loader *blsDirLoader) loadKeys() (multibls.PrivateKeys, error) {
var err error
if loader.pps, err = loader.getPassProviders(); err != nil {
return multibls.PrivateKeys{}, err
} }
if loader.kcp, err = loader.getKmsClientProvider(); err != nil { switch ext := filepath.Ext(*cfg.BlsKeyFile); ext {
return multibls.PrivateKeys{}, err case basicKeyExt, kmsKeyExt:
}
return loader.loadKeyFiles()
}
func (loader *blsDirLoader) getPassProviders() ([]passProvider, error) {
switch loader.pst {
case PassSrcFile:
return []passProvider{loader.getFilePassProvider()}, nil
case PassSrcPrompt:
return []passProvider{loader.getPromptPassProvider()}, nil
case PassSrcAuto:
return []passProvider{
loader.getFilePassProvider(),
loader.getPromptPassProvider(),
}, nil
default: default:
return nil, errors.New("unknown pass source type") return fmt.Errorf("unknown key file extension %v", ext)
}
}
func (loader *blsDirLoader) getFilePassProvider() passProvider {
if stringIsSet(loader.passFile) {
return newFilePassProvider(*loader.passFile)
} }
return newDirPassProvider(loader.dirPath) } else if stringIsSet(cfg.BlsDir) {
} if !isDir(*cfg.BlsDir) {
return fmt.Errorf("dir not exist %v", *cfg.BlsDir)
func (loader *blsDirLoader) getPromptPassProvider() passProvider {
provider := newPromptPassProvider()
if loader.persistPassphrase {
provider.setPersist(loader.dirPath)
}
return provider
}
func (loader *blsDirLoader) getKmsClientProvider() (kmsClientProvider, error) {
switch loader.act {
case AwsCfgSrcFile:
if stringIsSet(loader.awsConfigFile) {
return newFileKmsProvider(*loader.awsConfigFile), nil
} }
return newSharedKmsProvider(), nil } else {
case AwsCfgSrcPrompt: return errors.New("either BlsKeyFile or BlsDir must be set")
return newPromptKmsProvider(defKmsPromptTimeout), nil
case AwsCfgSrcShared:
return newSharedKmsProvider(), nil
default:
return nil, errors.New("unknown aws config source type")
}
}
func (loader *blsDirLoader) loadKeyFiles() (multibls.PrivateKeys, error) {
err := filepath.Walk(loader.dirPath, loader.processFileWalk)
if err != nil {
return multibls.PrivateKeys{}, err
} }
return secretKeyToMultiPrivateKey(loader.secretKeys...), nil if err := cfg.getPassProviderConfig().validate(); err != nil {
}
func (loader *blsDirLoader) processFileWalk(path string, info os.FileInfo, err error) error {
key, err := loader.loadKeyFromFile(path, info)
if err != nil {
if !errIsErrors(err, loader.skippingErrors()) {
// unexpected error, return the error and break the file walk loop
return err return err
} }
// expected error. Skipping these files return cfg.getKmsProviderConfig().validate()
skipStr := fmt.Sprintf("Skipping [%s]: %v\n", path, err)
console.println(skipStr)
return nil
}
loader.secretKeys = append(loader.secretKeys, key)
return nil
}
// errors to be neglected for directory bls loading
func (loader *blsDirLoader) skippingErrors() []error {
return []error{
errUnknownExtension,
errNilPassProvider,
errNilKMSClientProvider,
}
} }
func (loader *blsDirLoader) loadKeyFromFile(path string, info os.FileInfo) (*bls.SecretKey, error) { func (cfg *Config) getPassProviderConfig() passProviderConfig {
var ( return passProviderConfig{
key *bls.SecretKey passSrcType: cfg.PassSrcType,
err error passFile: cfg.PassFile,
) persistPassphrase: cfg.PersistPassphrase,
switch {
case isBasicKeyFile(info):
key, err = loadBasicKey(path, loader.pps)
case isKMSKeyFile(info):
key, err = loadKmsKeyFromFile(path, loader.kcp)
default:
err = errUnknownExtension
} }
return key, err
} }
// errIsErrors return whether the err is one of the errs func (cfg *Config) getKmsProviderConfig() kmsProviderConfig {
func errIsErrors(err error, errs []error) bool { return kmsProviderConfig{
for _, targetErr := range errs { awsCfgSrcType: cfg.AwsCfgSrcType,
if errors.Is(err, targetErr) { awsConfigFile: cfg.AwsConfigFile,
return true
}
} }
return false
} }

@ -3,12 +3,15 @@ package blsloader
import "time" import "time"
const ( const (
// Extensions for files.
passExt = ".pass" passExt = ".pass"
basicKeyExt = ".key" basicKeyExt = ".key"
kmsKeyExt = ".bls" kmsKeyExt = ".bls"
) )
const ( const (
// The default timeout for kms config prompt. The timeout is introduced
// for security concern.
defKmsPromptTimeout = 1 * time.Second defKmsPromptTimeout = 1 * time.Second
) )
@ -25,37 +28,18 @@ const (
type PassSrcType uint8 type PassSrcType uint8
const ( const (
PassSrcNil PassSrcType = iota // nil place holder 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 PassSrcFile // provide the passphrase through a pass file
PassSrcPrompt // provide the passphrase through prompt PassSrcPrompt // provide the passphrase through prompt
PassSrcAuto // first try to unlock with pass from file, then look for prompt
) )
// String return the string presentation of PassSrcType func (srcType PassSrcType) isValid() bool {
func (srcType PassSrcType) String() string {
switch srcType { switch srcType {
case PassSrcFile: case PassSrcAuto, PassSrcFile, PassSrcPrompt:
return "file" return true
case PassSrcPrompt:
return "prompt"
case PassSrcAuto:
return "auto"
default:
return "unknown"
}
}
// PassSrcTypeFromString parse PassSrcType from string specifier
func PassSrcTypeFromString(str string) PassSrcType {
switch str {
case "file":
return PassSrcFile
case "prompt":
return PassSrcPrompt
case "auto":
return PassSrcAuto
default: default:
return PassSrcNil return false
} }
} }
@ -72,28 +56,11 @@ const (
AwsCfgSrcShared // through shared aws config AwsCfgSrcShared // through shared aws config
) )
func (srcType AwsCfgSrcType) String() string { func (srcType AwsCfgSrcType) isValid() bool {
switch srcType { switch srcType {
case AwsCfgSrcFile: case AwsCfgSrcFile, AwsCfgSrcPrompt, AwsCfgSrcShared:
return "file" return true
case AwsCfgSrcPrompt:
return "prompt"
case AwsCfgSrcShared:
return "shared"
default:
return "unknown"
}
}
func AwsCfgSrcTypeFromString(str string) AwsCfgSrcType {
switch str {
case "file":
return AwsCfgSrcFile
case "prompt":
return AwsCfgSrcPrompt
case "shared":
return AwsCfgSrcShared
default: default:
return AwsCfgSrcNil return false
} }
} }

@ -1,12 +1,27 @@
package blsloader package blsloader
import ( import (
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
) )
// passProviderConfig is the data structure of passProviders config
type passProviderConfig struct {
passSrcType PassSrcType
passFile *string
persistPassphrase bool
}
func (config passProviderConfig) validate() error {
if !config.passSrcType.isValid() {
return errors.New("unknown PassSrcType")
}
return nil
}
// 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

@ -3,13 +3,13 @@ package blsloader
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/pkg/errors"
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/crypto/bls"
"github.com/harmony-one/harmony/internal/blsgen" "github.com/harmony-one/harmony/internal/blsgen"
"github.com/harmony-one/harmony/multibls"
"github.com/pkg/errors"
) )
var ( var (
@ -62,34 +62,42 @@ func loadKmsKeyFromFile(blsKeyFile string, kcp kmsClientProvider) (*bls_core.Sec
return secretKey, nil return secretKey, nil
} }
// isBasicKeyFile return whether the given file is a bls file func isFile(path string) bool {
func isBasicKeyFile(info os.FileInfo) bool { info, err := os.Stat(path)
if info.IsDir() { if err != nil {
return false return false
} }
if !strings.HasSuffix(info.Name(), basicKeyExt) { return !info.IsDir()
}
func isDir(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false return false
} }
return true return info.IsDir()
} }
// isKMSKeyFile returns whether the given file is a kms key file func isBasicKeyFile(path string) bool {
func isKMSKeyFile(info os.FileInfo) bool { exist := isFile(path)
if info.IsDir() { if !exist {
return false return false
} }
if !strings.HasSuffix(info.Name(), kmsKeyExt) { return filepath.Ext(path) == basicKeyExt
}
func isKMSKeyFile(path string) bool {
exist := isFile(path)
if !exist {
return false return false
} }
return true return filepath.Ext(path) == kmsKeyExt
} }
// keyFileToPassFileBase convert a key file base name to passphrase file base name
func keyFileToPassFileBase(keyFileBase string) string { func keyFileToPassFileBase(keyFileBase string) string {
return strings.Trim(keyFileBase, basicKeyExt) + passExt return strings.Trim(keyFileBase, basicKeyExt) + passExt
} }
// keyFileToPassFileFull convert a key file full path to passphrase file full path
func keyFileToPassFileFull(keyFile string) string { func keyFileToPassFileFull(keyFile string) string {
return strings.Trim(keyFile, basicKeyExt) + passExt return strings.Trim(keyFile, basicKeyExt) + passExt
} }
@ -124,15 +132,6 @@ func promptYesNo(prompt string) (bool, error) {
} }
} }
func secretKeyToMultiPrivateKey(secretKeys ...*bls_core.SecretKey) multibls.PrivateKeys {
keys := make(multibls.PrivateKeys, 0, len(secretKeys))
for _, secretKey := range secretKeys {
key := bls.WrapperFromPrivateKey(secretKey)
keys = append(keys, key)
}
return keys
}
func stringIsSet(val *string) bool { func stringIsSet(val *string) bool {
return val != nil && *val != "" return val != nil && *val != ""
} }

Loading…
Cancel
Save