From f4826bd1213e8f09421edf789581276989202ec4 Mon Sep 17 00:00:00 2001 From: Bret Harrison Date: Sat, 17 Feb 2018 13:18:13 -0500 Subject: [PATCH] FAB-8351 NodeSDK - add parms to PKCS11 Add the ability to configure the usertype and readwrite settings when creating a session with a HSM. Change-Id: Iab7be50a0452a0384b895d1981fa46f33620979e Signed-off-by: Bret Harrison --- fabric-client/lib/impl/bccsp_pkcs11.js | 89 ++++++++--- test/unit/cryptosuite-pkcs11.js | 206 +++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 20 deletions(-) create mode 100644 test/unit/cryptosuite-pkcs11.js diff --git a/fabric-client/lib/impl/bccsp_pkcs11.js b/fabric-client/lib/impl/bccsp_pkcs11.js index a723c6c2bf..0590fc8c71 100644 --- a/fabric-client/lib/impl/bccsp_pkcs11.js +++ b/fabric-client/lib/impl/bccsp_pkcs11.js @@ -1,5 +1,5 @@ /** - * Copyright 2017 IBM All Rights Reserved. + * Copyright 2017, 2018 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. @@ -79,19 +79,45 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { /** * @param {number} keySize Length of key (in bytes), a.k.a "security level" * @param {string} hash Optional. Hash algorithm, supported values are "SHA2" and "SHA3" - * @param {Object} opts Option is the form { lib: string, slot: number, pin: string } - * - * If lib is not specified or null, its value will be taken from the + * @param {Object} opts Options are of the form + *
+	 *   {
+	 *     lib: string,       // the library package to support this implementation
+	 *     slot: number,      // the hardware slot number
+	 *     pin: string,       // the user's PIN
+	 *     usertype: number,  // the user type
+	 *     readwrite: boolean // true if the session is read/write or false if read-only
+ 	 *   }
+	 * 
+ * If 'lib' is not specified or null, its value will be taken from the * CRYPTO_PKCS11_LIB env var, and if the env var is not set, its value will * be taken from the crypto-pkcs11-lib key in the configuration file. - * - * If slot is not specified or null, its value will be taken from the + *

+ * If 'slot' is not specified or null, its value will be taken from the * CRYPTO_PKCS11_SLOT env var, and if the env var is not set, its value will * be taken from the crypto-pkcs11-slot key in the configuration file. - * - * If pin is not specified or null, its value will be taken from the + *

+ * If 'pin' is not specified or null, its value will be taken from the * CRYPTO_PKCS11_PIN env var, and if the env var is not set, its value will * be taken from the crypto-pkcs11-pin key in the configuration file. + *

+ * If 'usertype' is not specified or null, its value will be taken from the + * CRYPTO_PKCS11_USERTYPE env var, if the env var is not set, its value will + * be taken from the crypto-pkcs11-usertype key in the configuration file, + * if the config value is not set, its value will default to 1. + * The value will not be validated, assumes the C_Login will validate. + * --- from http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html + *
+	 * 0          CKU_SO                          0UL
+	 * 1          CKU_USER                        1UL
+	 * 2          CKU_CONTEXT_SPECIFIC            2UL
+	 * 4294967295 max allowed            0xFFFFFFFFUL
+	 *
+ *
+ * If 'readwrite' is not specified or null, its value will be taken from the + * CRYPTO_PKCS11_READWRITE env var, if the env var is not set, its value will + * be taken from the crypto-pkcs11-readwrite key in the configuration file, + * if the config value is not set, its value will default to true. */ constructor(keySize, hash, opts) { if (typeof keySize === 'undefined' || keySize === null) @@ -100,7 +126,7 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { if (keySize != 256 && keySize != 384) throw new Error(__func() + 'only 256 or 384 bits key sizes are supported'); - logger.info(__func() + 'keySize: ' + keySize); + logger.debug(__func() + 'keySize: ' + keySize); /* * If no lib specified, get it from env var or config file. */ @@ -110,7 +136,7 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { if (typeof pkcs11Lib === 'undefined' || pkcs11Lib === null || typeof pkcs11Lib !== 'string') throw new Error(__func() + 'PKCS11 library path must be specified'); - logger.info(__func() + 'PKCS11 library: ' + pkcs11Lib); + logger.debug(__func() + 'PKCS11 library: ' + pkcs11Lib); /* * If no slot specified, get it from env var or config file. */ @@ -122,7 +148,27 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { if (typeof pkcs11Slot === 'string') pkcs11Slot = parseInt(pkcs11Slot, 10); if (isNaN(pkcs11Slot)) throw new Error(__func() + 'PKCS11 slot number invalid'); - logger.info(__func() + 'PKCS11 slot: ' + pkcs11Slot); + logger.debug(__func() + 'PKCS11 slot: ' + pkcs11Slot); + /* + * If no user type is specified, check env var or config file, then + * default to 1 (pkcs11js.CKU_USER) + */ + var pkcs11UserType = opts ? opts.usertype: null; + if (typeof pkcs11UserType === 'undefined' || pkcs11UserType === null) + pkcs11UserType = utils.getConfigSetting('crypto-pkcs11-usertype', 1); + if(!Number.isInteger(pkcs11UserType)) { + throw new Error(__func() + 'PKCS11 usertype number invalid'); + } + /* + * If no read write specified, check env var or config file, then + * default to true + */ + var pkcs11ReadWrite = opts ? opts.readwrite: null; + if (typeof pkcs11ReadWrite === 'undefined' || pkcs11ReadWrite === null) + pkcs11ReadWrite = utils.getConfigSetting('crypto-pkcs11-readwrite', true); + if (typeof pkcs11ReadWrite !== 'boolean') { + throw new Error(__func() + 'PKCS11 readwrite is invalid'); + } /* * If no pin specified, get it from env var or config file. */ @@ -177,7 +223,7 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { } this._pkcs11 = _pkcs11; - this._pkcs11OpenSession(this._pkcs11, pkcs11Lib, pkcs11Slot, pkcs11Pin); + this._pkcs11OpenSession(this._pkcs11, pkcs11Lib, pkcs11Slot, pkcs11Pin, pkcs11UserType, pkcs11ReadWrite); /* * SKI to key cache for getKey(ski) function. */ @@ -225,9 +271,9 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { /* * Open pkcs11 session and login. */ - _pkcs11OpenSession(pkcs11, pkcs11Lib, pkcs11Slot, pkcs11Pin) { - logger.debug(__func() + 'parameters are pkcs11Slot %s pkcs11Pin %s pkcs11Lib %s', - pkcs11Slot, pkcs11Pin, pkcs11Lib); + _pkcs11OpenSession(pkcs11, pkcs11Lib, pkcs11Slot, pkcs11Pin, pkcs11UserType, pkcs11ReadWrite) { + logger.debug(__func() + 'parameters are pkcs11Slot %s pkcs11Lib %s', pkcs11Slot, pkcs11Lib); + if (!_initialized) { pkcs11.load(pkcs11Lib); pkcs11.C_Initialize(); @@ -261,8 +307,12 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { /* * Open session. */ - this._pkcs11Session = pkcs11.C_OpenSession( - slot, pkcs11js.CKF_RW_SESSION|pkcs11js.CKF_SERIAL_SESSION); + let flags = pkcs11js.CKF_SERIAL_SESSION; + if(pkcs11ReadWrite) { + flags = flags | pkcs11js.CKF_RW_SESSION; + } + this._pkcs11Session = pkcs11.C_OpenSession(slot, flags); + // Getting info about Session logger.debug(__func() + 'C_GetSessionInfo(' + util.inspect( @@ -273,10 +323,9 @@ var CryptoSuite_PKCS11 = class extends api.CryptoSuite { /* * Login with PIN. Error will be thrown if wrong PIN. */ - pkcs11.C_Login(this._pkcs11Session, - 1/*pkcs11js.CKU_USER*/, pkcs11Pin); + pkcs11.C_Login(this._pkcs11Session, pkcs11UserType, pkcs11Pin); this._pkcs11Login = true; - logger.info(__func() + 'session login successful'); + logger.debug(__func() + 'session login successful'); //pkcs11.C_Logout(session); //pkcs11.C_CloseSession(session); diff --git a/test/unit/cryptosuite-pkcs11.js b/test/unit/cryptosuite-pkcs11.js new file mode 100644 index 0000000000..1c804216a7 --- /dev/null +++ b/test/unit/cryptosuite-pkcs11.js @@ -0,0 +1,206 @@ +/** + * Copyright 2018 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 tape = require('tape'); +var _test = require('tape-promise'); +var test = _test(tape); +var testutil = require('./util.js'); +var Client = require('fabric-client'); +var PKCS11 = require('fabric-client/lib/impl/bccsp_pkcs11.js'); + +test('\n\n** bccsp_pkcs11 tests **\n\n', (t) => { + testutil.resetDefaults(); + + t.throws( + function () { + let pkcss11 = new PKCS11(); + }, + /keySize must be specified/, + 'Checking: keySize must be specified' + ); + t.throws( + function () { + let pkcss11 = new PKCS11(222); + }, + /only 256 or 384 bits key sizes are supported/, + 'Checking: only 256 or 384 bits key sizes are supported' + ); + t.throws( + function () { + let pkcss11 = new PKCS11(256); + }, + /PKCS11 library path must be specified/, + 'Checking: PKCS11 key size is specified and valid' + ); + let opts = {lib: '/temp'}; + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2', opts); + }, + /PKCS11 slot must be specified/, + 'Checking: PKCS11 lib must be specified' + ); + opts.slot = 'a'; + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2', opts); + }, + /PKCS11 slot number invalid/, + 'Checking: PKCS11 slot number invalid' + ); + opts.slot = 2; + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2', opts); + }, + /PKCS11 PIN must be set/, + 'Checking: PKCS11 slot must be set to a number' + ); + opts.pin = 7; + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2', opts); + }, + /PKCS11 PIN must be set/, + 'Checking: PKCS11 PIN must be set to a string' + ); + + // getting the missing file or image means the same thing to these tests + // that we have gotten a good respond to the parameter and the failure is + // after that check, so that parameter that is being tested is valid + let checkError = function(error, msg) { + let error_msg = error.toString(); + if(error_msg.indexOf('no suitable image found') > -1 || error_msg.indexOf('No such file or directory') > -1) { + t.pass(msg); + } else { + t.fail(msg + ' failed with ::' + error_msg); + } + }; + + opts.pin = 'pin'; + let testing = 'Checking: for valid PIN'; + try { + let pkcss11 = new PKCS11(256, 'sha2', opts); + t.fail(testing); + } catch(error) { + checkError(error,testing); + } + + opts.usertype = 'a'; + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2', opts); + }, + /usertype number invalid/, + 'Checking: for valid usertype' + ); + opts.usertype = 2; + testing = 'Checking: for valid usertype'; + try { + let pkcss11 = new PKCS11(256, 'sha2', opts); + t.fail(testing); + } catch(error) { + checkError(error,testing); + } + + opts.readwrite = 'not'; + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2', opts); + }, + /readwrite is invalid/, + 'Checking: for valid readwrite' + ); + opts.readwrite = false; + testing = 'Checking: for valid readwrite'; + try { + let pkcss11 = new PKCS11(256, 'sha2', opts); + t.fail(testing); + } catch(error) { + checkError(error,testing); + } + + Client.setConfigSetting('crypto-pkcs11-lib', '/temp'); + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2'); + }, + /PKCS11 slot must be specified/, + 'Checking: PKCS11 lib must be specified' + ); + Client.setConfigSetting('crypto-pkcs11-slot', 2); + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2'); + }, + /PKCS11 PIN must be set/, + 'Checking: PKCS11 slot must be set to a number' + ); + Client.setConfigSetting('crypto-pkcs11-pin', 'PIN'); + testing = 'Checking: for valid PIN in config'; + try { + let pkcss11 = new PKCS11(256, 'sha2'); + t.fail(testing); + } catch(error) { + checkError(error,testing); + } + + Client.setConfigSetting('crypto-pkcs11-usertype', 'not'); + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2'); + }, + /usertype number invalid/, + 'Checking: for valid usertype' + ); + Client.setConfigSetting('crypto-pkcs11-usertype', 1.2); + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2'); + }, + /usertype number invalid/, + 'Checking: for valid usertype' + ); + Client.setConfigSetting('crypto-pkcs11-usertype', 2); + testing = 'Checking: for valid usertype in config'; + try { + let pkcss11 = new PKCS11(256, 'sha2'); + t.fail(testing); + } catch(error) { + checkError(error,testing); + } + + Client.setConfigSetting('crypto-pkcs11-readwrite', 'false'); + t.throws( + function () { + let pkcss11 = new PKCS11(256, 'sha2'); + }, + /readwrite is invalid/, + 'Checking: for valid readwrite' + ); + Client.setConfigSetting('crypto-pkcs11-readwrite', false); + testing = 'Checking: for valid readwrite in config'; + try { + let pkcss11 = new PKCS11(256, 'sha2'); + t.fail(testing); + } catch(error) { + checkError(error,testing); + } + + t.end(); +});