Skip to content

Commit

Permalink
Create a keystore class for improved code flow
Browse files Browse the repository at this point in the history
FAB-2084
Current code uses the FileKeyValueStore in combination of special
key index logic to save and retrieve keys. This makes it error
prone. Improved to encapsulate the key persistence strategy in
a key store class.

Change-Id: I1aa378fbbfe88a33fc54d2fafd32e76f0869ecd2
Signed-off-by: Jim Zhang <[email protected]>
  • Loading branch information
jimthematrix committed Feb 7, 2017
1 parent b9d5f26 commit 4cdabba
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 163 deletions.
77 changes: 77 additions & 0 deletions fabric-client/lib/impl/CryptoKeyStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
Copyright 2016-2017 IBM 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.
*/

'use strict';

var jsrsasign = require('jsrsasign');
var KEYUTIL = jsrsasign.KEYUTIL;

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

var logger = utils.getLogger('CryptoKeyStore.js');

var CryptoKeyStore = class extends FKVS {
constructor(options) {
return super(options);
}

getKey(ski) {
var self = this;

// first try the private key entry, since it encapsulates both
// the private key and public key
return this.getValue(_getKeyIndex(ski, true))
.then((raw) => {
if (raw !== null) {
var privKey = KEYUTIL.getKeyFromPlainPrivatePKCS8PEM(raw);
// TODO: for now assuming ECDSA keys only, need to add support for RSA keys
return new ECDSAKey(privKey, privKey.ecparams.keylen);
}

// didn't find the private key entry matching the SKI
// next try the public key entry
return self.getValue(_getKeyIndex(ski, false));
}).then((key) => {
if (key instanceof ECDSAKey)
return key;

if (key !== null) {
var pubKey = KEYUTIL.getKey(key);
return new ECDSAKey(pubKey, pubKey.ecparams.keylen);
}
});
}

putKey(key) {
var idx = _getKeyIndex(key.getSKI(), key.isPrivate());
var pem = key.toBytes();
return this.setValue(idx, pem)
.then(() => {
return key;
});
}
};

function _getKeyIndex(ski, isPrivateKey) {
if (isPrivateKey)
return ski + '-priv';
else
return ski + '-pub';
}

module.exports = CryptoKeyStore;
101 changes: 39 additions & 62 deletions fabric-client/lib/impl/CryptoSuite_ECDSA_AES.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ var EC = elliptic.ec;
var jsrsa = require('jsrsasign');
var KEYUTIL = jsrsa.KEYUTIL;
var util = require('util');
var hashPrimitives = require('../hash.js');
var utils = require('../utils');
var ECDSAKey = require('./ecdsa/key.js');
var BN = require('bn.js');
var Signature = require('elliptic/lib/elliptic/ec/signature.js');
var path = require('path');
const os = require('os');

var hashPrimitives = require('../hash.js');
var utils = require('../utils');
var ECDSAKey = require('./ecdsa/key.js');
var CKS = require('./CryptoKeyStore.js');

var logger = utils.getLogger('crypto_ecdsa_aes');

