Skip to content

Commit

Permalink
Allow per-chain variations of BCCSP/CryptoSuite
Browse files Browse the repository at this point in the history
FAB-2148
Instances of BCCSP implementations, aka CryptoSuite, must be
allowed to be created within the context of a single application
instance to support different configurations:
- HSM or software-managed (should only be a per-app instance setting)
- key length (can be a per-chain setting)
- digital signature algorithm (can be a per-chain setting)

Change-Id: I55cfa1b437c4272c21a7ea1d78163905959ec670
Signed-off-by: Jim Zhang <jzhang@us.ibm.com>
  • Loading branch information
jimthematrix committed Feb 9, 2017
1 parent d32cdd2 commit 024f6f0
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 17 deletions.
9 changes: 7 additions & 2 deletions fabric-ca-client/config/default.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"request-timeout" : 3000,
"tcert-batch-size" : 10,
"crypto-asymmetric-key-algo": "ECDSA",
"crypto-hash-algo": "SHA2",
"crypto-keysize": 256,
"crypto-suite": "./impl/CryptoSuite_ECDSA_AES.js"
"crypto-hsm": false,
"crypto-suite-software": {
"EC": "./impl/CryptoSuite_ECDSA_AES.js"
},
"crypto-suite-hsm": {
"EC": "./impl/bccsp_pkcs11.js"
}
}
12 changes: 8 additions & 4 deletions fabric-client/config/default.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"request-timeout" : 3000,
"tcert-batch-size" : 10,
"crypto-asymmetric-key-algo": "ECDSA",
"crypto-hash-algo": "SHA2",
"crypto-keysize": 256,
"crypto-suite": "./impl/CryptoSuite_ECDSA_AES.js",
"crypto-hsm": false,
"crypto-suite-software": {
"EC": "./impl/CryptoSuite_ECDSA_AES.js"
},
"crypto-suite-hsm": {
"EC": "./impl/bccsp_pkcs11.js"
},
"key-value-store": "./impl/FileKeyValueStore.js",
"nonce-size" : 24,
"dockerfile-contents" : "from hyperledger/fabric-ccenv\nCOPY . $GOPATH/src/build-chaincode/\nWORKDIR $GOPATH\n\nRUN go install build-chaincode && mv $GOPATH/bin/build-chaincode $GOPATH/bin/%s"
"nonce-size" : 24
}
57 changes: 51 additions & 6 deletions fabric-client/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ var grpc = require('grpc');
var path = require('path');
var zlib = require('zlib');
var urlParser = require('url');
var util = require('util');
var winston = require('winston');
var Config = require('./Config.js');
var crypto = require('crypto');
var Config = require('./Config.js');

//
// Load required crypto stuff.
Expand All @@ -36,12 +37,56 @@ var sha3_256 = require('js-sha3').sha3_256;
// The following methods are for loading the proper implementation of an extensible APIs.
//

module.exports.getCryptoSuite = function(opts) {
// returns a new instance of the CryptoSuite API implementation
//
// @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)
// 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"
//
// @param {object} opts Implementation-specific option object used in the constructor
//
module.exports.getCryptoSuite = function(setting, opts) {
var csImpl, keysize, algorithm, haveSettings = false;

csImpl = this.getConfigSetting('crypto-hsm') ? this.getConfigSetting('crypto-suite-hsm') : this.getConfigSetting('crypto-suite-software');

if (typeof setting === 'object' && typeof setting.keysize === 'number') {
keysize = setting.keysize;
haveSettings = true;
} else
keysize = this.getConfigSetting('crypto-keysize');

if (typeof setting === 'object' && typeof setting.algorithm === 'string') {
algorithm = setting.algorithm.toUpperCase();
haveSettings = true;
} else
algorithm = 'EC';

// csImpl at this point should be a map (see config/default.json) with keys being the algorithm
csImpl = csImpl[algorithm];

if (!csImpl)
throw new Error(util.format('Desired CryptoSuite module not found supporting algorithm "%s"', algorithm));

// expecting a path to an alternative implementation
var csEnv = this.getConfigSetting('crypto-suite');
var cryptoSuite = require(csEnv);
var keySize = this.getConfigSetting('crypto-keysize');
return new cryptoSuite(keySize, opts);
var cryptoSuite = require(csImpl);

if (typeof setting !== 'undefined' && typeof opts !== 'undefined')
return new cryptoSuite(keysize, opts);

if (typeof setting !== 'undefined') {
// only one object is passed in as argument, we need to decide whether it's meant for the 'setting' or 'opts'
if (haveSettings)
return new cryptoSuite(keysize); // this implementation must not need any opts because no 'opts' argument was passed in
else
return new cryptoSuite(keysize, setting); // the function was called with only the 'opts' argument
}

// the function was called without any argument
return new cryptoSuite(keysize);
};

// Provide a Promise-based keyValueStore for couchdb, etc.
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/config/overrides.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"crypto-hsm": true
}
40 changes: 40 additions & 0 deletions test/unit/headless-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1440,12 +1440,52 @@ var api = require('fabric-client/lib/api.js');
var elliptic = require('elliptic');
var BN = require('bn.js');
var Signature = require('elliptic/lib/elliptic/ec/signature.js');
var PKCS11 = require('fabric-client/lib/impl/bccsp_pkcs11.js');

