diff --git a/bccsp/factory/factory.go b/bccsp/factory/factory.go index 0ba1145dea1..d62da5e76cb 100644 --- a/bccsp/factory/factory.go +++ b/bccsp/factory/factory.go @@ -41,7 +41,7 @@ var ( // Factories' Initialization Error factoriesInitError error - logger = logging.MustGetLogger("BCCSP_FACTORY") + logger = logging.MustGetLogger("bccsp") ) // BCCSPFactory is used to get instances of the BCCSP interface. diff --git a/bccsp/factory/factory_test.go b/bccsp/factory/factory_test.go index 74290a87e17..5b23ba60af8 100644 --- a/bccsp/factory/factory_test.go +++ b/bccsp/factory/factory_test.go @@ -23,12 +23,13 @@ import ( "os" "testing" + "github.com/hyperledger/fabric/bccsp/pkcs11" "github.com/spf13/viper" ) func TestMain(m *testing.M) { flag.Parse() - lib, pin, label, enable := findPKCS11Lib() + lib, pin, label := pkcs11.FindPKCS11Lib() var jsonBCCSP, yamlBCCSP *FactoryOpts jsonCFG := []byte( @@ -55,7 +56,7 @@ BCCSP: Label: %s `, lib, pin, label) - if !enable { + if lib == "" { fmt.Printf("Could not find PKCS11 libraries, running without\n") yamlCFG = ` BCCSP: @@ -121,34 +122,3 @@ func TestGetBCCSP(t *testing.T) { t.Fatal("Failed Software BCCSP. Nil instance.") } } - -func findPKCS11Lib() (lib, pin, label string, enablePKCS11tests bool) { - //FIXME: Till we workout the configuration piece, look for the libraries in the familiar places - lib = os.Getenv("PKCS11_LIB") - if lib == "" { - pin = "98765432" - label = "ForFabric" - possibilities := []string{ - "/usr/lib/softhsm/libsofthsm2.so", //Debian - "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so", //Ubuntu - "/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so", //Ubuntu - "/usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so", //Power - "/usr/local/Cellar/softhsm/2.1.0/lib/softhsm/libsofthsm2.so", //MacOS - } - for _, path := range possibilities { - if _, err := os.Stat(path); !os.IsNotExist(err) { - lib = path - enablePKCS11tests = true - break - } - } - if lib == "" { - enablePKCS11tests = false - } - } else { - enablePKCS11tests = true - pin = os.Getenv("PKCS11_PIN") - label = os.Getenv("PKCS11_LABEL") - } - return lib, pin, label, enablePKCS11tests -} diff --git a/bccsp/factory/nopkcs11.go b/bccsp/factory/nopkcs11.go index 74e4c0bcf9e..409373c1fce 100644 --- a/bccsp/factory/nopkcs11.go +++ b/bccsp/factory/nopkcs11.go @@ -68,3 +68,18 @@ func InitFactories(config *FactoryOpts) error { return factoriesInitError } + +// GetBCCSPFromOpts returns a BCCSP created according to the options passed in input. +func GetBCCSPFromOpts(config *FactoryOpts) (bccsp.BCCSP, error) { + var f BCCSPFactory + switch config.ProviderName { + case "SW": + f = &SWFactory{} + } + + csp, err := f.Get(config) + if err != nil { + return nil, fmt.Errorf("Could not initialize BCCSP %s [%s]", f.Name(), err) + } + return csp, nil +} diff --git a/bccsp/factory/pkcs11.go b/bccsp/factory/pkcs11.go index 55f3c96b826..cafdd3a6f08 100644 --- a/bccsp/factory/pkcs11.go +++ b/bccsp/factory/pkcs11.go @@ -21,12 +21,13 @@ import ( "fmt" "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/pkcs11" ) type FactoryOpts struct { - ProviderName string `mapstructure:"default" json:"default" yaml:"Default"` - SwOpts *SwOpts `mapstructure:"SW,omitempty" json:"SW,omitempty" yaml:"SwOpts"` - Pkcs11Opts *PKCS11Opts `mapstructure:"PKCS11,omitempty" json:"PKCS11,omitempty" yaml:"PKCS11"` + ProviderName string `mapstructure:"default" json:"default" yaml:"Default"` + SwOpts *SwOpts `mapstructure:"SW,omitempty" json:"SW,omitempty" yaml:"SwOpts"` + Pkcs11Opts *pkcs11.PKCS11Opts `mapstructure:"PKCS11,omitempty" json:"PKCS11,omitempty" yaml:"PKCS11"` } // InitFactories must be called before using factory interfaces @@ -78,3 +79,20 @@ func InitFactories(config *FactoryOpts) error { return factoriesInitError } + +// GetBCCSPFromOpts returns a BCCSP created according to the options passed in input. +func GetBCCSPFromOpts(config *FactoryOpts) (bccsp.BCCSP, error) { + var f BCCSPFactory + switch config.ProviderName { + case "SW": + f = &SWFactory{} + case "PKCS11": + f = &PKCS11Factory{} + } + + csp, err := f.Get(config) + if err != nil { + return nil, fmt.Errorf("Could not initialize BCCSP %s [%s]", f.Name(), err) + } + return csp, nil +} diff --git a/bccsp/factory/pkcs11factory.go b/bccsp/factory/pkcs11factory.go index 8cf2fd806e3..10a7bb17d8a 100644 --- a/bccsp/factory/pkcs11factory.go +++ b/bccsp/factory/pkcs11factory.go @@ -42,7 +42,7 @@ func (f *PKCS11Factory) Name() string { // Get returns an instance of BCCSP using Opts. func (f *PKCS11Factory) Get(config *FactoryOpts) (bccsp.BCCSP, error) { // Validate arguments - if config == nil || config.SwOpts == nil { + if config == nil || config.Pkcs11Opts == nil { return nil, errors.New("Invalid config. It must not be nil.") } @@ -62,29 +62,5 @@ func (f *PKCS11Factory) Get(config *FactoryOpts) (bccsp.BCCSP, error) { // Default to DummyKeystore ks = sw.NewDummyKeyStore() } - err := pkcs11.InitPKCS11(p11Opts.Library, p11Opts.Pin, p11Opts.Label) - if err != nil { - return nil, fmt.Errorf("Failed initializing PKCS11 library %s %s [%s]", - p11Opts.Library, p11Opts.Label, err) - } - return pkcs11.New(p11Opts.SecLevel, p11Opts.HashFamily, ks) -} - -// PKCS11Opts contains options for the P11Factory -type PKCS11Opts struct { - // Default algorithms when not specified (Deprecated?) - SecLevel int `mapstructure:"security" json:"security"` - HashFamily string `mapstructure:"hash" json:"hash"` - - // Keystore options - Ephemeral bool `mapstructure:"tempkeys,omitempty" json:"tempkeys,omitempty"` - FileKeystore *FileKeystoreOpts `mapstructure:"filekeystore,omitempty" json:"filekeystore,omitempty"` - DummyKeystore *DummyKeystoreOpts `mapstructure:"dummykeystore,omitempty" json:"dummykeystore,omitempty"` - - // PKCS11 options - Library string `mapstructure:"library" json:"library"` - Label string `mapstructure:"label" json:"label"` - Pin string `mapstructure:"pin" json:"pin"` - Sensitive bool `mapstructure:"sensitivekeys,omitempty" json:"sensitivekeys,omitempty"` - SoftVerify bool `mapstructure:"softwareverify,omitempty" json:"softwareverify,omitempty"` + return pkcs11.New(*p11Opts, ks) } diff --git a/bccsp/pkcs11/conf.go b/bccsp/pkcs11/conf.go index 556a97e8cb0..156f7a0576c 100644 --- a/bccsp/pkcs11/conf.go +++ b/bccsp/pkcs11/conf.go @@ -83,3 +83,30 @@ func (conf *config) setSecurityLevelSHA3(level int) (err error) { } return } + +// PKCS11Opts contains options for the P11Factory +type PKCS11Opts struct { + // Default algorithms when not specified (Deprecated?) + SecLevel int `mapstructure:"security" json:"security"` + HashFamily string `mapstructure:"hash" json:"hash"` + + // Keystore options + Ephemeral bool `mapstructure:"tempkeys,omitempty" json:"tempkeys,omitempty"` + FileKeystore *FileKeystoreOpts `mapstructure:"filekeystore,omitempty" json:"filekeystore,omitempty"` + DummyKeystore *DummyKeystoreOpts `mapstructure:"dummykeystore,omitempty" json:"dummykeystore,omitempty"` + + // PKCS11 options + Library string `mapstructure:"library" json:"library"` + Label string `mapstructure:"label" json:"label"` + Pin string `mapstructure:"pin" json:"pin"` + Sensitive bool `mapstructure:"sensitivekeys,omitempty" json:"sensitivekeys,omitempty"` + SoftVerify bool `mapstructure:"softwareverify,omitempty" json:"softwareverify,omitempty"` +} + +// Since currently only ECDSA operations go to PKCS11, need a keystore still +// Pluggable Keystores, could add JKS, P12, etc.. +type FileKeystoreOpts struct { + KeyStorePath string `mapstructure:"keystore" json:"keystore" yaml:"KeyStore"` +} + +type DummyKeystoreOpts struct{} diff --git a/bccsp/pkcs11/ecdsa.go b/bccsp/pkcs11/ecdsa.go index e78295242d5..d6b0bc4cd51 100644 --- a/bccsp/pkcs11/ecdsa.go +++ b/bccsp/pkcs11/ecdsa.go @@ -16,6 +16,7 @@ limitations under the License. package pkcs11 import ( + "crypto/ecdsa" "crypto/elliptic" "encoding/asn1" "errors" @@ -73,7 +74,7 @@ func unmarshalECDSASignature(raw []byte) (*big.Int, *big.Int, error) { } func (csp *impl) signECDSA(k ecdsaPrivateKey, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) { - r, s, err := signECDSA(k.ski, digest) + r, s, err := csp.signP11ECDSA(k.ski, digest) if err != nil { return nil, err } @@ -111,5 +112,9 @@ func (csp *impl) verifyECDSA(k ecdsaPublicKey, signature, digest []byte, opts bc return false, fmt.Errorf("Invalid S. Must be smaller than half the order [%s][%s].", s, halfOrder) } - return verifyECDSA(k.ski, digest, r, s, k.pub.Curve.Params().BitSize/8) + if csp.softVerify { + return ecdsa.Verify(k.pub, digest, r, s), nil + } else { + return csp.verifyP11ECDSA(k.ski, digest, r, s, k.pub.Curve.Params().BitSize/8) + } } diff --git a/bccsp/pkcs11/impl.go b/bccsp/pkcs11/impl.go index 0f29c8f58e8..e9407a1d0b1 100644 --- a/bccsp/pkcs11/impl.go +++ b/bccsp/pkcs11/impl.go @@ -28,40 +28,26 @@ import ( "fmt" "hash" "math/big" + "os" "github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric/bccsp/utils" + "github.com/miekg/pkcs11" "github.com/op/go-logging" "golang.org/x/crypto/sha3" ) var ( - logger = logging.MustGetLogger("PKCS11_BCCSP") + logger = logging.MustGetLogger("bccsp_p11") + sessionCacheSize = 10 ) -// NewDefaultSecurityLevel returns a new instance of the software-based BCCSP -// 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) -} - -// NewDefaultSecurityLevel returns a new instance of the software-based BCCSP -// at security level 256, hash family SHA2 and using the passed KeyStore. -func NewDefaultSecurityLevelWithKeystore(keyStore bccsp.KeyStore) (bccsp.BCCSP, error) { - return New(256, "SHA2", keyStore) -} - // New returns a new instance of the software-based BCCSP // set at the passed security level, hash family and KeyStore. -func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) { +func New(opts PKCS11Opts, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) { // Init config conf := &config{} - err := conf.setSecurityLevel(securityLevel, hashFamily) + err := conf.setSecurityLevel(opts.SecLevel, opts.HashFamily) if err != nil { return nil, fmt.Errorf("Failed initializing configuration [%s]", err) } @@ -71,13 +57,32 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B return nil, errors.New("Invalid bccsp.KeyStore instance. It must be different from nil.") } - return &impl{conf, keyStore}, nil + lib := opts.Library + pin := opts.Pin + label := opts.Label + ctx, slot, session, err := loadLib(lib, pin, label) + if err != nil { + return nil, fmt.Errorf("Failed initializing PKCS11 library %s %s [%s]", + lib, label, err) + } + + sessions := make(chan pkcs11.SessionHandle, sessionCacheSize) + csp := &impl{conf, keyStore, ctx, sessions, slot, lib, opts.Sensitive, opts.SoftVerify} + csp.returnSession(*session) + return csp, nil } -// SoftwareBasedBCCSP is the software-based implementation of the BCCSP. type impl struct { conf *config ks bccsp.KeyStore + + ctx *pkcs11.Ctx + sessions chan pkcs11.SessionHandle + slot uint + + lib string + noPrivImport bool + softVerify bool } // KeyGen generates a key using opts. @@ -92,7 +97,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { // Parse algorithm switch opts.(type) { case *bccsp.ECDSAKeyGenOpts: - ski, pub, err := generateECKey(csp.conf.ellipticCurve, opts.Ephemeral()) + ski, pub, err := csp.generateECKey(csp.conf.ellipticCurve, opts.Ephemeral()) if err != nil { return nil, fmt.Errorf("Failed generating ECDSA key [%s]", err) } @@ -100,7 +105,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { pkcs11Stored = true case *bccsp.ECDSAP256KeyGenOpts: - ski, pub, err := generateECKey(oidNamedCurveP256, opts.Ephemeral()) + ski, pub, err := csp.generateECKey(oidNamedCurveP256, opts.Ephemeral()) if err != nil { return nil, fmt.Errorf("Failed generating ECDSA P256 key [%s]", err) } @@ -109,7 +114,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { pkcs11Stored = true case *bccsp.ECDSAP384KeyGenOpts: - ski, pub, err := generateECKey(oidNamedCurveP384, opts.Ephemeral()) + ski, pub, err := csp.generateECKey(oidNamedCurveP384, opts.Ephemeral()) if err != nil { return nil, fmt.Errorf("Failed generating ECDSA P384 key [%s]", err) } @@ -269,7 +274,7 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e return nil, errors.New("Do not know OID for this Curve.") } - ski, err := importECKey(oid, nil, ecPt, opts.Ephemeral(), isPublicKey) + ski, err := csp.importECKey(oid, nil, ecPt, opts.Ephemeral(), publicKeyFlag) if err != nil { return nil, fmt.Errorf("Failed getting importing EC Public Key [%s]", err) } @@ -295,7 +300,7 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e case *bccsp.ECDSAReRandKeyOpts: reRandOpts := opts.(*bccsp.ECDSAReRandKeyOpts) pubKey := ecdsaK.pub.pub - secret := getSecretValue(ecdsaK.ski) + secret := csp.getSecretValue(ecdsaK.ski) if secret == nil { return nil, errors.New("Could not obtain EC Private Key") } @@ -334,7 +339,7 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e return nil, errors.New("Do not know OID for this Curve.") } - ski, err := importECKey(oid, tempSK.D.Bytes(), ecPt, opts.Ephemeral(), isPrivateKey) + ski, err := csp.importECKey(oid, tempSK.D.Bytes(), ecPt, opts.Ephemeral(), privateKeyFlag) if err != nil { return nil, fmt.Errorf("Failed getting importing EC Public Key [%s]", err) } @@ -488,15 +493,33 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K return nil, errors.New("Do not know OID for this Curve.") } - ski, err := importECKey(oid, nil, ecPt, opts.Ephemeral(), isPublicKey) - if err != nil { - return nil, fmt.Errorf("Failed getting importing EC Public Key [%s]", err) + var ski []byte + if csp.noPrivImport { + // opencryptoki does not support public ec key imports. This is a sufficient + // workaround for now to use soft verify + hash := sha256.Sum256(ecPt) + ski = hash[:] + } else { + // Warn about potential future problems + if !csp.softVerify { + logger.Debugf("opencryptoki workaround warning: Importing public EC Key does not store out to pkcs11 store,\n" + + "so verify with this key will fail, unless key is already present in store. Enable 'softwareverify'\n" + + "in pkcs11 options, if suspect this issue.") + } + ski, err = csp.importECKey(oid, nil, ecPt, opts.Ephemeral(), publicKeyFlag) + if err != nil { + return nil, fmt.Errorf("Failed getting importing EC Public Key [%s]", err) + } } k = &ecdsaPublicKey{ski, ecdsaPK} return k, nil case *bccsp.ECDSAPrivateKeyImportOpts: + if csp.noPrivImport { + return nil, errors.New("[ECDSADERPrivateKeyImportOpts] PKCS11 options 'sensitivekeys' is set to true. Cannot import.") + } + der, ok := raw.([]byte) if !ok { return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw material. Expected byte array.") @@ -522,7 +545,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K return nil, errors.New("Do not know OID for this Curve.") } - ski, err := importECKey(oid, ecdsaSK.D.Bytes(), ecPt, opts.Ephemeral(), isPrivateKey) + ski, err := csp.importECKey(oid, ecdsaSK.D.Bytes(), ecPt, opts.Ephemeral(), privateKeyFlag) if err != nil { return nil, fmt.Errorf("Failed getting importing EC Private Key [%s]", err) } @@ -542,9 +565,23 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K return nil, errors.New("Do not know OID for this Curve.") } - ski, err := importECKey(oid, nil, ecPt, opts.Ephemeral(), isPublicKey) - if err != nil { - return nil, fmt.Errorf("Failed getting importing EC Public Key [%s]", err) + var ski []byte + if csp.noPrivImport { + // opencryptoki does not support public ec key imports. This is a sufficient + // workaround for now to use soft verify + hash := sha256.Sum256(ecPt) + ski = hash[:] + } else { + // Warn about potential future problems + if !csp.softVerify { + logger.Debugf("opencryptoki workaround warning: Importing public EC Key does not store out to pkcs11 store,\n" + + "so verify with this key will fail, unless key is already present in store. Enable 'softwareverify'\n" + + "in pkcs11 options, if suspect this issue.") + } + ski, err = csp.importECKey(oid, nil, ecPt, opts.Ephemeral(), publicKeyFlag) + if err != nil { + return nil, fmt.Errorf("Failed getting importing EC Public Key [%s]", err) + } } k = &ecdsaPublicKey{ski, lowLevelKey} @@ -594,7 +631,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) { - pubKey, isPriv, err := getECKey(ski) + pubKey, isPriv, err := csp.getECKey(ski) if err == nil { if isPriv { return &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pubKey}}, nil @@ -680,7 +717,8 @@ func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signat return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts) default: - return nil, fmt.Errorf("Key type not recognized [%s]", k) + //return nil, fmt.Errorf("Key type not recognized [%s]", k) + panic(fmt.Errorf("Key type not recognized - [%+v] [%#v] [%T] [%T]", k, k, k, k)) } } @@ -783,3 +821,32 @@ func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpt return nil, fmt.Errorf("Key type not recognized [%s]", k) } } + +// THIS IS ONLY USED FOR TESTING +// This is a convenience function. Useful to self-configure, for tests where usual configuration is not +// available +func FindPKCS11Lib() (lib, pin, label string) { + //FIXME: Till we workout the configuration piece, look for the libraries in the familiar places + lib = os.Getenv("PKCS11_LIB") + if lib == "" { + pin = "98765432" + label = "ForFabric" + possibilities := []string{ + "/usr/lib/softhsm/libsofthsm2.so", //Debian + "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so", //Ubuntu + "/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so", //Ubuntu + "/usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so", //Power + "/usr/local/Cellar/softhsm/2.1.0/lib/softhsm/libsofthsm2.so", //MacOS + } + for _, path := range possibilities { + if _, err := os.Stat(path); !os.IsNotExist(err) { + lib = path + break + } + } + } else { + pin = os.Getenv("PKCS11_PIN") + label = os.Getenv("PKCS11_LABEL") + } + return lib, pin, label +} diff --git a/bccsp/pkcs11/impl_test.go b/bccsp/pkcs11/impl_test.go index b08283da5f1..219691dc356 100644 --- a/bccsp/pkcs11/impl_test.go +++ b/bccsp/pkcs11/impl_test.go @@ -32,12 +32,12 @@ import ( "math/big" "net" "os" + "strings" "testing" "time" "github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric/bccsp/signer" - "github.com/hyperledger/fabric/bccsp/sw" "github.com/hyperledger/fabric/bccsp/utils" "golang.org/x/crypto/sha3" ) @@ -45,13 +45,14 @@ import ( var ( currentKS bccsp.KeyStore currentBCCSP bccsp.BCCSP - currentSWBCCSP bccsp.BCCSP currentTestConfig testConfig ) type testConfig struct { securityLevel int hashFamily string + softVerify bool + noKeyImport bool } func TestMain(m *testing.M) { @@ -62,41 +63,43 @@ func TestMain(m *testing.M) { } currentKS = ks - lib, pin, label := findPKCS11Lib() - if enablePKCS11tests { - err := InitPKCS11(lib, pin, label) - if err != nil { - fmt.Printf("Failed initializing PKCS11 library [%s]", err) - os.Exit(-1) - } - } else { - fmt.Printf("No PKCS11 library found, skipping PKCS11 tests") + lib, pin, label := FindPKCS11Lib() + tests := []testConfig{ + {256, "SHA2", true, true}, + {256, "SHA3", false, true}, + {384, "SHA2", false, true}, + {384, "SHA3", false, true}, + {384, "SHA3", true, true}, } - tests := []testConfig{ - {256, "SHA2"}, - {256, "SHA3"}, - {384, "SHA2"}, - {384, "SHA3"}, + if strings.Contains(lib, "softhsm") { + tests = append(tests, []testConfig{ + {256, "SHA2", true, false}, + }...) } + opts := PKCS11Opts{ + Library: lib, + Label: label, + Pin: pin, + } for _, config := range tests { var err error currentTestConfig = config - currentBCCSP, err = New(config.securityLevel, config.hashFamily, currentKS) - if err != nil { - fmt.Printf("Failed initiliazing BCCSP at [%d, %s]: [%s]", config.securityLevel, config.hashFamily, err) - os.Exit(-1) - } - currentSWBCCSP, err = sw.New(config.securityLevel, config.hashFamily, sw.NewDummyKeyStore()) + opts.HashFamily = config.hashFamily + opts.SecLevel = config.securityLevel + opts.SoftVerify = config.softVerify + opts.Sensitive = config.noKeyImport + currentBCCSP, err = New(opts, currentKS) if err != nil { - fmt.Printf("Failed initiliazing BCCSP at [%d, %s]: [%s]", config.securityLevel, config.hashFamily, err) + fmt.Printf("Failed initiliazing BCCSP at [%+v]: [%s]", opts, err) os.Exit(-1) } + ret := m.Run() if ret != 0 { - fmt.Printf("Failed testing at [%d, %s]", config.securityLevel, config.hashFamily) + fmt.Printf("Failed testing at [%+v]", opts) os.Exit(-1) } } @@ -104,7 +107,18 @@ func TestMain(m *testing.M) { } func TestInvalidNewParameter(t *testing.T) { - r, err := New(0, "SHA2", currentKS) + lib, pin, label := FindPKCS11Lib() + opts := PKCS11Opts{ + Library: lib, + Label: label, + Pin: pin, + SoftVerify: true, + Sensitive: true, + } + + opts.HashFamily = "SHA2" + opts.SecLevel = 0 + r, err := New(opts, currentKS) if err == nil { t.Fatal("Error should be different from nil in this case") } @@ -112,7 +126,9 @@ func TestInvalidNewParameter(t *testing.T) { t.Fatal("Return value should be equal to nil in this case") } - r, err = New(256, "SHA8", currentKS) + opts.HashFamily = "SHA8" + opts.SecLevel = 256 + r, err = New(opts, currentKS) if err == nil { t.Fatal("Error should be different from nil in this case") } @@ -120,7 +136,9 @@ func TestInvalidNewParameter(t *testing.T) { t.Fatal("Return value should be equal to nil in this case") } - r, err = New(256, "SHA2", nil) + opts.HashFamily = "SHA2" + opts.SecLevel = 256 + r, err = New(opts, nil) if err == nil { t.Fatal("Error should be different from nil in this case") } @@ -128,15 +146,9 @@ func TestInvalidNewParameter(t *testing.T) { t.Fatal("Return value should be equal to nil in this case") } - r, err = New(0, "SHA3", nil) - if err == nil { - t.Fatal("Error should be different from nil in this case") - } - if r != nil { - t.Fatal("Return value should be equal to nil in this case") - } - - r, err = NewDefaultSecurityLevel("") + opts.HashFamily = "SHA3" + opts.SecLevel = 0 + r, err = New(opts, nil) if err == nil { t.Fatal("Error should be different from nil in this case") } @@ -586,6 +598,10 @@ func TestECDSAPublicKeySKI(t *testing.T) { func TestECDSAKeyReRand(t *testing.T) { + if currentBCCSP.(*impl).noPrivImport { + t.Skip("Key import turned off. Skipping Derivation tests as they currently require Key Import.") + } + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) if err != nil { t.Fatalf("Failed generating ECDSA key [%s]", err) @@ -721,6 +737,10 @@ func TestECDSAVerify(t *testing.T) { func TestECDSAKeyDeriv(t *testing.T) { + if currentBCCSP.(*impl).noPrivImport { + t.Skip("Key import turned off. Skipping Derivation tests as they currently require Key Import.") + } + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) if err != nil { t.Fatalf("Failed generating ECDSA key [%s]", err) @@ -858,6 +878,9 @@ func TestECDSAKeyImportFromECDSAPublicKey(t *testing.T) { } func TestECDSAKeyImportFromECDSAPrivateKey(t *testing.T) { + if currentBCCSP.(*impl).noPrivImport { + t.Skip("Key import turned off. Skipping Derivation tests as they currently require Key Import.") + } // Generate an ECDSA key, default is P256 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -1121,7 +1144,7 @@ func TestECDSALowS(t *testing.T) { // Ensure that signature with high-S are rejected. for { - R, S, err = signECDSA(k.SKI(), digest) + R, S, err = currentBCCSP.(*impl).signP11ECDSA(k.SKI(), digest) if err != nil { t.Fatalf("Failed generating signature [%s]", err) } @@ -1927,36 +1950,3 @@ func getCryptoHashIndex(t *testing.T) crypto.Hash { return crypto.SHA3_256 } - -var enablePKCS11tests = false - -func findPKCS11Lib() (lib, pin, label string) { - //FIXME: Till we workout the configuration piece, look for the libraries in the familiar places - lib = os.Getenv("PKCS11_LIB") - if lib == "" { - pin = "98765432" - label = "ForFabric" - possibilities := []string{ - "/usr/lib/softhsm/libsofthsm2.so", //Debian - "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so", //Ubuntu - "/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so", //Ubuntu - "/usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so", //Power - "/usr/local/Cellar/softhsm/2.1.0/lib/softhsm/libsofthsm2.so", //MacOS - } - for _, path := range possibilities { - if _, err := os.Stat(path); !os.IsNotExist(err) { - lib = path - enablePKCS11tests = true - break - } - } - if lib == "" { - enablePKCS11tests = false - } - } else { - enablePKCS11tests = true - pin = os.Getenv("PKCS11_PIN") - label = os.Getenv("PKCS11_LABEL") - } - return lib, pin, label -} diff --git a/bccsp/pkcs11/pkcs11.go b/bccsp/pkcs11/pkcs11.go index d650fbf6059..927778dc578 100644 --- a/bccsp/pkcs11/pkcs11.go +++ b/bccsp/pkcs11/pkcs11.go @@ -30,35 +30,22 @@ import ( "github.com/op/go-logging" ) -var ( - ctx *pkcs11.Ctx - sessions = make(chan pkcs11.SessionHandle, 2000) - slot uint - probablySoftHSM = false //Only needed for KeyImport -) - -func InitPKCS11(lib, pin, label string) error { - if strings.Contains(lib, "softhsm") { - probablySoftHSM = true - } - return loadLib(lib, pin, label) -} - -func loadLib(lib, pin, label string) error { +func loadLib(lib, pin, label string) (*pkcs11.Ctx, uint, *pkcs11.SessionHandle, error) { + var slot uint = 0 logger.Debugf("Loading pkcs11 library [%s]\n", lib) if lib == "" { - return fmt.Errorf("No PKCS11 library default") + return nil, slot, nil, fmt.Errorf("No PKCS11 library default") } - ctx = pkcs11.New(lib) + ctx := pkcs11.New(lib) if ctx == nil { - return fmt.Errorf("Instantiate failed [%s]", lib) + return nil, slot, nil, fmt.Errorf("Instantiate failed [%s]", lib) } ctx.Initialize() slots, err := ctx.GetSlotList(true) if err != nil { - return err + return nil, slot, nil, fmt.Errorf("Could not get Slot List [%s]", err) } found := false for _, s := range slots { @@ -66,6 +53,7 @@ func loadLib(lib, pin, label string) error { if err != nil { continue } + logger.Debugf("Looking for %s, found label %s\n", label, info.Label) if label == info.Label { found = true slot = s @@ -73,33 +61,47 @@ func loadLib(lib, pin, label string) error { } } if !found { - return fmt.Errorf("Could not find token with label %s", label) + return nil, slot, nil, fmt.Errorf("Could not find token with label %s", label) + } + + var session pkcs11.SessionHandle + for i := 0; i < 10; i++ { + session, err = ctx.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + if err != nil { + logger.Warningf("OpenSession failed, retrying [%s]\n", err) + } else { + break + } + } + if err != nil { + logger.Fatalf("OpenSession [%s]\n", err) } - session := getSession() - defer returnSession(session) + logger.Debugf("Created new pkcs11 session %+v on slot %d\n", session, slot) if pin == "" { - return fmt.Errorf("No PIN set\n") + return nil, slot, nil, fmt.Errorf("No PIN set\n") } err = ctx.Login(session, pkcs11.CKU_USER, pin) if err != nil { - return fmt.Errorf("Login failed [%s]\n", err) + if err != pkcs11.Error(pkcs11.CKR_USER_ALREADY_LOGGED_IN) { + return nil, slot, nil, fmt.Errorf("Login failed [%s]\n", err) + } } - return nil + return ctx, slot, &session, nil } -func getSession() (session pkcs11.SessionHandle) { +func (csp *impl) getSession() (session pkcs11.SessionHandle) { select { - case session = <-sessions: - logger.Debugf("Reusing existing pkcs11 session %x on slot %d\n", session, slot) + case session = <-csp.sessions: + logger.Debugf("Reusing existing pkcs11 session %+v on slot %d\n", session, csp.slot) default: - // create one + // cache is empty (or completely in use), create a new session var s pkcs11.SessionHandle var err error = nil for i := 0; i < 10; i++ { - s, err = ctx.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + s, err = csp.ctx.OpenSession(csp.slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) if err != nil { logger.Warningf("OpenSession failed, retrying [%s]\n", err) } else { @@ -107,32 +109,38 @@ func getSession() (session pkcs11.SessionHandle) { } } if err != nil { - logger.Fatalf("OpenSession [%s]\n", err) + panic(fmt.Errorf("OpenSession failed [%s]\n", err)) } - logger.Debugf("Created new pkcs11 session %x on slot %d\n", session, slot) + logger.Debugf("Created new pkcs11 session %+v on slot %d\n", s, csp.slot) session = s } return session } -func returnSession(session pkcs11.SessionHandle) { - sessions <- session +func (csp *impl) returnSession(session pkcs11.SessionHandle) { + select { + case csp.sessions <- session: + // returned session back to session cache + default: + // have plenty of sessions in cache, dropping + csp.ctx.CloseSession(session) + } } // Look for an EC key by SKI, stored in CKA_ID // This function can probably be addapted for both EC and RSA keys. -func getECKey(ski []byte) (pubKey *ecdsa.PublicKey, isPriv bool, err error) { - p11lib := ctx - session := getSession() - defer returnSession(session) +func (csp *impl) getECKey(ski []byte) (pubKey *ecdsa.PublicKey, isPriv bool, err error) { + p11lib := csp.ctx + session := csp.getSession() + defer csp.returnSession(session) isPriv = true - _, err = findKeyPairFromSKI(p11lib, session, ski, isPrivateKey) + _, err = findKeyPairFromSKI(p11lib, session, ski, privateKeyFlag) if err != nil { isPriv = false logger.Debugf("Private key not found [%s] for SKI [%s], looking for Public key", err, hex.EncodeToString(ski)) } - publicKey, err := findKeyPairFromSKI(p11lib, session, ski, isPublicKey) + publicKey, err := findKeyPairFromSKI(p11lib, session, ski, publicKeyFlag) if err != nil { return nil, false, fmt.Errorf("Public key not found [%s] for SKI [%s]", err, hex.EncodeToString(ski)) } @@ -212,10 +220,10 @@ func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { return nil, false } -func generateECKey(curve asn1.ObjectIdentifier, ephemeral bool) (ski []byte, pubKey *ecdsa.PublicKey, err error) { - p11lib := ctx - session := getSession() - defer returnSession(session) +func (csp *impl) generateECKey(curve asn1.ObjectIdentifier, ephemeral bool) (ski []byte, pubKey *ecdsa.PublicKey, err error) { + p11lib := csp.ctx + session := csp.getSession() + defer csp.returnSession(session) id := nextIDCtr() publabel := fmt.Sprintf("BCPUB%s", id.Text(16)) @@ -232,7 +240,7 @@ func generateECKey(curve asn1.ObjectIdentifier, ephemeral bool) (ski []byte, pub pkcs11.NewAttribute(pkcs11.CKA_TOKEN, !ephemeral), pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true), pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, marshaledOID), - pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), + pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), pkcs11.NewAttribute(pkcs11.CKA_ID, publabel), pkcs11.NewAttribute(pkcs11.CKA_LABEL, publabel), @@ -242,13 +250,13 @@ func generateECKey(curve asn1.ObjectIdentifier, ephemeral bool) (ski []byte, pub pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_EC), pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), pkcs11.NewAttribute(pkcs11.CKA_TOKEN, !ephemeral), - pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), + pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), pkcs11.NewAttribute(pkcs11.CKA_SIGN, true), pkcs11.NewAttribute(pkcs11.CKA_ID, prvlabel), pkcs11.NewAttribute(pkcs11.CKA_LABEL, prvlabel), - pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, true), + pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, !csp.noPrivImport), } pub, prv, err := p11lib.GenerateKeyPair(session, @@ -298,12 +306,12 @@ func generateECKey(curve asn1.ObjectIdentifier, ephemeral bool) (ski []byte, pub return ski, pubGoKey, nil } -func signECDSA(ski []byte, msg []byte) (R, S *big.Int, err error) { - p11lib := ctx - session := getSession() - defer returnSession(session) +func (csp *impl) signP11ECDSA(ski []byte, msg []byte) (R, S *big.Int, err error) { + p11lib := csp.ctx + session := csp.getSession() + defer csp.returnSession(session) - privateKey, err := findKeyPairFromSKI(p11lib, session, ski, isPrivateKey) + privateKey, err := findKeyPairFromSKI(p11lib, session, ski, privateKeyFlag) if err != nil { return nil, nil, fmt.Errorf("Private key not found [%s]\n", err) } @@ -328,14 +336,14 @@ func signECDSA(ski []byte, msg []byte) (R, S *big.Int, err error) { return R, S, nil } -func verifyECDSA(ski []byte, msg []byte, R, S *big.Int, byteSize int) (valid bool, err error) { - p11lib := ctx - session := getSession() - defer returnSession(session) +func (csp *impl) verifyP11ECDSA(ski []byte, msg []byte, R, S *big.Int, byteSize int) (valid bool, err error) { + p11lib := csp.ctx + session := csp.getSession() + defer csp.returnSession(session) logger.Debugf("Verify ECDSA\n") - publicKey, err := findKeyPairFromSKI(p11lib, session, ski, isPublicKey) + publicKey, err := findKeyPairFromSKI(p11lib, session, ski, publicKeyFlag) if err != nil { return false, fmt.Errorf("Public key not found [%s]\n", err) } @@ -364,10 +372,10 @@ func verifyECDSA(ski []byte, msg []byte, R, S *big.Int, byteSize int) (valid boo return true, nil } -func importECKey(curve asn1.ObjectIdentifier, privKey, ecPt []byte, ephemeral bool, isPrivate bool) (ski []byte, err error) { - p11lib := ctx - session := getSession() - defer returnSession(session) +func (csp *impl) importECKey(curve asn1.ObjectIdentifier, privKey, ecPt []byte, ephemeral bool, keyType bool) (ski []byte, err error) { + p11lib := csp.ctx + session := csp.getSession() + defer csp.returnSession(session) id := nextIDCtr() @@ -377,15 +385,15 @@ func importECKey(curve asn1.ObjectIdentifier, privKey, ecPt []byte, ephemeral bo } var keyTemplate []*pkcs11.Attribute - if isPrivate == isPublicKey { + if keyType == publicKeyFlag { logger.Debug("Importing Public EC Key") publabel := fmt.Sprintf("BCPUB%s", id.Text(16)) hash := sha256.Sum256(ecPt) ski = hash[:] - ski[0] = 1 - if probablySoftHSM { + if strings.Contains(csp.lib, "softhsm") { + // Probably SoftHSM, some handcrafting necessary ecPt = append([]byte{0x04, byte(len(ecPt))}, ecPt...) } @@ -402,7 +410,7 @@ func importECKey(curve asn1.ObjectIdentifier, privKey, ecPt []byte, ephemeral bo pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), } } else { // isPrivateKey - ski, err = importECKey(curve, nil, ecPt, ephemeral, isPublicKey) + ski, err = csp.importECKey(curve, nil, ecPt, ephemeral, publicKeyFlag) if err != nil { return nil, fmt.Errorf("Failed importing private EC Key [%s]\n", err) } @@ -438,13 +446,13 @@ func importECKey(curve asn1.ObjectIdentifier, privKey, ecPt []byte, ephemeral bo } const ( - isPrivateKey = true - isPublicKey = false + privateKeyFlag = true + publicKeyFlag = false ) -func findKeyPairFromSKI(mod *pkcs11.Ctx, session pkcs11.SessionHandle, ski []byte, isPrivate bool) (*pkcs11.ObjectHandle, error) { +func findKeyPairFromSKI(mod *pkcs11.Ctx, session pkcs11.SessionHandle, ski []byte, keyType bool) (*pkcs11.ObjectHandle, error) { ktype := pkcs11.CKO_PUBLIC_KEY - if isPrivate == isPrivateKey { + if keyType == privateKeyFlag { ktype = pkcs11.CKO_PRIVATE_KEY } @@ -555,7 +563,7 @@ func ecPoint(p11lib *pkcs11.Ctx, session pkcs11.SessionHandle, key pkcs11.Object func listAttrs(p11lib *pkcs11.Ctx, session pkcs11.SessionHandle, obj pkcs11.ObjectHandle) { var cktype, ckclass uint - var ckaid, cklabel, privKey []byte + var ckaid, cklabel []byte if p11lib == nil { return @@ -566,13 +574,12 @@ func listAttrs(p11lib *pkcs11.Ctx, session pkcs11.SessionHandle, obj pkcs11.Obje pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, cktype), pkcs11.NewAttribute(pkcs11.CKA_ID, ckaid), pkcs11.NewAttribute(pkcs11.CKA_LABEL, cklabel), - pkcs11.NewAttribute(pkcs11.CKA_VALUE, privKey), } // certain errors are tolerated, if value is missing attr, err := p11lib.GetAttributeValue(session, obj, template) if err != nil { - logger.Warningf("P11: get(attrlist) [%s]\n", err) + logger.Debugf("P11: get(attrlist) [%s]\n", err) } for _, a := range attr { @@ -581,12 +588,12 @@ func listAttrs(p11lib *pkcs11.Ctx, session pkcs11.SessionHandle, obj pkcs11.Obje } } -func getSecretValue(ski []byte) []byte { - p11lib := ctx - session := getSession() - defer returnSession(session) +func (csp *impl) getSecretValue(ski []byte) []byte { + p11lib := csp.ctx + session := csp.getSession() + defer csp.returnSession(session) - keyHandle, err := findKeyPairFromSKI(p11lib, session, ski, isPrivateKey) + keyHandle, err := findKeyPairFromSKI(p11lib, session, ski, privateKeyFlag) var privKey []byte template := []*pkcs11.Attribute{ diff --git a/bccsp/pkcs11/pkcs11_test.go b/bccsp/pkcs11/pkcs11_test.go index a7ca8729abd..68ef9527f3e 100644 --- a/bccsp/pkcs11/pkcs11_test.go +++ b/bccsp/pkcs11/pkcs11_test.go @@ -24,20 +24,46 @@ import ( "testing" "github.com/hyperledger/fabric/bccsp" + "github.com/miekg/pkcs11" + "github.com/stretchr/testify/assert" ) func TestPKCS11GetSession(t *testing.T) { - if !enablePKCS11tests { - t.SkipNow() + var sessions []pkcs11.SessionHandle + for i := 0; i < 3*sessionCacheSize; i++ { + sessions = append(sessions, currentBCCSP.(*impl).getSession()) } - session := getSession() - defer returnSession(session) + // Return all sessions, should leave sessionCacheSize cached + for _, session := range sessions { + currentBCCSP.(*impl).returnSession(session) + } + sessions = nil + + // Lets break OpenSession, so non-cached session cannot be opened + oldSlot := currentBCCSP.(*impl).slot + currentBCCSP.(*impl).slot = ^uint(0) + + // Should be able to get sessionCacheSize cached sessions + for i := 0; i < sessionCacheSize; i++ { + sessions = append(sessions, currentBCCSP.(*impl).getSession()) + } + + // This one should fail + assert.Panics(t, func() { + currentBCCSP.(*impl).getSession() + }, "Should not been able to create another session") + + // Cleanup + for _, session := range sessions { + currentBCCSP.(*impl).returnSession(session) + } + currentBCCSP.(*impl).slot = oldSlot } func TestPKCS11ECKeySignVerify(t *testing.T) { - if !enablePKCS11tests { - t.SkipNow() + if currentBCCSP.(*impl).noPrivImport { + t.Skip("Key import turned off. Skipping Derivation tests as they currently require Key Import.") } msg1 := []byte("This is my very authentic message") @@ -52,18 +78,18 @@ func TestPKCS11ECKeySignVerify(t *testing.T) { oid = oidNamedCurveP384 } - key, pubKey, err := generateECKey(oid, true) + key, pubKey, err := currentBCCSP.(*impl).generateECKey(oid, true) if err != nil { t.Fatalf("Failed generating Key [%s]", err) } - R, S, err := signECDSA(key, hash1) + R, S, err := currentBCCSP.(*impl).signP11ECDSA(key, hash1) if err != nil { t.Fatalf("Failed signing message [%s]", err) } - pass, err := verifyECDSA(key, hash1, R, S, currentTestConfig.securityLevel/8) + pass, err := currentBCCSP.(*impl).verifyP11ECDSA(key, hash1, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 1 [%s]", err) } @@ -76,7 +102,7 @@ func TestPKCS11ECKeySignVerify(t *testing.T) { t.Fatal("Signature should match with software verification!") } - pass, err = verifyECDSA(key, hash2, R, S, currentTestConfig.securityLevel/8) + pass, err = currentBCCSP.(*impl).verifyP11ECDSA(key, hash2, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 2 [%s]", err) } @@ -92,8 +118,8 @@ func TestPKCS11ECKeySignVerify(t *testing.T) { } func TestPKCS11ECKeyImportSignVerify(t *testing.T) { - if !enablePKCS11tests { - t.SkipNow() + if currentBCCSP.(*impl).noPrivImport { + t.Skip("Key import turned off. Skipping Derivation tests as they currently require Key Import.") } msg1 := []byte("This is my very authentic message") @@ -118,18 +144,18 @@ func TestPKCS11ECKeyImportSignVerify(t *testing.T) { } ecPt := elliptic.Marshal(key.Curve, key.X, key.Y) - ski, err := importECKey(oid, key.D.Bytes(), ecPt, false, isPrivateKey) + ski, err := currentBCCSP.(*impl).importECKey(oid, key.D.Bytes(), ecPt, false, privateKeyFlag) if err != nil { t.Fatalf("Failed getting importing EC Public Key [%s]", err) } - R, S, err := signECDSA(ski, hash1) + R, S, err := currentBCCSP.(*impl).signP11ECDSA(ski, hash1) if err != nil { t.Fatalf("Failed signing message [%s]", err) } - pass, err := verifyECDSA(ski, hash1, R, S, currentTestConfig.securityLevel/8) + pass, err := currentBCCSP.(*impl).verifyP11ECDSA(ski, hash1, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 1 [%s]\n%s\n\n%s", err, hex.Dump(R.Bytes()), hex.Dump(S.Bytes())) } @@ -142,7 +168,7 @@ func TestPKCS11ECKeyImportSignVerify(t *testing.T) { t.Fatal("Signature should match with software verification!") } - pass, err = verifyECDSA(ski, hash2, R, S, currentTestConfig.securityLevel/8) + pass, err = currentBCCSP.(*impl).verifyP11ECDSA(ski, hash2, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 2 [%s]", err) } @@ -158,8 +184,8 @@ func TestPKCS11ECKeyImportSignVerify(t *testing.T) { } func TestPKCS11ECKeyExport(t *testing.T) { - if !enablePKCS11tests { - t.SkipNow() + if currentBCCSP.(*impl).noPrivImport { + t.Skip("Key import turned off. Skipping Derivation tests as they currently require Key Import.") } msg1 := []byte("This is my very authentic message") @@ -174,12 +200,12 @@ func TestPKCS11ECKeyExport(t *testing.T) { oid = oidNamedCurveP384 } - key, pubKey, err := generateECKey(oid, false) + key, pubKey, err := currentBCCSP.(*impl).generateECKey(oid, false) if err != nil { t.Fatalf("Failed generating Key [%s]", err) } - secret := getSecretValue(key) + secret := currentBCCSP.(*impl).getSecretValue(key) x, y := pubKey.ScalarBaseMult(secret) if 0 != x.Cmp(pubKey.X) { @@ -191,14 +217,14 @@ func TestPKCS11ECKeyExport(t *testing.T) { } ecPt := elliptic.Marshal(pubKey.Curve, x, y) - key2, err := importECKey(oid, secret, ecPt, false, isPrivateKey) + key2, err := currentBCCSP.(*impl).importECKey(oid, secret, ecPt, false, privateKeyFlag) - R, S, err := signECDSA(key2, hash1) + R, S, err := currentBCCSP.(*impl).signP11ECDSA(key2, hash1) if err != nil { t.Fatalf("Failed signing message [%s]", err) } - pass, err := verifyECDSA(key2, hash1, R, S, currentTestConfig.securityLevel/8) + pass, err := currentBCCSP.(*impl).verifyP11ECDSA(key2, hash1, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 1 [%s]", err) } @@ -206,7 +232,7 @@ func TestPKCS11ECKeyExport(t *testing.T) { t.Fatal("Signature should match! [1]") } - pass, err = verifyECDSA(key, hash1, R, S, currentTestConfig.securityLevel/8) + pass, err = currentBCCSP.(*impl).verifyP11ECDSA(key, hash1, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 2 [%s]", err) } @@ -219,7 +245,7 @@ func TestPKCS11ECKeyExport(t *testing.T) { t.Fatal("Signature should match with software verification!") } - pass, err = verifyECDSA(key, hash2, R, S, currentTestConfig.securityLevel/8) + pass, err = currentBCCSP.(*impl).verifyP11ECDSA(key, hash2, R, S, currentTestConfig.securityLevel/8) if err != nil { t.Fatalf("Error verifying message 3 [%s]", err) } diff --git a/bccsp/sw/impl.go b/bccsp/sw/impl.go index 8871c81ffb8..14bb4246c3d 100644 --- a/bccsp/sw/impl.go +++ b/bccsp/sw/impl.go @@ -41,7 +41,7 @@ import ( ) var ( - logger = logging.MustGetLogger("SW_BCCSP") + logger = logging.MustGetLogger("bccsp_sw") ) // NewDefaultSecurityLevel returns a new instance of the software-based BCCSP diff --git a/common/viperutil/config_test.go b/common/viperutil/config_test.go index 827e3703271..f22473be533 100644 --- a/common/viperutil/config_test.go +++ b/common/viperutil/config_test.go @@ -384,3 +384,45 @@ func TestStringFromFileEnv(t *testing.T) { } } + +func TestEnhancedExactUnmarshalKey(t *testing.T) { + type Nested struct { + Key string + } + + type nestedKey struct { + Nested Nested + } + + yaml := "---\n" + + "Top:\n" + + " Nested:\n" + + " Nested:\n" + + " Key: BAD\n" + + envVar := "VIPERUTIL_TOP_NESTED_NESTED_KEY" + envVal := "GOOD" + os.Setenv(envVar, envVal) + defer os.Unsetenv(envVar) + + viper.SetEnvPrefix(Prefix) + defer viper.Reset() + viper.AutomaticEnv() + replacer := strings.NewReplacer(".", "_") + viper.SetEnvKeyReplacer(replacer) + viper.SetConfigType("yaml") + + if err := viper.ReadConfig(bytes.NewReader([]byte(yaml))); err != nil { + t.Fatalf("Error reading config: %s", err) + } + + var uconf nestedKey + if err := EnhancedExactUnmarshalKey("top.Nested", &uconf); err != nil { + t.Fatalf("Failed to unmarshall: %s", err) + } + + if uconf.Nested.Key != envVal { + t.Fatalf(`Expected: "%s", Actual: "%s"`, envVal, uconf.Nested.Key) + } + +} diff --git a/common/viperutil/config_util.go b/common/viperutil/config_util.go index aa95d3d308d..05a53816a53 100644 --- a/common/viperutil/config_util.go +++ b/common/viperutil/config_util.go @@ -36,11 +36,13 @@ import ( var logger = logging.MustGetLogger("viperutil") -func getKeysRecursively(base string, v *viper.Viper, nodeKeys map[string]interface{}) map[string]interface{} { +type viperGetter func(key string) interface{} + +func getKeysRecursively(base string, getKey viperGetter, nodeKeys map[string]interface{}) map[string]interface{} { result := make(map[string]interface{}) for key := range nodeKeys { fqKey := base + key - val := v.Get(fqKey) + val := getKey(fqKey) if m, ok := val.(map[interface{}]interface{}); ok { logger.Debugf("Found map[interface{}]interface{} value for %s", fqKey) tmp := make(map[string]interface{}) @@ -51,17 +53,17 @@ func getKeysRecursively(base string, v *viper.Viper, nodeKeys map[string]interfa } tmp[cik] = iv } - result[key] = getKeysRecursively(fqKey+".", v, tmp) + result[key] = getKeysRecursively(fqKey+".", getKey, tmp) } else if m, ok := val.(map[string]interface{}); ok { logger.Debugf("Found map[string]interface{} value for %s", fqKey) - result[key] = getKeysRecursively(fqKey+".", v, m) + result[key] = getKeysRecursively(fqKey+".", getKey, m) } else if m, ok := unmarshalJSON(val); ok { logger.Debugf("Found real value for %s setting to map[string]string %v", fqKey, m) result[key] = m } else { if val == nil { fileSubKey := fqKey + ".File" - fileVal := v.Get(fileSubKey) + fileVal := getKey(fileSubKey) if fileVal != nil { result[key] = map[string]interface{}{"File": fileVal} continue @@ -256,8 +258,10 @@ func pemBlocksFromFileDecodeHook() mapstructure.DecodeHookFunc { // producing error when extraneous variables are introduced and supporting // the time.Duration type func EnhancedExactUnmarshal(v *viper.Viper, output interface{}) error { - baseKeys := v.AllSettings() // AllKeys doesn't actually return all keys, it only returns the base ones - leafKeys := getKeysRecursively("", v, baseKeys) + // AllKeys doesn't actually return all keys, it only returns the base ones + baseKeys := v.AllSettings() + getterWithClass := func(key string) interface{} { return v.Get(key) } // hide receiver + leafKeys := getKeysRecursively("", getterWithClass, baseKeys) logger.Debugf("%+v", leafKeys) config := &mapstructure.DecoderConfig{ @@ -279,3 +283,13 @@ func EnhancedExactUnmarshal(v *viper.Viper, output interface{}) error { } return decoder.Decode(leafKeys) } + +// EnhancedExactUnmarshalKey is intended to unmarshal a config file subtreee into a structure +func EnhancedExactUnmarshalKey(baseKey string, output interface{}) error { + m := make(map[string]interface{}) + m[baseKey] = nil + leafKeys := getKeysRecursively("", viper.Get, m) + + logger.Debugf("%+v", leafKeys) + return mapstructure.Decode(leafKeys[baseKey], output) +} diff --git a/peer/common/common.go b/peer/common/common.go index 277f3b60a98..0067550fed4 100644 --- a/peer/common/common.go +++ b/peer/common/common.go @@ -23,6 +23,7 @@ import ( "github.com/hyperledger/fabric/bccsp/factory" "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/common/viperutil" "github.com/hyperledger/fabric/core/errors" "github.com/hyperledger/fabric/core/peer" "github.com/hyperledger/fabric/msp" @@ -64,7 +65,7 @@ func InitConfig(cmdRoot string) error { func InitCrypto(mspMgrConfigDir string, localMSPID string) error { // Init the BCCSP var bccspConfig *factory.FactoryOpts - err := viper.UnmarshalKey("peer.BCCSP", &bccspConfig) + err := viperutil.EnhancedExactUnmarshalKey("peer.BCCSP", &bccspConfig) if err != nil { return fmt.Errorf("Could not parse YAML config [%s]", err) }