Skip to content

Commit

Permalink
[FAB-7513] MSP-like key and cert storage
Browse files Browse the repository at this point in the history
This change introduces a key value store that supports MSP-like
directory structure, the same that is currently used for
pre-enrolled users (generated by the cryptogen tool).
Also, the CredentialManager is modified to use this type of
store for user private keys (in case of SW crypto provider) and user certs.

Change-Id: I7e3dcd07d14354deb35514ee9275b88a5e9055ae
Signed-off-by: Aleksandar Likic <aleksandar.likic@securekey.com>
  • Loading branch information
Aleksandar Likic committed Feb 6, 2018
1 parent 4c64194 commit e50cd25
Show file tree
Hide file tree
Showing 27 changed files with 695 additions and 262 deletions.
31 changes: 0 additions & 31 deletions api/apifabclient/keyvaluestore.go

This file was deleted.

34 changes: 34 additions & 0 deletions api/kvstore/kvstore.go
@@ -0,0 +1,34 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package kvstore

import "github.com/pkg/errors"

var (
// ErrNotFound indicates that a value for the key does not exist
ErrNotFound = errors.New("value for key not found")
)

// KVStore is a generic key-value store interface.
type KVStore interface {

/**
* Store sets the value for the key.
*/
Store(key interface{}, value interface{}) error

/**
* Load returns the value stored in the store for a key.
* If a value for the key was not found, returns (nil, ErrNotFound)
*/
Load(key interface{}) (interface{}, error)

/**
* Delete deletes the value for a key.
*/
Delete(key interface{}) error
}
34 changes: 23 additions & 11 deletions pkg/config/cryptoutil/cryptoutils.go
Expand Up @@ -27,18 +27,8 @@ var logger = logging.NewLogger("fabric_sdk_go")
// GetPrivateKeyFromCert will return private key represented by SKI in cert's public key
func GetPrivateKeyFromCert(cert []byte, cs apicryptosuite.CryptoSuite) (apicryptosuite.Key, error) {

dcert, _ := pem.Decode(cert)
if dcert == nil {
return nil, errors.Errorf("Unable to decode cert bytes [%v]", cert)
}

x509Cert, err := x509.ParseCertificate(dcert.Bytes)
if err != nil {
return nil, errors.Errorf("Unable to parse cert from decoded bytes: %s", err)
}

// get the public key in the right format
certPubK, err := cs.KeyImport(x509Cert, factory.GetX509PublicKeyImportOpts(true))
certPubK, err := GetPublicKeyFromCert(cert, cs)
if err != nil {
return nil, errors.WithMessage(err, "Failed to import certificate's public key")
}
Expand All @@ -60,6 +50,28 @@ func GetPrivateKeyFromCert(cert []byte, cs apicryptosuite.CryptoSuite) (apicrypt
return key, nil
}

// GetPublicKeyFromCert will return public key the from cert
func GetPublicKeyFromCert(cert []byte, cs apicryptosuite.CryptoSuite) (apicryptosuite.Key, error) {

dcert, _ := pem.Decode(cert)
if dcert == nil {
return nil, errors.Errorf("Unable to decode cert bytes [%v]", cert)
}

x509Cert, err := x509.ParseCertificate(dcert.Bytes)
if err != nil {
return nil, errors.Errorf("Unable to parse cert from decoded bytes: %s", err)
}

// get the public key in the right format
key, err := cs.KeyImport(x509Cert, factory.GetX509PublicKeyImportOpts(true))
if err != nil {
return nil, errors.WithMessage(err, "Failed to import certificate's public key")
}

return key, nil
}

// X509KeyPair will return cer/key pair used for mutual TLS
func X509KeyPair(certPEMBlock []byte, pk apicryptosuite.Key, cs apicryptosuite.CryptoSuite) (tls.Certificate, error) {

Expand Down
7 changes: 3 additions & 4 deletions pkg/cryptosuite/bccsp/sw/cryptosuiteimpl.go
Expand Up @@ -27,22 +27,21 @@ func GetSuiteByConfig(config apiconfig.Config) (apicryptosuite.CryptoSuite, erro

opts := getOptsByConfig(config)
bccsp, err := getBCCSPFromOpts(opts)

if err != nil {
return nil, err
}
return &wrapper.CryptoSuite{BCCSP: bccsp}, nil
return wrapper.NewCryptoSuite(bccsp), nil
}

//GetSuiteWithDefaultEphemeral returns cryptosuite adaptor for bccsp with default ephemeral options (intended to aid testing)
func GetSuiteWithDefaultEphemeral() (apicryptosuite.CryptoSuite, error) {
opts := getEphemeralOpts()
bccsp, err := getBCCSPFromOpts(opts)

bccsp, err := getBCCSPFromOpts(opts)
if err != nil {
return nil, err
}
return &wrapper.CryptoSuite{BCCSP: bccsp}, nil
return wrapper.NewCryptoSuite(bccsp), nil
}

func getBCCSPFromOpts(config *bccspSw.SwOpts) (bccsp.BCCSP, error) {
Expand Down
6 changes: 4 additions & 2 deletions pkg/cryptosuite/bccsp/wrapper/cryptosuiteimpl.go
Expand Up @@ -17,8 +17,10 @@ import (
var logger = logging.NewLogger("fabric_sdk_go")

//NewCryptoSuite returns cryptosuite adaptor for given bccsp.BCCSP implementation
func NewCryptoSuite(bccsp bccsp.BCCSP) CryptoSuite {
return CryptoSuite{bccsp}
func NewCryptoSuite(bccsp bccsp.BCCSP) apicryptosuite.CryptoSuite {
return &CryptoSuite{
BCCSP: bccsp,
}
}

//GetKey returns implementation of of cryptosuite.Key
Expand Down
3 changes: 2 additions & 1 deletion pkg/cryptosuite/bccsp/wrapper/cryptosuiteimpl_test.go
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/golang/mock/gomock"
"github.com/hyperledger/fabric-sdk-go/api/apiconfig/mocks"
"github.com/hyperledger/fabric-sdk-go/api/apicryptosuite"
"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric-sdk-go/pkg/logging/utils"
)
Expand Down Expand Up @@ -146,7 +147,7 @@ func TestCreateInvalidSecurityProviderPanic(t *testing.T) {
t.Fatalf("Getting cryptosuite with invalid security provider supposed to panic")
}

func verifyCryptoSuite(t *testing.T, samplecryptoSuite CryptoSuite) {
func verifyCryptoSuite(t *testing.T, samplecryptoSuite apicryptosuite.CryptoSuite) {
//Test cryptosuite.Sign
signedBytes, err := samplecryptoSuite.Sign(GetKey(getMockKey(signingKey)), nil, nil)
utils.VerifyEmpty(t, err, "Not supposed to get any error for samplecryptoSuite.GetKey : %s", err)
Expand Down
24 changes: 16 additions & 8 deletions pkg/fabric-client/client.go
Expand Up @@ -11,6 +11,7 @@ import (

config "github.com/hyperledger/fabric-sdk-go/api/apiconfig"
fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient"
"github.com/hyperledger/fabric-sdk-go/api/kvstore"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"

Expand All @@ -29,7 +30,7 @@ var logger = logging.NewLogger("fabric_sdk_go")
type Client struct {
channels map[string]fab.Channel
cryptoSuite apicryptosuite.CryptoSuite
stateStore fab.KeyValueStore
stateStore kvstore.KVStore
signingIdentity fab.IdentityContext
config config.Config
signingManager fab.SigningManager
Expand Down Expand Up @@ -94,16 +95,16 @@ func (c *Client) QueryChannelInfo(name string, peers []fab.Peer) (fab.Channel, e
// Deprecated: see fabsdk package.
/*
* The SDK should have a built-in key value store implementation (suggest a file-based implementation to allow easy setup during
* development). But production systems would want a store backed by database for more robust storage and clustering,
* development). But production systems would want a store backed by database for more robust kvstore and clustering,
* so that multiple app instances can share app state via the database (note that this doesn’t necessarily make the app stateful).
* This API makes this pluggable so that different store implementations can be selected by the application.
*/
func (c *Client) SetStateStore(stateStore fab.KeyValueStore) {
func (c *Client) SetStateStore(stateStore kvstore.KVStore) {
c.stateStore = stateStore
}

// StateStore is a convenience method for obtaining the state store object in use for this client.
func (c *Client) StateStore() fab.KeyValueStore {
func (c *Client) StateStore() kvstore.KVStore {
return c.stateStore
}

Expand Down Expand Up @@ -161,7 +162,7 @@ func (c *Client) SaveUserToStateStore(user fab.User) error {
if err != nil {
return errors.Wrap(err, "marshal json return error")
}
err = c.stateStore.SetValue(user.Name(), data)
err = c.stateStore.Store(user.Name(), data)
if err != nil {
return errors.WithMessage(err, "stateStore SetValue failed")
}
Expand All @@ -184,12 +185,19 @@ func (c *Client) LoadUserFromStateStore(name string) (fab.User, error) {
if c.cryptoSuite == nil {
return nil, errors.New("cryptoSuite required")
}
value, err := c.stateStore.Value(name)
value, err := c.stateStore.Load(name)
if err != nil {
return nil, nil
if err == kvstore.ErrNotFound {
return nil, nil
}
return nil, err
}
valueBytes, ok := value.([]byte)
if !ok {
return nil, errors.New("state store return wrong data type")
}
var userJSON identity.JSON
err = json.Unmarshal(value, &userJSON)
err = json.Unmarshal(valueBytes, &userJSON)
if err != nil {
return nil, errors.Wrap(err, "unmarshal user JSON failed")
}
Expand Down
14 changes: 9 additions & 5 deletions pkg/fabric-client/client_test.go
Expand Up @@ -85,17 +85,21 @@ func TestClientMethods(t *testing.T) {
t.Fatalf("client.NewChain create wrong chain")
}

stateStore, err := kvs.CreateNewFileKeyValueStore("/tmp/keyvaluestore")
stateStore, err := kvs.NewFileKeyValueStore(&kvs.FileKeyValueStoreOptions{Path: "/tmp/keyvaluestore"})
if err != nil {
t.Fatalf("CreateNewFileKeyValueStore return error[%s]", err)
}
client.SetStateStore(stateStore)
client.StateStore().SetValue("testvalue", []byte("data"))
value, err := client.StateStore().Value("testvalue")
client.StateStore().Store("testvalue", []byte("data"))
value, err := client.StateStore().Load("testvalue")
if err != nil {
t.Fatalf("client.StateStore().GetValue() return error[%s]", err)
t.Fatalf("client.StateStore().Load() return error[%s]", err)
}
if string(value) != "data" {
valueBytes, ok := value.([]byte)
if !ok {
t.Fatalf("client.StateStore().Load() returned wrong data type")
}
if string(valueBytes) != "data" {
t.Fatalf("client.StateStore().GetValue() didn't return the right value")
}

Expand Down

0 comments on commit e50cd25

Please sign in to comment.