const halfOrdersForCurve = {
'secp256r1': elliptic.curves['p256'].n.shrn(1),
'secp384r1': elliptic.curves['p384'].n.shrn(1)
};

test('\n\n** utils.getCryptoSuite tests **\n\n', (t) => {
var cs = utils.getCryptoSuite({keysize: 384, algorithm: 'EC'}, keyValStorePath5);
t.equal(cs instanceof CryptoSuite_ECDSA_AES, true, 'Should return an instance of CryptoSuite_ECDSA_AES');
t.equal(cs._keySize, 384, 'Returned instance should have keysize of 384');
t.equal(cs._storePath, keyValStorePath5, 'Returned instance should have store path of ' + keyValStorePath5);

cs = utils.getCryptoSuite({keysize: 384, algorithm: 'EC'}, keyValStorePath5);
t.equal(cs instanceof CryptoSuite_ECDSA_AES, true, 'Default test: should return an instance of CryptoSuite_ECDSA_AES');
t.equal(cs._keySize, 384, 'Returned instance should have keysize of 384');
t.equal(cs._storePath, keyValStorePath5, 'Returned instance should have store path of ' + keyValStorePath5);

cs = utils.getCryptoSuite({algorithm: 'EC'}, keyValStorePath5);
t.equal(cs instanceof CryptoSuite_ECDSA_AES, true, 'Should return an instance of CryptoSuite_ECDSA_AES');
t.equal(cs._keySize, 256, 'Returned instance should have keysize of 256');
t.equal(cs._storePath, keyValStorePath5, 'Returned instance should have store path of ' + keyValStorePath5);

// each app instance is expected to use either HSM or software-based key management, as such this question
// is answered with a config setting rather than controlled on a case-by-case basis
utils.setConfigSetting('crypto-hsm', true);
t.throws(
() => {
cs = utils.getCryptoSuite({lib: '/usr/local/lib', slot: 0, pin: '1234' });
},
/Error:.*\/usr\/local\/lib/,
'Should attempt to load the bccsp_pkcs11 module and fail because of the dummy library path'
);

utils.setConfigSetting('crypto-hsm', false);
t.throws(
() => {
cs = utils.getCryptoSuite({lib: '/usr/local/lib', slot: 0, pin: '1234' });
},
/^Error: The "kvsPath" parameter for this constructor, if specified, must be a string specifying a file system path/,
'Should attempt to load the CryptoSuite_ECDSA_AES module and fail because of the invalid file store path'
);

t.end();
});

test('\n\n ** CryptoSuite_ECDSA_AES - constructor tests **\n\n', function (t) {
cleanupFileKeyValueStore(keyValStorePath5);
var keyValueStorePromise = utils.newKeyValueStore({ path: getRelativePath(keyValStorePath5) });
Expand Down
19 changes: 14 additions & 5 deletions test/unit/pkcs11-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ var test = _test(tape);

var crypto = require('crypto');
var util = require('util');
var path = require('path');

var utils = require('fabric-client/lib/utils.js');

var libpath;
Expand All @@ -39,13 +41,20 @@ default:
libpath = '/usr/lib/libacsp-pkcs11.so'; //LinuxOne
}

utils.setConfigSetting('crypto-suite', './impl/bccsp_pkcs11.js');
var cryptoUtils = utils.getCryptoSuite({
lib: libpath,
slot: 0,
pin: '1234' });
// use this specific way to test application overriding the default with a config file
utils.addConfigFile(path.join(__dirname, '../fixtures/config/overrides.json'));

var cryptoUtils;

test('\n\n**PKCS11 - generate an ephemeral key\n\n', (t) => {
t.equal(utils.getConfigSetting('crypto-hsm'), true, 'Verify that the HSM based key management module has been enabled');

cryptoUtils = utils.getCryptoSuite({
lib: libpath,
slot: 0,
pin: '1234'
});

// Test generate AES key, encrypt, and decrypt.
cryptoUtils.generateKey({ algorithm: 'AES', ephemeral: true })
.then((key) => {
Expand Down

0 comments on commit 024f6f0

Please sign in to comment.