Skip to content

Commit

Permalink
[FAB-1648] Enable BCCSP configuration w. JSON/YAML
Browse files Browse the repository at this point in the history
Require call to
   func InitFactories(config *FactoryOpts)

That takes a config file appropriate for json.Unmarshall() and
viper.UnmarshallKey()

Removed ability from factory.go to get BCCSP instances on new Opts.

Factory API is now:
   func GetDefault() (bccsp.BCCSP, error)
   func GetBCCSP(name string) (bccsp.BCCSP, error)

InitFactories reads the config, initializes a hashmap for GetBCCSP
and initializes pointer for GetDefault()

To break up this change into digestible pieces, this change
calls InitFactories(nil) as a workaround. Next set of changes will
deal with modifying yaml files and calling InitFactories() with
real data

Change-Id: I7c68e52c75fc6751c7e758bf06dcaa88219aa6a9
Signed-off-by: Volodymyr Paprotski <vpaprots@ca.ibm.com>
  • Loading branch information
Volodymyr Paprotski committed Feb 19, 2017
1 parent ef962ca commit 5159e6e
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 213 deletions.
122 changes: 57 additions & 65 deletions bccsp/factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,27 @@ limitations under the License.
package factory

import (
"errors"
"fmt"
"sync"

"os"

"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/sw"
"github.com/op/go-logging"
)

var (
// Default BCCSP
defaultBCCSP bccsp.BCCSP

// BCCSP Factories
factories map[string]BCCSPFactory
bccspMap map[string]bccsp.BCCSP

// factories' Sync on Initialization
factoriesInitOnce sync.Once

// Factories' Initialization Error
factoriesInitError error

logger = logging.MustGetLogger("BCCSP_FACTORY")
)

// BCCSPFactory is used to get instances of the BCCSP interface.
Expand All @@ -48,86 +47,79 @@ type BCCSPFactory interface {
Name() string

// Get returns an instance of BCCSP using opts.
Get(opts Opts) (bccsp.BCCSP, error)
}

// Opts contains options for instantiating BCCSPs.
type Opts interface {

// FactoryName returns the name of the factory to be used
FactoryName() string

// Ephemeral returns true if the BCCSP has to be ephemeral, false otherwise
Ephemeral() bool
Get(opts *FactoryOpts) (bccsp.BCCSP, error)
}

