From 2013daa953f8f3b7e8930a8f86422f379413ec82 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Wed, 23 Nov 2016 14:02:20 +0100 Subject: [PATCH] BCCSP KeyStore This change-set introduce the concept of KeyStore at the BCCSP level. A KeyStore represents a storage system for cryptographic keys. It allows to store and retrieve bccsp.Key objects. The key store can be read only, in that case storing a key will return an error. Now, to initialize a software-based BCCSP, a key store must be specified. A file based keystore is provided. In addition, all the dependencies from viper has been removed. In order to do that, some assumptions has been made on default security level. The organization of this paramenters will be better addressed in a subsequent change-set. This change-set comes in the context of: https://jira.hyperledger.org/browse/FAB-354 Change-Id: I7e96eea7e5e89ecec86863ebc11b919d29c0019a Signed-off-by: Angelo De Caro --- core/crypto/bccsp/factory/factory.go | 11 +- core/crypto/bccsp/factory/factory_test.go | 25 +- .../factory/{sw_factory.go => swfactory.go} | 16 +- core/crypto/bccsp/keystore.go | 34 ++ core/crypto/bccsp/signer/signer_test.go | 5 +- core/crypto/bccsp/sw/conf.go | 59 +-- core/crypto/bccsp/sw/fileks.go | 396 ++++++++++++++++++ core/crypto/bccsp/sw/impl.go | 89 ++-- core/crypto/bccsp/sw/impl_test.go | 7 +- core/crypto/bccsp/sw/ks.go | 251 ----------- 10 files changed, 505 insertions(+), 388 deletions(-) rename core/crypto/bccsp/factory/{sw_factory.go => swfactory.go} (83%) create mode 100644 core/crypto/bccsp/keystore.go create mode 100644 core/crypto/bccsp/sw/fileks.go delete mode 100644 core/crypto/bccsp/sw/ks.go diff --git a/core/crypto/bccsp/factory/factory.go b/core/crypto/bccsp/factory/factory.go index 69f81cddcd4..0245917008c 100644 --- a/core/crypto/bccsp/factory/factory.go +++ b/core/crypto/bccsp/factory/factory.go @@ -20,8 +20,10 @@ import ( "fmt" "sync" + "os" + "github.com/hyperledger/fabric/core/crypto/bccsp" - "github.com/spf13/viper" + "github.com/hyperledger/fabric/core/crypto/bccsp/sw" ) var ( @@ -104,12 +106,7 @@ func initFactoriesMap() error { } func createDefaultBCCSP() (bccsp.BCCSP, error) { - defaultBCCSPFactoryName := viper.GetString("bccsp.default") - if defaultBCCSPFactoryName == "" { - defaultBCCSPFactoryName = SoftwareBasedFactoryName - } - - return getBCCSPInternal(&DefaultOpts{defaultBCCSPFactoryName, false}) + return sw.NewDefaultSecurityLevel(os.TempDir()) } func getBCCSPInternal(opts Opts) (bccsp.BCCSP, error) { diff --git a/core/crypto/bccsp/factory/factory_test.go b/core/crypto/bccsp/factory/factory_test.go index c3ccae270ad..d6b3aac7c25 100644 --- a/core/crypto/bccsp/factory/factory_test.go +++ b/core/crypto/bccsp/factory/factory_test.go @@ -15,7 +15,12 @@ limitations under the License. */ package factory -import "testing" +import ( + "os" + "testing" + + "github.com/hyperledger/fabric/core/crypto/bccsp/sw" +) func TestGetDefault(t *testing.T) { bccsp, err := GetDefault() @@ -28,12 +33,17 @@ func TestGetDefault(t *testing.T) { } func TestGetBCCPEphemeral(t *testing.T) { - bccsp1, err := GetBCCSP(&SwOpts{EphemeralFlag: true}) + ks := &sw.FileBasedKeyStore{} + if err := ks.Init(nil, os.TempDir(), false); err != nil { + t.Fatalf("Failed initializing key store [%s]", err) + } + + bccsp1, err := GetBCCSP(&SwOpts{Ephemeral_: true, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks}) if err != nil { t.Fatalf("Failed getting ephemeral software-based BCCSP [%s]", err) } - bccsp2, err := GetBCCSP(&SwOpts{EphemeralFlag: true}) + bccsp2, err := GetBCCSP(&SwOpts{Ephemeral_: true, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks}) if err != nil { t.Fatalf("Failed getting ephemeral software-based BCCSP [%s]", err) } @@ -44,12 +54,17 @@ func TestGetBCCPEphemeral(t *testing.T) { } func TestGetBCCP2Ephemeral(t *testing.T) { - bccsp1, err := GetBCCSP(&SwOpts{EphemeralFlag: false}) + ks := &sw.FileBasedKeyStore{} + if err := ks.Init(nil, os.TempDir(), false); err != nil { + t.Fatalf("Failed initializing key store [%s]", err) + } + + bccsp1, err := GetBCCSP(&SwOpts{Ephemeral_: false, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks}) if err != nil { t.Fatalf("Failed getting non-ephemeral software-based BCCSP [%s]", err) } - bccsp2, err := GetBCCSP(&SwOpts{EphemeralFlag: false}) + bccsp2, err := GetBCCSP(&SwOpts{Ephemeral_: false, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks}) if err != nil { t.Fatalf("Failed getting non-ephemeral software-based BCCSP [%s]", err) } diff --git a/core/crypto/bccsp/factory/sw_factory.go b/core/crypto/bccsp/factory/swfactory.go similarity index 83% rename from core/crypto/bccsp/factory/sw_factory.go rename to core/crypto/bccsp/factory/swfactory.go index 7855b2174d5..0d56588c34c 100644 --- a/core/crypto/bccsp/factory/sw_factory.go +++ b/core/crypto/bccsp/factory/swfactory.go @@ -52,20 +52,28 @@ func (f *SWFactory) Get(opts Opts) (bccsp.BCCSP, error) { return nil, fmt.Errorf("Invalid Provider Name [%s]. Opts must refer to [%s].", opts.FactoryName(), f.Name()) } + swOpts, ok := opts.(*SwOpts) + if !ok { + return nil, errors.New("Invalid opts. They must be of type SwOpts.") + } + if !opts.Ephemeral() { f.initOnce.Do(func() { - f.bccsp, f.err = sw.NewDefaultSecurityLevel() + f.bccsp, f.err = sw.New(swOpts.SecLevel, swOpts.HashFamily, swOpts.KeyStore) return }) return f.bccsp, f.err } - return sw.NewDefaultSecurityLevel() + return sw.New(swOpts.SecLevel, swOpts.HashFamily, swOpts.KeyStore) } // SwOpts contains options for the SWFactory type SwOpts struct { - EphemeralFlag bool + Ephemeral_ bool + SecLevel int + HashFamily string + KeyStore bccsp.KeyStore } // FactoryName returns the name of the provider @@ -75,5 +83,5 @@ func (o *SwOpts) FactoryName() string { // Ephemeral returns true if the CSP has to be ephemeral, false otherwise func (o *SwOpts) Ephemeral() bool { - return o.EphemeralFlag + return o.Ephemeral_ } diff --git a/core/crypto/bccsp/keystore.go b/core/crypto/bccsp/keystore.go new file mode 100644 index 00000000000..bb1ca6c8c24 --- /dev/null +++ b/core/crypto/bccsp/keystore.go @@ -0,0 +1,34 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package bccsp + +// KeyStore represents a storage system for cryptographic keys. +// It allows to store and retrieve bccsp.Key objects. +// The KeyStore can be read only, in that case StoreKey will return +// an error. +type KeyStore interface { + + // ReadOnly returns true if this KeyStore is read only, false otherwise. + // If ReadOnly is true then StoreKey will fail. + ReadOnly() bool + + // GetKey returns a key object whose SKI is the one passed. + GetKey(ski []byte) (k Key, err error) + + // StoreKey stores the key k in this KeyStore. + // If this KeyStore is read only then the method will fail. + StoreKey(k Key) (err error) +} diff --git a/core/crypto/bccsp/signer/signer_test.go b/core/crypto/bccsp/signer/signer_test.go index 0424056a245..3bbfb72ff63 100644 --- a/core/crypto/bccsp/signer/signer_test.go +++ b/core/crypto/bccsp/signer/signer_test.go @@ -22,7 +22,6 @@ import ( "github.com/hyperledger/fabric/core/crypto/bccsp" "github.com/hyperledger/fabric/core/crypto/bccsp/sw" - "github.com/spf13/viper" ) var ( @@ -31,10 +30,8 @@ var ( func getBCCSP(t *testing.T) bccsp.BCCSP { if swBCCSPInstance == nil { - viper.Set("security.bccsp.default.keyStorePath", os.TempDir()) - var err error - swBCCSPInstance, err = sw.NewDefaultSecurityLevel() + swBCCSPInstance, err = sw.NewDefaultSecurityLevel(os.TempDir()) if err != nil { t.Fatalf("Failed initializing key store [%s]", err) } diff --git a/core/crypto/bccsp/sw/conf.go b/core/crypto/bccsp/sw/conf.go index d044a398b8e..2d4eaff95d2 100644 --- a/core/crypto/bccsp/sw/conf.go +++ b/core/crypto/bccsp/sw/conf.go @@ -16,57 +16,24 @@ limitations under the License. package sw import ( - "errors" - "path/filepath" - - "os" - "crypto/elliptic" "crypto/sha256" "crypto/sha512" "fmt" "hash" - "github.com/spf13/viper" "golang.org/x/crypto/sha3" ) type config struct { - keystorePath string + keyStorePath string securityLevel int hashFamily string - configurationPathProperty string - ellipticCurve elliptic.Curve - hashFunction func() hash.Hash - aesBitLength int - rsaBitLength int -} - -func (conf *config) init(securityLevel int, hashFamily string) error { - // Set security level - err := conf.setSecurityLevel(securityLevel, hashFamily) - if err != nil { - return fmt.Errorf("Failed initliazing security level [%s]", err) - } - // Set ks path - conf.configurationPathProperty = "security.bccsp.default.keyStorePath" - - // Check mandatory fields - var rootPath string - if err := conf.checkProperty(conf.configurationPathProperty); err != nil { - logger.Warning("'security.bccsp.default.keyStorePath' not set. Using the default directory [%s] for temporary files", os.TempDir()) - rootPath = os.TempDir() - } else { - rootPath = viper.GetString(conf.configurationPathProperty) - } - logger.Infof("Root Path [%s]", rootPath) - // Set configuration path - rootPath = filepath.Join(rootPath, "crypto") - - conf.keystorePath = filepath.Join(rootPath, "ks") - - return nil + ellipticCurve elliptic.Curve + hashFunction func() hash.Hash + aesBitLength int + rsaBitLength int } func (conf *config) setSecurityLevel(securityLevel int, hashFamily string) (err error) { @@ -116,19 +83,3 @@ func (conf *config) setSecurityLevelSHA3(level int) (err error) { } return } - -func (conf *config) checkProperty(property string) error { - res := viper.GetString(property) - if res == "" { - return errors.New("Property not specified in configuration file. Please check that property is set: " + property) - } - return nil -} - -func (conf *config) getKeyStorePath() string { - return conf.keystorePath -} - -func (conf *config) getPathForAlias(alias, suffix string) string { - return filepath.Join(conf.getKeyStorePath(), alias+"_"+suffix) -} diff --git a/core/crypto/bccsp/sw/fileks.go b/core/crypto/bccsp/sw/fileks.go new file mode 100644 index 00000000000..288b7223cd5 --- /dev/null +++ b/core/crypto/bccsp/sw/fileks.go @@ -0,0 +1,396 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package sw + +import ( + "io/ioutil" + "os" + "sync" + + "github.com/hyperledger/fabric/core/crypto/utils" + + "errors" + "strings" + + "crypto/ecdsa" + "crypto/rsa" + "encoding/hex" + "fmt" + "path/filepath" + + "github.com/hyperledger/fabric/core/crypto/bccsp" + "github.com/hyperledger/fabric/core/crypto/primitives" +) + +// FileBasedKeyStore is a folder-based KeyStore. +// Each key is stored in a separated file whose name contains the key's SKI +// and flags to identity the key's type. All the keys are stored in +// a folder whose path is provided at initialization time. +// The KeyStore can be initialized with a password, this password +// is used to encrypt and decrypt the files storing the keys. +// A KeyStore can be read only to avoid the overwriting of keys. +type FileBasedKeyStore struct { + path string + + readOnly bool + isOpen bool + + pwd []byte + + // Sync + m sync.Mutex +} + +// Init initializes this KeyStore with a password, a path to a folder +// where the keys are stored and a read only flag. +// Each key is stored in a separated file whose name contains the key's SKI +// and flags to identity the key's type. +// If the KeyStore is initialized with a password, this password +// is used to encrypt and decrypt the files storing the keys. +// The pwd can be nil for non-encrypted KeyStores. If an encrypted +// key-store is initialized without a password, then retrieving keys from the +// KeyStore will fail. +// A KeyStore can be read only to avoid the overwriting of keys. +func (ks *FileBasedKeyStore) Init(pwd []byte, path string, readOnly bool) error { + // Validate inputs + // pwd can be nil + + if len(path) == 0 { + return errors.New("An invalid KeyStore path provided. Path cannot be an empty string.") + } + + ks.m.Lock() + defer ks.m.Unlock() + + if ks.isOpen { + return errors.New("KeyStore already initilized.") + } + + ks.path = path + ks.pwd = utils.Clone(pwd) + + err := ks.createKeyStoreIfNotExists() + if err != nil { + return err + } + + err = ks.openKeyStore() + if err != nil { + return err + } + + ks.readOnly = readOnly + + return nil +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *FileBasedKeyStore) ReadOnly() bool { + return ks.readOnly +} + +// GetKey returns a key object whose SKI is the one passed. +func (ks *FileBasedKeyStore) GetKey(ski []byte) (k bccsp.Key, err error) { + // Validate arguments + if len(ski) == 0 { + return nil, errors.New("Invalid SKI. Cannot be of zero length.") + } + + suffix := ks.getSuffix(hex.EncodeToString(ski)) + + switch suffix { + case "key": + // Load the key + key, err := ks.loadKey(hex.EncodeToString(ski)) + if err != nil { + return nil, fmt.Errorf("Failed loading key [%x] [%s]", ski, err) + } + + return &aesPrivateKey{key, false}, nil + case "sk": + // Load the private key + key, err := ks.loadPrivateKey(hex.EncodeToString(ski)) + if err != nil { + return nil, fmt.Errorf("Failed loading key [%x] [%s]", ski, err) + } + + switch key.(type) { + case *ecdsa.PrivateKey: + return &ecdsaPrivateKey{key.(*ecdsa.PrivateKey)}, nil + case *rsa.PrivateKey: + return &rsaPrivateKey{key.(*rsa.PrivateKey)}, nil + default: + return nil, errors.New("Key type not recognized") + } + default: + return nil, errors.New("Key not recognized") + } +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *FileBasedKeyStore) StoreKey(k bccsp.Key) (err error) { + if ks.readOnly { + return errors.New("Read only KeyStore.") + } + + if k == nil { + return errors.New("Invalid key. It must be different from nil.") + } + switch k.(type) { + case *ecdsaPrivateKey: + kk := k.(*ecdsaPrivateKey) + + err = ks.storePrivateKey(hex.EncodeToString(k.SKI()), kk.k) + if err != nil { + return fmt.Errorf("Failed storing ECDSA private key [%s]", err) + } + + case *ecdsaPublicKey: + kk := k.(*ecdsaPublicKey) + + err = ks.storePublicKey(hex.EncodeToString(k.SKI()), kk.k) + if err != nil { + return fmt.Errorf("Failed storing ECDSA public key [%s]", err) + } + + case *rsaPrivateKey: + kk := k.(*rsaPrivateKey) + + err = ks.storePrivateKey(hex.EncodeToString(k.SKI()), kk.k) + if err != nil { + return fmt.Errorf("Failed storing RSA private key [%s]", err) + } + + case *rsaPublicKey: + kk := k.(*rsaPublicKey) + + err = ks.storePublicKey(hex.EncodeToString(k.SKI()), kk.k) + if err != nil { + return fmt.Errorf("Failed storing RSA public key [%s]", err) + } + + case *aesPrivateKey: + kk := k.(*aesPrivateKey) + + err = ks.storeKey(hex.EncodeToString(k.SKI()), kk.k) + if err != nil { + return fmt.Errorf("Failed storing AES key [%s]", err) + } + + default: + return fmt.Errorf("Key type not reconigned [%s]", k) + } + + return +} + +func (ks *FileBasedKeyStore) getSuffix(alias string) string { + files, _ := ioutil.ReadDir(ks.path) + for _, f := range files { + if strings.HasPrefix(f.Name(), alias) { + if strings.HasSuffix(f.Name(), "sk") { + return "sk" + } + if strings.HasSuffix(f.Name(), "pk") { + return "pk" + } + if strings.HasSuffix(f.Name(), "key") { + return "key" + } + break + } + } + return "" +} + +func (ks *FileBasedKeyStore) storePrivateKey(alias string, privateKey interface{}) error { + if ks.readOnly { + return errors.New("Read only KeyStore.") + } + + rawKey, err := primitives.PrivateKeyToPEM(privateKey, ks.pwd) + if err != nil { + logger.Errorf("Failed converting private key to PEM [%s]: [%s]", alias, err) + return err + } + + err = ioutil.WriteFile(ks.getPathForAlias(alias, "sk"), rawKey, 0700) + if err != nil { + logger.Errorf("Failed storing private key [%s]: [%s]", alias, err) + return err + } + + return nil +} + +func (ks *FileBasedKeyStore) storePublicKey(alias string, publicKey interface{}) error { + if ks.readOnly { + return errors.New("Read only KeyStore.") + } + + rawKey, err := primitives.PublicKeyToPEM(publicKey, ks.pwd) + if err != nil { + logger.Errorf("Failed converting public key to PEM [%s]: [%s]", alias, err) + return err + } + + err = ioutil.WriteFile(ks.getPathForAlias(alias, "pk"), rawKey, 0700) + if err != nil { + logger.Errorf("Failed storing private key [%s]: [%s]", alias, err) + return err + } + + return nil +} + +func (ks *FileBasedKeyStore) storeKey(alias string, key []byte) error { + if ks.readOnly { + return errors.New("Read only KeyStore.") + } + + pem, err := primitives.AEStoEncryptedPEM(key, ks.pwd) + if err != nil { + logger.Errorf("Failed converting key to PEM [%s]: [%s]", alias, err) + return err + } + + err = ioutil.WriteFile(ks.getPathForAlias(alias, "key"), pem, 0700) + if err != nil { + logger.Errorf("Failed storing key [%s]: [%s]", alias, err) + return err + } + + return nil +} + +func (ks *FileBasedKeyStore) loadPrivateKey(alias string) (interface{}, error) { + path := ks.getPathForAlias(alias, "sk") + logger.Debugf("Loading private key [%s] at [%s]...", alias, path) + + raw, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("Failed loading private key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + privateKey, err := primitives.PEMtoPrivateKey(raw, ks.pwd) + if err != nil { + logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + return privateKey, nil +} + +func (ks *FileBasedKeyStore) loadPublicKey(alias string) (interface{}, error) { + path := ks.getPathForAlias(alias, "pk") + logger.Debugf("Loading public key [%s] at [%s]...", alias, path) + + raw, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("Failed loading public key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + privateKey, err := primitives.PEMtoPublicKey(raw, ks.pwd) + if err != nil { + logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + return privateKey, nil +} + +func (ks *FileBasedKeyStore) loadKey(alias string) ([]byte, error) { + path := ks.getPathForAlias(alias, "key") + logger.Debugf("Loading key [%s] at [%s]...", alias, path) + + pem, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("Failed loading key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + key, err := primitives.PEMtoAES(pem, ks.pwd) + if err != nil { + logger.Errorf("Failed parsing key [%s]: [%s]", alias, err) + + return nil, err + } + + return key, nil +} + +func (ks *FileBasedKeyStore) close() error { + ks.isOpen = false + logger.Debug("Closing keystore...done!") + return nil +} + +func (ks *FileBasedKeyStore) createKeyStoreIfNotExists() error { + // Check keystore directory + ksPath := ks.path + missing, err := utils.DirMissingOrEmpty(ksPath) + logger.Infof("KeyStore path [%s] missing [%t]: [%s]", ksPath, missing, utils.ErrToString(err)) + + if missing { + err := ks.createKeyStore() + if err != nil { + logger.Errorf("Failed creating KeyStore At [%s]: [%s]", ksPath, err.Error()) + return nil + } + } + + return nil +} + +func (ks *FileBasedKeyStore) createKeyStore() error { + // Create keystore directory root if it doesn't exist yet + ksPath := ks.path + logger.Debugf("Creating KeyStore at [%s]...", ksPath) + + os.MkdirAll(ksPath, 0755) + + logger.Debugf("KeyStore created at [%s].", ksPath) + return nil +} + +func (ks *FileBasedKeyStore) deleteKeyStore() error { + logger.Debugf("Removing KeyStore at [%s].", ks.path) + + return os.RemoveAll(ks.path) +} + +func (ks *FileBasedKeyStore) openKeyStore() error { + if ks.isOpen { + return nil + } + + logger.Debugf("KeyStore opened at [%s]...done", ks.path) + + return nil +} + +func (ks *FileBasedKeyStore) getPathForAlias(alias, suffix string) string { + return filepath.Join(ks.path, alias+"_"+suffix) +} diff --git a/core/crypto/bccsp/sw/impl.go b/core/crypto/bccsp/sw/impl.go index 0aaafb34e94..451b3f02db3 100644 --- a/core/crypto/bccsp/sw/impl.go +++ b/core/crypto/bccsp/sw/impl.go @@ -19,7 +19,6 @@ import ( "crypto/ecdsa" "crypto/rand" "encoding/asn1" - "encoding/hex" "errors" "fmt" "math/big" @@ -43,25 +42,32 @@ var ( ) // NewDefaultSecurityLevel returns a new instance of the software-based BCCSP -// at security level 256 and hash family SHA2 -func NewDefaultSecurityLevel() (bccsp.BCCSP, error) { - return New(256, "SHA2") +// at security level 256, hash family SHA2 and using FolderBasedKeyStore as keystore. +func NewDefaultSecurityLevel(keyStorePath string) (bccsp.BCCSP, error) { + ks := &FileBasedKeyStore{} + if err := ks.Init(nil, keyStorePath, false); err != nil { + return nil, fmt.Errorf("Failed initializing key store [%s]", err) + } + + return New(256, "SHA2", ks) } // New returns a new instance of the software-based BCCSP -// set at the passed security level and hash family. -func New(securityLevel int, hashFamily string) (bccsp.BCCSP, error) { +// set at the passed security level, hash family and keystore. +func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) { + // Init config conf := &config{} - err := conf.init(securityLevel, hashFamily) + err := conf.setSecurityLevel(securityLevel, hashFamily) if err != nil { return nil, fmt.Errorf("Failed initializing configuration [%s]", err) } - ks := &keyStore{} - if err := ks.init(nil, conf); err != nil { - return nil, fmt.Errorf("Failed initializing key store [%s]", err) + // Check keystore + if keyStore == nil { + return nil, errors.New("Invalid bccsp.KeyStore instance. It must be different from nil.") } - return &impl{conf, ks}, nil + + return &impl{conf, keyStore}, nil } // SoftwareBasedBCCSP is the software-based implementation of the BCCSP. @@ -70,7 +76,7 @@ func New(securityLevel int, hashFamily string) (bccsp.BCCSP, error) { // It can be configured via viper. type impl struct { conf *config - ks *keyStore + ks bccsp.KeyStore } // KeyGen generates a key using opts. @@ -93,7 +99,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -112,7 +118,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storeKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing AES key [%s]", err) } @@ -131,7 +137,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing AES key [%s]", err) } @@ -203,7 +209,7 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePrivateKey(hex.EncodeToString(reRandomizedKey.SKI()), tempSK) + err = csp.ks.StoreKey(reRandomizedKey) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -234,7 +240,7 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storeKey(hex.EncodeToString(hmacedKey.SKI()), hmacedKey.k) + err = csp.ks.StoreKey(hmacedKey) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -253,7 +259,7 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storeKey(hex.EncodeToString(hmacedKey.SKI()), hmacedKey.k) + err = csp.ks.StoreKey(hmacedKey) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -300,7 +306,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storeKey(hex.EncodeToString(aesK.SKI()), aesK.k) + err = csp.ks.StoreKey(aesK) if err != nil { return nil, fmt.Errorf("Failed storing AES key [%s]", err) } @@ -323,7 +329,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storeKey(hex.EncodeToString(aesK.SKI()), aesK.k) + err = csp.ks.StoreKey(aesK) if err != nil { return nil, fmt.Errorf("Failed storing AES key [%s]", err) } @@ -356,7 +362,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -389,7 +395,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -408,7 +414,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -427,7 +433,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // If the key is not Ephemeral, store it. if !opts.Ephemeral() { // Store the key - err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey) + err = csp.ks.StoreKey(k) if err != nil { return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) } @@ -460,40 +466,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K // GetKey returns the key this CSP associates to // the Subject Key Identifier ski. func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) { - // Validate arguments - if len(ski) == 0 { - return nil, errors.New("Invalid SKI. Cannot be of zero length.") - } - - suffix := csp.ks.getSuffix(hex.EncodeToString(ski)) - - switch suffix { - case "key": - // Load the key - key, err := csp.ks.loadKey(hex.EncodeToString(ski)) - if err != nil { - return nil, fmt.Errorf("Failed loading key [%x] [%s]", ski, err) - } - - return &aesPrivateKey{key, false}, nil - case "sk": - // Load the private key - key, err := csp.ks.loadPrivateKey(hex.EncodeToString(ski)) - if err != nil { - return nil, fmt.Errorf("Failed loading key [%x] [%s]", ski, err) - } - - switch key.(type) { - case *ecdsa.PrivateKey: - return &ecdsaPrivateKey{key.(*ecdsa.PrivateKey)}, nil - case *rsa.PrivateKey: - return &rsaPrivateKey{key.(*rsa.PrivateKey)}, nil - default: - return nil, errors.New("Key type not recognized") - } - default: - return nil, errors.New("Key not recognized") - } + return csp.ks.GetKey(ski) } // Hash hashes messages msg using options opts. diff --git a/core/crypto/bccsp/sw/impl_test.go b/core/crypto/bccsp/sw/impl_test.go index 02b000baf2f..78708ca8bde 100644 --- a/core/crypto/bccsp/sw/impl_test.go +++ b/core/crypto/bccsp/sw/impl_test.go @@ -38,7 +38,6 @@ import ( "github.com/hyperledger/fabric/core/crypto/bccsp" "github.com/hyperledger/fabric/core/crypto/bccsp/signer" "github.com/hyperledger/fabric/core/crypto/primitives" - "github.com/spf13/viper" ) var ( @@ -47,12 +46,10 @@ var ( func getBCCSP(t *testing.T) bccsp.BCCSP { if swBCCSPInstance == nil { - viper.Set("security.bccsp.default.keyStorePath", os.TempDir()) - var err error - swBCCSPInstance, err = NewDefaultSecurityLevel() + swBCCSPInstance, err = NewDefaultSecurityLevel(os.TempDir()) if err != nil { - t.Fatalf("Failed initializing key store [%s]", err) + t.Fatalf("Failed initializing KeyStore [%s]", err) } } diff --git a/core/crypto/bccsp/sw/ks.go b/core/crypto/bccsp/sw/ks.go deleted file mode 100644 index 03e2cae4b03..00000000000 --- a/core/crypto/bccsp/sw/ks.go +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright IBM Corp. 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package sw - -import ( - "io/ioutil" - "os" - "sync" - - "github.com/hyperledger/fabric/core/crypto/utils" - - "errors" - "strings" - - "github.com/hyperledger/fabric/core/crypto/primitives" -) - -type keyStore struct { - conf *config - - isOpen bool - - pwd []byte - - // Sync - m sync.Mutex -} - -func (ks *keyStore) init(pwd []byte, conf *config) error { - // Validate inputs - // pwd can be nil - - if conf == nil { - return errors.New("Invalid config. Nil.") - } - - ks.m.Lock() - defer ks.m.Unlock() - - if ks.isOpen { - return errors.New("Keystore already Initilized.") - } - - ks.conf = conf - ks.pwd = utils.Clone(pwd) - - err := ks.createKeyStoreIfNotExists() - if err != nil { - return err - } - - err = ks.openKeyStore() - if err != nil { - return err - } - - return nil -} - -func (ks *keyStore) getSuffix(alias string) string { - files, _ := ioutil.ReadDir(ks.conf.getKeyStorePath()) - for _, f := range files { - if strings.HasPrefix(f.Name(), alias) { - if strings.HasSuffix(f.Name(), "sk") { - return "sk" - } - if strings.HasSuffix(f.Name(), "pk") { - return "pk" - } - if strings.HasSuffix(f.Name(), "key") { - return "key" - } - break - } - } - return "" -} - -func (ks *keyStore) storePrivateKey(alias string, privateKey interface{}) error { - rawKey, err := primitives.PrivateKeyToPEM(privateKey, ks.pwd) - if err != nil { - logger.Errorf("Failed converting private key to PEM [%s]: [%s]", alias, err) - return err - } - - err = ioutil.WriteFile(ks.conf.getPathForAlias(alias, "sk"), rawKey, 0700) - if err != nil { - logger.Errorf("Failed storing private key [%s]: [%s]", alias, err) - return err - } - - return nil -} - -func (ks *keyStore) loadPrivateKey(alias string) (interface{}, error) { - path := ks.conf.getPathForAlias(alias, "sk") - logger.Debugf("Loading private key [%s] at [%s]...", alias, path) - - raw, err := ioutil.ReadFile(path) - if err != nil { - logger.Errorf("Failed loading private key [%s]: [%s].", alias, err.Error()) - - return nil, err - } - - privateKey, err := primitives.PEMtoPrivateKey(raw, ks.pwd) - if err != nil { - logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) - - return nil, err - } - - return privateKey, nil -} - -func (ks *keyStore) storePublicKey(alias string, publicKey interface{}) error { - rawKey, err := primitives.PublicKeyToPEM(publicKey, ks.pwd) - if err != nil { - logger.Errorf("Failed converting public key to PEM [%s]: [%s]", alias, err) - return err - } - - err = ioutil.WriteFile(ks.conf.getPathForAlias(alias, "pk"), rawKey, 0700) - if err != nil { - logger.Errorf("Failed storing private key [%s]: [%s]", alias, err) - return err - } - - return nil -} - -func (ks *keyStore) loadPublicKey(alias string) (interface{}, error) { - path := ks.conf.getPathForAlias(alias, "pk") - logger.Debugf("Loading public key [%s] at [%s]...", alias, path) - - raw, err := ioutil.ReadFile(path) - if err != nil { - logger.Errorf("Failed loading public key [%s]: [%s].", alias, err.Error()) - - return nil, err - } - - privateKey, err := primitives.PEMtoPublicKey(raw, ks.pwd) - if err != nil { - logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) - - return nil, err - } - - return privateKey, nil -} - -func (ks *keyStore) storeKey(alias string, key []byte) error { - pem, err := primitives.AEStoEncryptedPEM(key, ks.pwd) - if err != nil { - logger.Errorf("Failed converting key to PEM [%s]: [%s]", alias, err) - return err - } - - err = ioutil.WriteFile(ks.conf.getPathForAlias(alias, "key"), pem, 0700) - if err != nil { - logger.Errorf("Failed storing key [%s]: [%s]", alias, err) - return err - } - - return nil -} - -func (ks *keyStore) loadKey(alias string) ([]byte, error) { - path := ks.conf.getPathForAlias(alias, "key") - logger.Debugf("Loading key [%s] at [%s]...", alias, path) - - pem, err := ioutil.ReadFile(path) - if err != nil { - logger.Errorf("Failed loading key [%s]: [%s].", alias, err.Error()) - - return nil, err - } - - key, err := primitives.PEMtoAES(pem, ks.pwd) - if err != nil { - logger.Errorf("Failed parsing key [%s]: [%s]", alias, err) - - return nil, err - } - - return key, nil -} - -func (ks *keyStore) close() error { - ks.isOpen = false - logger.Debug("Closing keystore...done!") - return nil -} - -func (ks *keyStore) createKeyStoreIfNotExists() error { - // Check keystore directory - ksPath := ks.conf.getKeyStorePath() - missing, err := utils.DirMissingOrEmpty(ksPath) - logger.Infof("Keystore path [%s] missing [%t]: [%s]", ksPath, missing, utils.ErrToString(err)) - - if missing { - err := ks.createKeyStore() - if err != nil { - logger.Errorf("Failed creating ks At [%s]: [%s]", ksPath, err.Error()) - return nil - } - } - - return nil -} - -func (ks *keyStore) createKeyStore() error { - // Create keystore directory root if it doesn't exist yet - ksPath := ks.conf.getKeyStorePath() - logger.Debugf("Creating Keystore at [%s]...", ksPath) - - os.MkdirAll(ksPath, 0755) - - logger.Debugf("Keystore created at [%s].", ksPath) - return nil -} - -func (ks *keyStore) deleteKeyStore() error { - logger.Debugf("Removing KeyStore at [%s].", ks.conf.getKeyStorePath()) - - return os.RemoveAll(ks.conf.getKeyStorePath()) -} - -func (ks *keyStore) openKeyStore() error { - if ks.isOpen { - return nil - } - - logger.Debugf("Keystore opened at [%s]...done", ks.conf.getKeyStorePath()) - - return nil -}