/**
Expand All @@ -47,26 +49,27 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {
* constructor
*
* @param {number} keySize Key size for the ECDSA algorithm, can only be 256 or 384
* @param {KeyValueStore} kvs An instance of a {@link module:api.KeyValueStore} implementation used to save private keys
* @param {string} kvsPath A path to a directory used by the built-in key store to save private keys
*/
constructor(keySize, kvs) {
constructor(keySize, kvsPath) {
if (keySize !== 256 && keySize !== 384) {
throw new Error('Illegal key size: ' + keySize + ' - this crypto suite only supports key sizes 256 or 384');
}

super();

if (typeof kvs === 'undefined' || kvs === null) {
this._store = null;
if (typeof kvsPath === 'undefined' || kvsPath === null) {
this._storePath = null;
} else {
if (typeof kvs.getValue !== 'function' || typeof kvs.setValue !== 'function') {
throw new Error('The "kvs" parameter for this constructor must be an instance of a KeyValueStore implementation');
if (typeof kvsPath !== 'string') {
throw new Error('The "kvsPath" parameter for this constructor, if specified, must be a string specifying a file system path');
}

this._store = kvs;
this._storePath = kvsPath;
}

this._keySize = keySize;
this._store = null;
this._initialize();
}

Expand Down Expand Up @@ -129,19 +132,15 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {

var self = this;
return new Promise((resolve, reject) => {
self._getKeyValueStore(self._store)
self._getKeyStore()
.then ((store) => {
logger.debug('generateKey, store.setValue');
store.setValue(_getKeyIndex(key.getSKI(), true), KEYUTIL.getPEM(pair.prvKeyObj, 'PKCS8PRV'))
.then(
function() {
return store.putKey(key)
.then(() => {
return resolve(key);
}
).catch(
function(err) {
}).catch((err) => {
reject(err);
}
);
});
});
});
}
Expand Down Expand Up @@ -177,13 +176,9 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {
// save the key in the key store
var theKey = new ECDSAKey(key, key.ecparams.keylen);

var idx = _getKeyIndex(theKey.getSKI(), key.isPrivate);
var pem = (key.isPrivate) ? KEYUTIL.getPEM(key, 'PKCS8PRV') : KEYUTIL.getPEM(key);

// use <ski>-priv or <ski>-pub as the index to the key store entry
return self._getKeyValueStore(self._store)
return self._getKeyStore()
.then ((store) => {
return store.setValue(idx, pem);
return store.putKey(theKey);
}).then(() => {
return resolve(theKey);
}).catch((err) => {
Expand All @@ -209,25 +204,15 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {
var store;

return new Promise((resolve, reject) => {
self._getKeyValueStore(self._store)
self._getKeyStore()
.then ((st) => {
store = st;
// first try the private key entry, since it encapsulates both
// the private key and public key
return store.getValue(_getKeyIndex(ski, true));
}).then((raw) => {
if (raw !== null) {
var privKey = KEYUTIL.getKeyFromPlainPrivatePKCS8PEM(raw);
return resolve(new ECDSAKey(privKey, self._keySize));
} else {
// didn't find the private key entry matching the SKI
// next try the public key entry
return store.getValue(_getKeyIndex(ski, false));
}
return store.getKey(ski);
}).then((key) => {
if (key instanceof ECDSAKey)
return resolve(key);
else if (key !== null) {

if (key !== null) {
var pubKey = KEYUTIL.getKey(key);
return resolve(new ECDSAKey(pubKey, self._keySize));
}
Expand All @@ -237,25 +222,24 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {
});
}

_getKeyValueStore(store) {
_getKeyStore() {
var self = this;
return new Promise((resolve, reject) => {
if (store === null) {
var defaultKS = CryptoSuite_ECDSA_AES.getKeyStorePath();
logger.info('This class requires a KeyValueStore to save keys, no store was passed in, using the default store %s', defaultKS);
store = utils.newKeyValueStore({
path: defaultKS
if (self._store === null) {
var storePath = self._storePath ? self._storePath : CryptoSuite_ECDSA_AES.getDefaultKeyStorePath();
logger.info('This class requires a CryptoKeyStore to save keys, using the store at %s', self._storePath);

new CKS({
path: storePath
})
.then(
function (kvs) {
logger.debug('_getKeyValueStore returning kvs');
self._store = kvs;
resolve(kvs);
}
);
.then((ks) => {
logger.debug('_getKeyStore returning ks');
self._store = ks;
return resolve(self._store);
});
} else {
logger.debug('_getKeyValueStore resolving store');
resolve(store);
logger.debug('_getKeyStore resolving store');
return resolve(self._store);
}
});
}
Expand Down Expand Up @@ -338,18 +322,11 @@ var CryptoSuite_ECDSA_AES = class extends api.CryptoSuite {
throw new Error('Not implemented yet');
}

static getKeyStorePath() {
static getDefaultKeyStorePath() {
return path.join(os.homedir(), '.hfc-key-store');
}
};

function _getKeyIndex(ski, isPrivateKey) {
if (isPrivateKey)
return ski + '-priv';
else
return ski + '-pub';
}

// [Angelo De Caro] ECDSA signatures do not have unique representation and this can facilitate
// replay attacks and more. In order to have a unique representation,
// this change-set forses BCCSP to generate and accept only signatures
Expand Down
2 changes: 1 addition & 1 deletion fabric-client/lib/impl/ecdsa/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ module.exports = class ECDSA_KEY {
// this is specific to the private key format generated by
// npm module 'jsrsasign.KEYUTIL'
if (this.isPrivate()) {
throw new Error('This is a private key, dumping to bytes are not allowed');
return KEYUTIL.getPEM(this._key, 'PKCS8PRV');
} else {
return KEYUTIL.getPEM(this._key);
}
Expand Down
4 changes: 2 additions & 2 deletions fabric-client/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ 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(kvs) {
module.exports.getCryptoSuite = function(opts) {
// 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, kvs);
return new cryptoSuite(keySize, opts);
};

// Provide a Promise-based keyValueStore for couchdb, etc.
Expand Down
Loading

0 comments on commit 4cdabba

Please sign in to comment.