Skip to content

Commit

Permalink
COP BCCSP integration
Browse files Browse the repository at this point in the history
This is the first step of integrating BCCSP into COP.
One of the purposes is to provide HSM support.

In particular, this change set:
1) Adds the lib/csp package which support BCCSP configuration
2) Uses lib/csp to initialize BCCSP in the COP server

The next step will be use the BCCSP for:
1) authorization header sign and verification
2) tcerts including key derivation

https://jira.hyperledger.org/browse/FAB-1461

Change-Id: Icdc286118fe2c61d907046e8083e318fd85e5e77
Signed-off-by: Keith Smith <[email protected]>
  • Loading branch information
Keith Smith committed Jan 19, 2017
1 parent 8894989 commit 606fbdc
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 89 deletions.
2 changes: 2 additions & 0 deletions cli/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/cloudflare/cfssl/log"
"github.com/hyperledger/fabric-ca/api"
"github.com/hyperledger/fabric-ca/cli/server/ldap"
libcsp "github.com/hyperledger/fabric-ca/lib/csp"
"github.com/hyperledger/fabric-ca/lib/tls"
"github.com/hyperledger/fabric-ca/util"

Expand All @@ -46,6 +47,7 @@ type Config struct {
KeyFile string `json:"ca_key"`
TLSConf TLSConfig `json:"tls,omitempty"`
TLSDisable bool `json:"tls_disable,omitempty"`
CSP *libcsp.Config `json:"csp,omitempty"`
}

// UserReg defines the user registry properties
Expand Down
29 changes: 16 additions & 13 deletions cli/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ import (
"github.com/cloudflare/cfssl/signer/universal"
"github.com/cloudflare/cfssl/ubiquity"
"github.com/hyperledger/fabric-ca/cli/server/spi"
libcsp "github.com/hyperledger/fabric-ca/lib/csp"
"github.com/hyperledger/fabric-ca/util"
"github.com/hyperledger/fabric/core/crypto/bccsp"
"github.com/jmoiron/sqlx"
)

Expand Down Expand Up @@ -87,6 +90,7 @@ var (
configFile string
userRegistry spi.UserRegistry
certDBAccessor *CertDBAccessor
csp bccsp.BCCSP
)

