Skip to content

Commit

Permalink
FAB-3881 Remove local key store from Crypto
Browse files Browse the repository at this point in the history
CryptoSuite does not need to include a store
when used by Chain.js, so the store is removed
from CryptoSuite and when needed the application
will get a Client.newCryptoKeyStore(...) and
set it on the CryptoSuite.

Change-Id: I41168b20fd17121432f550bf26e814199a465648
Signed-off-by: cdaughtr <cdaughtr@us.ibm.com>
  • Loading branch information
cdaughtr committed May 18, 2017
1 parent 6268056 commit 6d5751a
Show file tree
Hide file tree
Showing 30 changed files with 426 additions and 361 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ tmp
.DS_Store
fabric-client/.DS_Store
fabric-ca-client/.DS_Store
test/fixtures/src/github.com/example_cc/junk.go
test/fixtures/src/github.com/example_cc/junk.go
2 changes: 1 addition & 1 deletion fabric-ca-client/lib/FabricCAClientImpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ var FabricCAServices = class {
this.cryptoPrimitives = cryptoSuite;
} else {
this.cryptoPrimitives = utils.newCryptoSuite();
this.cryptoPrimitives.setCryptoKeyStore(utils.newCryptoKeyStore());
}


this._fabricCAClient = new FabricCAClient({
caname: caName,
protocol: endpoint.protocol,
Expand Down
61 changes: 48 additions & 13 deletions fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ var Packager = require('./Packager.js');
var Peer = require('./Peer.js');
var Orderer = require('./Orderer.js');
var MSP = require('./msp/msp.js');
var MSPManager = require('./msp/msp-manager.js');

var logger = sdkUtils.getLogger('Client.js');
var util = require('util');
var path = require('path');
var fs = require('fs-extra');
var Constants = require('./Constants.js');

Expand Down Expand Up @@ -73,7 +72,19 @@ var Client = class {
}

/**
* Returns a new instance of the CryptoSuite API implementation
* Returns a new instance of the CryptoSuite API implementation.
*
* Creating a new CryptoSuite is optional and should be used if options other than defaults are needed.
* This instance should be set on the User and the Fabric CA Client.
*
* If not specified, an instance of {@link CryptoSuite} will be constructed based on the current configuration settings:
* crypto-hsm: use an implementation for Hardware Security Module (if set to true) or software-based key management (if set to false)
* crypto-keysize: security level, or key size, to use with the digital signature public key algorithm. Currently ECDSA
* is supported and the valid key sizes are 256 and 384
* crypto-hash-algo: hashing algorithm
* key-value-store: some CryptoSuite implementation requires a key store to persist private keys. A {@link CryptoKeyStore}
* is provided for this purpose, which can be used on top of any implementation of the {@link KeyValueStore} interface,
* such as a file-based store or a database-based one. The specific implementation is determined by the value of this configuration setting.
*
* @param {object} setting This optional parameter is an object with the following optional properties:
* - software {boolean}: Whether to load a software-based implementation (true) or HSM implementation (false)
Expand All @@ -82,14 +93,10 @@ var Client = class {
* - keysize {number}: The key size to use for the crypto suite instance. default is value of the setting 'crypto-keysize'
* - algorithm {string}: Digital signature algorithm, currently supporting ECDSA only with value "EC"
* - hash {string}: 'SHA2' or 'SHA3'
* @param {function} KVSImplClass Optional. The built-in key store saves private keys. The key store may be backed by different
* {@link KeyValueStore} implementations. If specified, the value of the argument must point to a module implementing the
* KeyValueStore interface.
* @param {object} opts Implementation-specific option object used in the constructor
* returns a new instance of the CryptoSuite API implementation
*/
newCryptoSuite(setting, KVSImplClass, opts) {
this._cryptoSuite = sdkUtils.newCryptoSuite(setting, KVSImplClass, opts);
newCryptoSuite(setting) {
this._cryptoSuite = sdkUtils.newCryptoSuite(setting);
return this._cryptoSuite;
}

Expand All @@ -101,6 +108,24 @@ var Client = class {
return this._cryptoSuite;
}

/**
* Returns a new instance of the CryptoKeyStore.
*
* When the application needs to use a key store other than the default,
* it should create a new CryptoKeyStore and set it on the CryptoSuite.
*
* cryptosuite.setCryptoKeyStore(client.newCryptoKeyStore(KVSImplClass, opts))
*
* @param {function} KVSImplClass Optional. The built-in key store saves private keys. The key store may be backed by different
* {@link KeyValueStore} implementations. If specified, the value of the argument must point to a module implementing the
* KeyValueStore interface.
* @param {object} opts Implementation-specific option object used in the constructor
* returns a new instance of the CryptoKeystore
*/
newCryptoKeyStore (KVSImplClass, opts) {
return sdkUtils.newCryptoKeyStore(KVSImplClass, opts);
}

/**
* Determine if dev mode is enabled.
*/
Expand Down Expand Up @@ -1052,14 +1077,19 @@ var Client = class {
(!opts.cryptoContent.privateKeyPEM || !opts.cryptoContent.signedCertPEM)) {
return Promise.reject(new Error('Client.createUser both parameters \'opts cryptoContent privateKeyPEM and signedCertPEM\' strings are required.'));
}

} else {
return Promise.reject(new Error('Client.createUser parameter \'opts cryptoContent\' is required.'));
}

if (this._cryptoSuite == null) {
logger.debug('cryptoSuite is null, creating default cryptoSuite and cryptoKeyStore');
this._cryptoSuite = sdkUtils.newCryptoSuite();
this._cryptoSuite.setCryptoKeyStore(this.newCryptoKeyStore());
} else {
if (this._cryptoSuite._cryptoKeyStore) logger.debug('cryptoSuite has a cryptoKeyStore');
else logger.info('cryptoSuite does not have a cryptoKeyStore');
}

var self = this;
return new Promise((resolve, reject) => {
logger.info('loading user from files');
Expand All @@ -1073,14 +1103,19 @@ var Client = class {

// first load the private key and save in the BCCSP's key store
var promise, member, importedKey;

if (opts.cryptoContent.privateKey) {
promise = readFile(opts.cryptoContent.privateKey);
} else {
promise = Promise.resolve(opts.cryptoContent.privateKeyPEM);
}
promise.then((data) => {
logger.debug('then privateKeyPEM data');
return self._cryptoSuite.importKey(data.toString());
if (data) {
logger.debug('then privateKeyPEM data');
return self._cryptoSuite.importKey(data.toString());
} else {
throw new Error('failed to load private key data');
}
}).then((key) => {
logger.debug('then key');
importedKey = key;
Expand Down Expand Up @@ -1260,4 +1295,4 @@ function _getChaincodePackageData(request, devMode) {
});
}

module.exports = Client;
module.exports = Client;
59 changes: 25 additions & 34 deletions fabric-client/lib/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ var idModule = require('./msp/identity.js');
var Identity = idModule.Identity;
var SigningIdentity = idModule.SigningIdentity;
var Signer = idModule.Signer;
var LocalMSP = require('./msp/msp.js');

/**
* The User class represents users that have been enrolled and represented by
Expand Down Expand Up @@ -71,7 +70,7 @@ var User = class {
this._enrollmentSecret = '';
this._identity = null;
this._signingIdentity = null;
this._mspImpl = null;
this._mspId = '';
this._cryptoSuite = null;
}

Expand Down Expand Up @@ -135,6 +134,15 @@ var User = class {
return this._cryptoSuite;
}

/**
* Set the cryptoSuite.
*
* When the application needs to use crypto settings or a key store other than the default,
* it needs to set a cryptoSuite instance that was created with the desired CryptoSuite
* settings and CryptoKeyStore options.
*
* @param {CryptoSuite} cryptoSuite The cryptoSuite.
*/
setCryptoSuite(cryptoSuite) {
this._cryptoSuite = cryptoSuite;
}
Expand All @@ -144,18 +152,6 @@ var User = class {
* @param {Key} privateKey the private key object
* @param {string} certificate the PEM-encoded string of certificate
* @param {string} mspId The Member Service Provider id for the local signing identity
* @param {object} opts optional. an object with the following attributes, all optional:
* - cryptoSettings: {object} an object with the following attributes:
* - software {boolean}: Whether to load a software-based implementation (true) or HSM implementation (false)
* default is true (for software based implementation), specific implementation module is specified
* in the setting 'crypto-suite-software'
* - keysize {number}: The key size to use for the crypto suite instance. default is value of the setting 'crypto-keysize'
* - algorithm {string}: Digital signature algorithm, currently supporting ECDSA only with value "EC"
* - hash {string}: 'SHA2' or 'SHA3'
* - KVSImplClass: {function} the User class persists crypto keys in a {@link CryptoKeyStore}, there is a file-based implementation
* that is provided as the default. Application can use this parameter to override the default, such as saving the keys in a key store
* backed by database. If present, the value must be the class for the alternative implementation.
* - kvsOpts: {object}: an options object specific to the implementation in KVSImplClass
* @returns {Promise} Promise for successful completion of creating the user's signing Identity
*/
setEnrollment(privateKey, certificate, mspId) {
Expand All @@ -171,19 +167,18 @@ var User = class {
throw new Error('Invalid parameter. Must have a valid mspId.');
}

this._mspId = mspId;

if (!this._cryptoSuite) {
this._cryptoSuite = sdkUtils.newCryptoSuite();
this._cryptoSuite.setCryptoKeyStore(sdkUtils.newCryptoKeyStore());
}
this._mspImpl = new LocalMSP({
id: mspId,
cryptoSuite: this._cryptoSuite
});

return this._mspImpl.cryptoSuite.importKey(certificate)
return this._cryptoSuite.importKey(certificate)
.then((pubKey) => {
var identity = new Identity('testIdentity', certificate, pubKey, this._mspImpl);
var identity = new Identity(certificate, pubKey, mspId, this._cryptoSuite);
this._identity = identity;
this._signingIdentity = new SigningIdentity('testSigningIdentity', certificate, pubKey, this._mspImpl, new Signer(this._mspImpl.cryptoSuite, privateKey));
this._signingIdentity = new SigningIdentity(certificate, pubKey, mspId, this._cryptoSuite, new Signer(this._cryptoSuite, privateKey));
});
}

Expand Down Expand Up @@ -215,39 +210,36 @@ var User = class {
if (typeof state.mspid === 'undefined' || state.mspid === null || state.mspid === '') {
throw new Error('Failed to find "mspid" in the deserialized state object for the user. Likely due to an outdated state store.');
}
this._mspId = state.mspid;

if (!this._cryptoSuite) {
this._cryptoSuite = sdkUtils.newCryptoSuite();
this._cryptoSuite.setCryptoKeyStore(sdkUtils.newCryptoKeyStore());
}

this._mspImpl = new LocalMSP({
id: state.mspid,
cryptoSuite: this._cryptoSuite
});

var self = this;
var pubKey;

return this._mspImpl.cryptoSuite.importKey(state.enrollment.identity.certificate, { algorithm: api.CryptoAlgorithms.X509Certificate })
return this._cryptoSuite.importKey(state.enrollment.identity.certificate, { algorithm: api.CryptoAlgorithms.X509Certificate })
.then((key) => {
pubKey = key;

var identity = new Identity(state.enrollment.identity.id, state.enrollment.identity.certificate, pubKey, self._mspImpl);
var identity = new Identity( state.enrollment.identity.certificate, pubKey, self._mspId, this._cryptoSuite);
self._identity = identity;

// during serialization (see toString() below) only the key's SKI are saved
// swap out that for the real key from the crypto provider
return self._mspImpl.cryptoSuite.getKey(state.enrollment.signingIdentity);
return self._cryptoSuite.getKey(state.enrollment.signingIdentity);
}).then((privateKey) => {
// the key retrieved from the key store using the SKI could be a public key
// or a private key, check to make sure it's a private key
if (privateKey.isPrivate()) {
self._signingIdentity = new SigningIdentity(
state.enrollment.identity.id,
state.enrollment.identity.certificate,
pubKey,
self._mspImpl,
new Signer(self._mspImpl.cryptoSuite, privateKey));
self._mspId,
self._cryptoSuite,
new Signer(self._cryptoSuite, privateKey));

return self;
} else {
Expand All @@ -268,14 +260,13 @@ var User = class {

if (this._identity) {
serializedEnrollment.identity = {
id: this._identity.getId(),
certificate: this._identity._certificate
};
}

var state = {
name: this._name,
mspid: this._mspImpl ? this._mspImpl.getId() : 'null',
mspid: this._mspId,
roles: this._roles,
affiliation: this._affiliation,
enrollmentSecret: this._enrollmentSecret,
Expand Down
4 changes: 2 additions & 2 deletions fabric-client/lib/impl/CryptoKeyStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var jsrsasign = require('jsrsasign');
var KEYUTIL = jsrsasign.KEYUTIL;

var api = require('../api.js');
var utils = require('../utils');
var utils = require('../utils.js');
var ECDSAKey = require('./ecdsa/key.js');

var logger = utils.getLogger('CryptoKeyStore.js');
Expand Down Expand Up @@ -104,4 +104,4 @@ function _getKeyIndex(ski, isPrivateKey) {
return ski + '-pub';
}

module.exports = CryptoKeyStore;
module.exports = CryptoKeyStore;
Loading

0 comments on commit 6d5751a

Please sign in to comment.