// GetDefault returns a non-ephemeral (long-term) BCCSP
func GetDefault() (bccsp.BCCSP, error) {
if err := initFactories(); err != nil {
return nil, err
func GetDefault() bccsp.BCCSP {
if defaultBCCSP == nil {
panic("BCCSP Factory: Must call InitFactories before using BCCSP!")
}

return defaultBCCSP, nil
}

// GetDefaultOrPanic returns a non-ephemeral (long-term) BCCSP or panic if an error occurs.
func GetDefaultOrPanic() bccsp.BCCSP {
if err := initFactories(); err != nil {
panic(err)
}

return defaultBCCSP
}

// GetBCCSP returns a BCCSP created according to the options passed in input.
func GetBCCSP(opts Opts) (bccsp.BCCSP, error) {
if err := initFactories(); err != nil {
return nil, err
}

return getBCCSPInternal(opts)
func GetBCCSP(name string) (bccsp.BCCSP, error) {
return bccspMap[name], nil
}

func initFactories() error {
// InitFactories must be called before using factory interfaces
// It is acceptable to call with config = nil, in which case
// some defaults will get used
// Error is returned only if defaultBCCSP cannot be found
func InitFactories(config *FactoryOpts) error {
factoriesInitOnce.Do(func() {
// Initialize factories map
if factoriesInitError = initFactoriesMap(); factoriesInitError != nil {
return
// Take some precautions on default opts
if config == nil {
config = &DefaultOpts
}

// Create default non-ephemeral (long-term) BCCSP
defaultBCCSP, factoriesInitError = createDefaultBCCSP()
if factoriesInitError != nil {
return
if config.ProviderName == "" {
config.ProviderName = "SW"
}
})
return factoriesInitError
}

func initFactoriesMap() error {
factories = make(map[string]BCCSPFactory)
if config.SwOpts == nil {
config.SwOpts = DefaultOpts.SwOpts
}

// Software-Based BCCSP
f := &SWFactory{}
factories[f.Name()] = f
// Initialize factories map
bccspMap = make(map[string]bccsp.BCCSP)

// Software-Based BCCSP
if config.SwOpts != nil {
f := &SWFactory{}
err := initBCCSP(f, config)
if err != nil {
factoriesInitError = fmt.Errorf("[%s]", err)
}
}

return nil
}
// PKCS11-Based BCCSP
if config.Pkcs11Opts != nil {
f := &PKCS11Factory{}
err := initBCCSP(f, config)
if err != nil {
factoriesInitError = fmt.Errorf("%s\n[%s]", factoriesInitError, err)
}
}

func createDefaultBCCSP() (bccsp.BCCSP, error) {
return sw.NewDefaultSecurityLevel(os.TempDir())
}
var ok bool
defaultBCCSP, ok = bccspMap[config.ProviderName]
if !ok {
factoriesInitError = fmt.Errorf("%s\nCould not find default `%s` BCCSP", factoriesInitError, config.ProviderName)
}
})

func getBCCSPInternal(opts Opts) (bccsp.BCCSP, error) {
// Validate arguments
if opts == nil {
return nil, errors.New("Cannot instantiate a factory with 'nil' Opts. A fully instantiated BCCSP Opts struct must be provided.")
}
return factoriesInitError
}

f, ok := factories[opts.FactoryName()]
if ok {
return f.Get(opts)
func initBCCSP(f BCCSPFactory, config *FactoryOpts) error {
csp, err := f.Get(config)
if err != nil {
return fmt.Errorf("Could not initialize BCCSP %s [%s]", f.Name(), err)
}

return nil, fmt.Errorf("Factory [%s] does not exist.", opts.FactoryName())
logger.Debugf("Initialize BCCSP [%s]", f.Name())
bccspMap[f.Name()] = csp
return nil
}
139 changes: 109 additions & 30 deletions bccsp/factory/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,60 +16,139 @@ limitations under the License.
package factory

import (
"bytes"
"encoding/json"
"flag"
"fmt"
"os"
"testing"

"github.com/hyperledger/fabric/bccsp/sw"
"github.com/spf13/viper"
)

func TestGetDefault(t *testing.T) {
bccsp, err := GetDefault()
func TestMain(m *testing.M) {
flag.Parse()
lib, pin, label, enable := findPKCS11Lib()

var jsonBCCSP, yamlBCCSP *FactoryOpts
jsonCFG := []byte(
`{ "default": "SW", "SW":{ "security": 384, "hash": "SHA3" } }`)

err := json.Unmarshal(jsonCFG, &jsonBCCSP)
if err != nil {
t.Fatalf("Failed getting default BCCSP [%s]", err)
fmt.Printf("Could not parse JSON config [%s]", err)
os.Exit(-1)
}
if bccsp == nil {
t.Fatal("Failed getting default BCCSP. Nil instance.")
}
}

func TestGetBCCPEphemeral(t *testing.T) {
ks := &sw.FileBasedKeyStore{}
if err := ks.Init(nil, os.TempDir(), false); err != nil {
t.Fatalf("Failed initializing key store [%s]", err)
yamlCFG := fmt.Sprintf(`
BCCSP:
default: PKCS11
SW:
Hash: SHA3
Security: 256
PKCS11:
Hash: SHA3
Security: 256
Library: %s
Pin: '%s'
Label: %s
`, lib, pin, label)

if !enable {
fmt.Printf("Could not find PKCS11 libraries, running without\n")
yamlCFG = `
BCCSP:
default: SW
SW:
Hash: SHA3
Security: 256`
}

bccsp1, err := GetBCCSP(&SwOpts{Ephemeral_: true, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks})
viper.SetConfigType("yaml")
err = viper.ReadConfig(bytes.NewBuffer([]byte(yamlCFG)))
if err != nil {
t.Fatalf("Failed getting ephemeral software-based BCCSP [%s]", err)
fmt.Printf("Could not read YAML config [%s]", err)
os.Exit(-1)
}

bccsp2, err := GetBCCSP(&SwOpts{Ephemeral_: true, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks})
err = viper.UnmarshalKey("bccsp", &yamlBCCSP)
if err != nil {
t.Fatalf("Failed getting ephemeral software-based BCCSP [%s]", err)
fmt.Printf("Could not parse YAML config [%s]", err)
os.Exit(-1)
}

if bccsp1 == bccsp2 {
t.Fatal("Ephemeral BCCSPs should point to different instances")
cfgVariations := []*FactoryOpts{
{
ProviderName: "SW",
SwOpts: &SwOpts{
HashFamily: "SHA2",
SecLevel: 256,

Ephemeral: true,
},
},
{},
{
ProviderName: "SW",
},
jsonBCCSP,
yamlBCCSP,
}
}

func TestGetBCCP2Ephemeral(t *testing.T) {
ks := &sw.FileBasedKeyStore{}
if err := ks.Init(nil, os.TempDir(), false); err != nil {
t.Fatalf("Failed initializing key store [%s]", err)
for index, config := range cfgVariations {
fmt.Printf("Trying configuration [%d]\n", index)
InitFactories(config)
InitFactories(nil)
m.Run()
}
os.Exit(0)
}

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)
func TestGetDefault(t *testing.T) {
bccsp := GetDefault()
if bccsp == nil {
t.Fatal("Failed getting default BCCSP. Nil instance.")
}
}

bccsp2, err := GetBCCSP(&SwOpts{Ephemeral_: false, SecLevel: 256, HashFamily: "SHA2", KeyStore: ks})
func TestGetBCCSP(t *testing.T) {
bccsp, err := GetBCCSP("SW")
if err != nil {
t.Fatalf("Failed getting non-ephemeral software-based BCCSP [%s]", err)
t.Fatalf("Failed getting default BCCSP [%s]", err)
}
if bccsp == nil {
t.Fatal("Failed Software BCCSP. Nil instance.")
}
}

if bccsp1 != bccsp2 {
t.Fatal("Non-ephemeral BCCSPs should point to the same 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
}
27 changes: 18 additions & 9 deletions bccsp/factory/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,26 @@ limitations under the License.
package factory

// DefaultOpts offers a default implementation for Opts
type DefaultOpts struct {
ProviderName string
EphemeralFlag bool
type FactoryOpts struct {
ProviderName string `mapstructure:"default" json:"default"`
SwOpts *SwOpts `mapstructure:"SW,omitempty" json:"SW,omitempty"`
Pkcs11Opts *PKCS11Opts `mapstructure:"PKCS11,omitempty" json:"PKCS11,omitempty"`
}

// FactoryName returns the name of the provider
func (o *DefaultOpts) FactoryName() string {
return o.ProviderName
var DefaultOpts = FactoryOpts{
ProviderName: "SW",
SwOpts: &SwOpts{
HashFamily: "SHA2",
SecLevel: 256,

Ephemeral: true,
},
}

// Ephemeral returns true if the CSP has to be ephemeral, false otherwise
func (o *DefaultOpts) Ephemeral() bool {
return o.EphemeralFlag
// FIXME! FIXME! This needs to be moved to where viper reads in configs!
var myHack = InitFactories(nil)

// FactoryName returns the name of the provider
func (o *FactoryOpts) FactoryName() string {
return o.ProviderName
}
Loading

0 comments on commit 5159e6e

Please sign in to comment.