var (
Expand Down Expand Up @@ -115,16 +119,7 @@ type Server struct {
// CreateHome will create a home directory if it does not exist
func (s *Server) CreateHome() (string, error) {
log.Debug("CreateHome")
home := os.Getenv("FABRIC_CA_HOME")
if home == "" {
home = os.Getenv("HOME")
if home != "" {
home = home + "/.fabric-ca"
}
}
if home == "" {
home = "/var/hyperledger/fabric/dev/.fabric-ca"
}
home := util.GetDefaultHomeDir()
if _, err := os.Stat(home); err != nil {
if os.IsNotExist(err) {
err := os.MkdirAll(home, 0755)
Expand All @@ -133,7 +128,6 @@ func (s *Server) CreateHome() (string, error) {
}
}
}

return home, nil
}

Expand All @@ -155,16 +149,25 @@ func startMain(args []string, c cli.Config) error {
var err error

s := new(Server)
homeDir, err = s.CreateHome()
home, err := s.CreateHome()
if err != nil {
return err
}
homeDir = home

configInit(&c)

// Initialize the Crypto Service Provider
csp, err = libcsp.Get(CFG.CSP)
if err != nil {
log.Errorf("Failed to get the crypto service provider: %s", err)
return err
}

// Initialize the user registry
err = InitUserRegistry(CFG)
if err != nil {
log.Errorf("Failed to initialize user registry [error: %s]", err)
log.Errorf("Failed to initialize user registry: %s", err)
return err
}

Expand Down
9 changes: 3 additions & 6 deletions cli/server/tcert.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
cerr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
"github.com/hyperledger/fabric-ca/api"
libcsp "github.com/hyperledger/fabric-ca/lib/csp"
"github.com/hyperledger/fabric-ca/lib/tcert"
"github.com/hyperledger/fabric-ca/util"
)
Expand All @@ -47,16 +48,12 @@ func NewTCertHandler() (h http.Handler, err error) {

func initTCertHandler() (h http.Handler, err error) {
log.Debug("Initializing TCert handler")
csp, err := util.GetBCCSP(nil)
if err != nil {
return nil, err
}
mgr, err := tcert.LoadMgr(CFG.KeyFile, CFG.CAFile)
if err != nil {
return nil, err
}
// TODO: The root prekey must be stored persistently in DB and retrieved here if not found
rootKey, err := util.GenRootKey(csp)
// FIXME: The root prekey must be stored persistently in DB and retrieved here if not found
rootKey, err := libcsp.GenRootKey(csp)
if err != nil {
return nil, err
}
Expand Down
137 changes: 137 additions & 0 deletions lib/csp/csp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
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 csp

import (
"crypto"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"path"

"github.com/hyperledger/fabric/core/crypto/bccsp"
"github.com/hyperledger/fabric/core/crypto/bccsp/factory"
"github.com/hyperledger/fabric/core/crypto/bccsp/signer"
"github.com/hyperledger/fabric/core/crypto/bccsp/sw"
)

const (
// SKIPEM is the PEM type to identify an SKI (Subject Key Identifier)
SKIPEM = "BCCSP SKI"
)

// Get returns an instance of the CSP (Crypto Service Provider)
// given some config. If config is nil, return the default instance.
func Get(cfg *Config) (bccsp.BCCSP, error) {
if cfg != nil {
return cfg.Get()
}
return factory.GetDefault()
}

// Config is the configuration for CSP (Crypto Service Provider)
// which allows plugging in support for HSMs (Hardware Service Modules)
// Currently supported types are: 'software'
type Config struct {
SW *SWConfig `json:"software,omitempty"`
}

// Get returns the instance of BCCSP for the config
func (c *Config) Get() (bccsp.BCCSP, error) {
if c.SW != nil {
return c.SW.Get()
}
return nil, fmt.Errorf("Invalid configuration; must contain one of: 'software'")
}

// SWConfig is configuration for the software implementation of CSP
type SWConfig struct {
KeyStoreDir string `json:"key_store_dir,omitempty"`
HashFamily string `json:"hash_family,omitempty"`
SecurityLevel int `json:"security_level,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
}

// Get returns the instance of the software CSP
func (sc *SWConfig) Get() (bccsp.BCCSP, error) {
// Set defaults
keyStoreDir := getStrVal(sc.KeyStoreDir, path.Join(os.Getenv("HOME"), ".bccsp", "ks"))
hashFamily := getStrVal(sc.HashFamily, "SHA2")
secLevel := getIntVal(sc.SecurityLevel, 256)
// Init keystore
ks := &sw.FileBasedKeyStore{}
err := ks.Init(nil, keyStoreDir, false)
if err != nil {
return nil, fmt.Errorf("Failed initializing software key store: %s", err)
}
// Return BCCSP instance
bccspOpts := &factory.SwOpts{KeyStore: ks, SecLevel: secLevel, HashFamily: hashFamily, Ephemeral_: sc.Ephemeral}
return factory.GetBCCSP(bccspOpts)
}

// GetSignerFromSKIFile returns a signer for an SKI file
func GetSignerFromSKIFile(skiFile string, csp bccsp.BCCSP) (crypto.Signer, error) {
if csp == nil {
return nil, fmt.Errorf("csp is nil")
}
keyBuff, err := ioutil.ReadFile(skiFile)
if err != nil {
return nil, fmt.Errorf("Could not read SKI file [%s]: %s", skiFile, err)
}

block, _ := pem.Decode(keyBuff)
if block == nil {
return nil, fmt.Errorf("Failed decoding SKI file [%s]", skiFile)
}

if block.Type != SKIPEM {
return nil, fmt.Errorf("Invalid PEM type in file %s; expecting '%s' but found '%s'", skiFile, SKIPEM, block.Type)
}

privateKey, err := csp.GetKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("Failed to get key from SKI file [%s]: %s", skiFile, err)
}

signer := &signer.CryptoSigner{}
if err = signer.Init(csp, privateKey); err != nil {
return nil, fmt.Errorf("Failed to initialize signer from SKI file [%s]: %s", skiFile, err)
}

return signer, nil
}

// GenRootKey generates a new root key
func GenRootKey(csp bccsp.BCCSP) (bccsp.Key, error) {
opts := &bccsp.AES256KeyGenOpts{Temporary: true}
return csp.KeyGen(opts)
}

func getStrVal(val, def string) string {
if val != "" {
return val
}
return def
}

func getIntVal(val, def int) int {
if val != 0 {
return val
}
return def
}
65 changes: 65 additions & 0 deletions lib/csp/csp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
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 csp_test

import (
"path"
"testing"

"github.com/hyperledger/fabric-ca/lib/csp"
"github.com/hyperledger/fabric/core/crypto/bccsp"
)

func TestBCCSP(t *testing.T) {
_, err := csp.Get(nil)
if err != nil {
t.Fatalf("Failed to get default BCCSP instance: %s", err)
}
_, err = csp.Get(&csp.Config{})
if err == nil {
t.Fatal("Empty config should have failed but didn't")
}
cfg := &csp.Config{SW: &csp.SWConfig{KeyStoreDir: getTestFile("ks")}}
bccsp, err := csp.Get(cfg)
if err != nil {
t.Fatalf("Failed to get test BCCSP instance: %s", err)
}
// GetSignerFromSKIFile test cases
// 1st is positive and others are negative
getSignerFromSKIFile("ec-key.ski", bccsp, "", t)
getSignerFromSKIFile("bogus-file", bccsp, "bad file", t)
getSignerFromSKIFile("", bccsp, "no file", t)
getSignerFromSKIFile("ec-key.ski", nil, "nil bccsp", t)
}

func getSignerFromSKIFile(name string, bccsp bccsp.BCCSP, expectFailure string, t *testing.T) {
file := getTestFile(name)
_, err := csp.GetSignerFromSKIFile(file, bccsp)
if err != nil {
if expectFailure == "" {
t.Errorf("Failed in GetSignerFromSKIFIle for file %s: %s", name, err)
}
} else {
if expectFailure != "" {
t.Errorf("Expected failure but passed: %s", expectFailure)
}
}
}

func getTestFile(name string) string {
return path.Join(".", "testdata", name)
}
3 changes: 3 additions & 0 deletions lib/csp/testdata/ec-key.ski
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN BCCSP SKI-----
sLJGcSFzmXHJlmULJ9Ne8//jZlTKnS8dsZvbQu4i27c=
-----END BCCSP SKI-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN ECDSA PRIVATE KEY-----
MHcCAQEEIOvM4nwA6Aes5Epz8jAB0x/A1btNmByakm8+i9PtBLn1oAoGCCqGSM49
AwEHoUQDQgAENVNQtk56NgFCVPdw35unp3/jYvH4H4QLQaBuLcn3g2opRfRJBPQl
E4wvGWozO4qyjg2TkGoQ0tPOr31uNgGr6Q==
-----END ECDSA PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN ECDSA PRIVATE KEY-----
MHcCAQEEINs5XopZVBEWTsUCCF8mU4H14/UN1alo+j5BzBQZ0PKtoAoGCCqGSM49
AwEHoUQDQgAEogflvYlpKaqJBcfKoL5yaScgJwWLkB11WOxCLMNXq5ni/qz49aIn
LN8D+tO0y9gA+r/J4QekFQHWPTnebGekyw==
-----END ECDSA PRIVATE KEY-----
3 changes: 3 additions & 0 deletions lib/csp/testdata/test-key.ski
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN BCCSP SKI-----
UKskUHhPT1tKDAMGIwmsUDrarLOmUgVlxuAa2HJuceg=
-----END BCCSP SKI-----
38 changes: 0 additions & 38 deletions util/bccsp.go

This file was deleted.

Loading

0 comments on commit 606fbdc

Please sign in to comment.