diff --git a/build/tasks/eslint.js b/build/tasks/eslint.js index 5b0fb727fc..5ac410d75f 100644 --- a/build/tasks/eslint.js +++ b/build/tasks/eslint.js @@ -2,7 +2,7 @@ var gulp = require('gulp'); var eslint = require('gulp-eslint'); gulp.task('lint', function () { - return gulp.src(['**/*.js', '!node_modules/**', '!docs/**']) + return gulp.src(['**/*.js', '!node_modules/**', '!docs/**', '!coverage/**', '!tmp/**']) .pipe(eslint( { env: ['es6', 'node'], diff --git a/lib/Member.js b/lib/Member.js index 6f77be131f..9ae03a5aff 100644 --- a/lib/Member.js +++ b/lib/Member.js @@ -220,8 +220,8 @@ var Member = class { return new Promise(function(resolve, reject) { var enrollment = self._enrollment; - if (enrollment) { - return resolve(enrollment); + if (self.isEnrolled()) { + return resolve(self.getEnrollment()); } else { var req = { enrollmentID: self.getName(), @@ -237,13 +237,16 @@ var Member = class { self._enrollment.queryStateKey = self._chain.cryptoPrimitives.generateNonce(); // Save state - return self.saveState(); + return self.saveState() + .then(function() { + return resolve(enrollment); + }); } ).then( - function(data) { + function(enrollment) { // Unmarshall chain key - // TODO: during restore, unmarshall enrollment.chainKey - var ecdsaChainKey = self._chain.cryptoPrimitives.ecdsaPEMToPublicKey(self._enrollment.chainKey); + // TODO: during restore, unmarshall enrollment.chainEncryptionKey + var ecdsaChainKey = self._chain.cryptoPrimitives.getPublicKeyFromPEM(self._enrollment.chainEncryptionKey); self._enrollment.enrollChainKey = ecdsaChainKey; return resolve(enrollment); @@ -361,50 +364,62 @@ var Member = class { * @returns Promise for a ProposalResponse */ sendDeploymentProposal(request) { - // Verify that chaincodePath is being passed + if (!request.endorserUrl || request.endorserUrl === '') { + logger.error('Invalid input parameter to "sendDeploymentProposal": must have "endorserUrl"'); + return Promise.reject(new Error('missing endorserUrl in Deployment proposal request')); + } + if (!request.chaincodePath || request.chaincodePath === '') { logger.error('Invalid input parameter to "sendDeploymentProposal": must have "chaincodePath"'); return Promise.reject(new Error('missing chaincodePath in Deployment proposal request')); } - return new Promise( - function(resolve, reject) { - packageChaincode(request.chaincodePath, request.fcn, request.args) - .then( - function(data) { - var targzFilePath = data[0]; - var hash = data[1]; + if (!request.fcn || request.fnc === '') { + logger.error('Invalid input parameter to "sendDeploymentProposal": must have "fcn" for the target function to call during chaincode initialization'); + return Promise.reject(new Error('missing fcn in Deployment proposal request')); + } - logger.debug('Successfully generated chaincode deploy archive and name hash (%s)', hash); + // args is optional because some chaincode may not need any input parameters during initialization + if (!request.args) { + request.args = []; + } - // at this point, the targzFile has been successfully generated + return packageChaincode(request.chaincodePath, request.fcn, request.args) + .then( + function(data) { + var targzFilePath = data[0]; + var hash = data[1]; - // step 1: construct a ChaincodeSpec - var args = []; - args.push(Buffer.from(request.fcn ? request.fcn : 'init', 'utf8')); + logger.debug('Successfully generated chaincode deploy archive and name hash (%s)', hash); - for (let i=0; i %d', level); + if (this._securityLevel != level) { throw Error('Invalid key. It\'s security does not match the current security level ' + this._securityLevel + ' ' + level); } @@ -158,54 +164,53 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { var EM = cipherText.slice(Rb_len, ct_len - D_len); // encrypted content bytes var D = cipherText.slice(ct_len - D_len); - // debug('Rb :\n', new Buffer(Rb).toString('hex')); - // debug('EM :\n', new Buffer(EM).toString('hex')); - // debug('D :\n', new Buffer(D).toString('hex')); + logger.debug('Rb :\n%s', new Buffer(Rb).toString('hex')); + logger.debug('EM :\n%s', new Buffer(EM).toString('hex')); + logger.debug('D :\n%s', new Buffer(D).toString('hex')); var EC = elliptic.ec; - //var curve = elliptic.curves['p'+level]; var ecdsa = new EC('p' + level); //convert bytes to usable key object var ephPubKey = ecdsa.keyFromPublic(new Buffer(Rb, 'hex'), 'hex'); - //var encPrivKey = ecdsa.keyFromPrivate(ecKeypair2.prvKeyObj.prvKeyHex, 'hex'); var privKey = ecdsa.keyFromPrivate(recipientPrivateKey.prvKeyHex, 'hex'); - // debug('computing Z...', privKey, ephPubKey); + logger.debug('computing Z... %s, %s', privKey, ephPubKey); var Z = privKey.derive(ephPubKey.pub); - // debug('Z computed', Z); - // debug('secret: ', new Buffer(Z.toArray(), 'hex')); + logger.debug('Z computed: %s', Z); + logger.debug('Shared secret: ', new Buffer(Z.toArray(), 'hex')); var kdfOutput = self._hkdf(Z.toArray(), ECIESKDFOutput, null, null); + // obtain the encryption key used to decrypt the token bytes var aesKey = kdfOutput.slice(0, AESKeyLength); + // obtain the hashing key for verifying the token bytes with MAC var hmacKey = kdfOutput.slice(AESKeyLength, AESKeyLength + HMACKeyLength); - // debug('secret: ', new Buffer(Z.toArray(), 'hex')); - // debug('aesKey: ', new Buffer(aesKey, 'hex')); - // debug('hmacKey: ', new Buffer(hmacKey, 'hex')); + logger.debug('aesKey: ', new Buffer(aesKey, 'hex')); + logger.debug('hmacKey: ', new Buffer(hmacKey, 'hex')); var recoveredD = self.hmac(hmacKey, EM); - debug('recoveredD: ', new Buffer(recoveredD).toString('hex')); + logger.debug('recoveredD: ', new Buffer(recoveredD).toString('hex')); if (D.compare(new Buffer(recoveredD)) != 0) { // debug('D='+D.toString('hex')+' vs '+new Buffer(recoveredD).toString('hex')); - throw new Error('HMAC verify failed'); + throw new Error('HMAC verify failed when trying to decrypt token challenge during user enrollment'); } var iv = EM.slice(0, IVLength); var cipher = crypto.createDecipheriv('aes-256-cfb', new Buffer(aesKey), iv); var decryptedBytes = cipher.update(EM.slice(IVLength)); - // debug('decryptedBytes: ',new Buffer(decryptedBytes).toString('hex')); + logger.debug('decryptedBytes: ',new Buffer(decryptedBytes).toString('hex')); return decryptedBytes; } getKeyPairForSigning(key, encoding) { // select curve and hash algo based on level var keypair = new EC(this._ecdsaCurve).keyFromPrivate(key, encoding); - debug('keypair: ', keypair); + logger.debug('keypair: ', keypair); return keypair; } getKeyPairForEncryption(key, encoding) { var publicKey = new EC(this._ecdsaCurve).keyFromPublic(key, encoding); - // debug('publicKey: [%j]', publicKey); + logger.debug('publicKey: [%j]', publicKey); return publicKey; } @@ -213,15 +218,15 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { var ecdsa = new EC(this._ecdsaCurve); var signKey = ecdsa.keyFromPrivate(key, 'hex'); var sig = ecdsa.sign(new Buffer(this._hashFunction(msg), 'hex'), signKey); - debug('ecdsa signature: ', sig); + logger.debug('ecdsa signature: ', sig); return sig; } - ecdsaPEMToPublicKey(chainKey) { + getPublicKeyFromPEM(chainKey) { // enrollChainKey is a PEM. Extract the key from it. var pem = new Buffer(chainKey, 'hex').toString(); - debug('ChainKey %s', pem); - chainKey = KEYUTIL.getHexFromPEM(pem, 'ECDSA PUBLIC KEY'); + logger.debug('ChainKey %s', pem); + chainKey = KEYUTIL.getHexFromPEM(pem, PEM_HEADER_PUBLIC_KEY); // debug(chainKey); var certBuffer = utils.toArrayBuffer(new Buffer(chainKey, 'hex')); var asn1 = certParser.org.pkijs.fromBER(certBuffer); @@ -274,38 +279,37 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { // Derive a shared secret field element z from the ephemeral secret key k // and convert z to an octet string Z - // debug('ecdsa.keyFromPublic=%s', util.inspect(ecdsaRecipientPublicKey));//XXX var Z = ephPrivKey.derive(ecdsaRecipientPublicKey.pub); - // debug('[Z]: %j', Z); + logger.debug('[Z]: %j', Z); var kdfOutput = self._hkdf(Z.toArray(), ECIESKDFOutput, null, null); - // debug('[kdfOutput]: %j', new Buffer(new Buffer(kdfOutput).toString('hex'), 'hex').toString('hex')); + logger.debug('[kdfOutput]: %j', new Buffer(new Buffer(kdfOutput).toString('hex'), 'hex').toString('hex')); var aesKey = kdfOutput.slice(0, AESKeyLength); var hmacKey = kdfOutput.slice(AESKeyLength, AESKeyLength + HMACKeyLength); - // debug('[Ek] ', new Buffer(aesKey, 'hex')); - // debug('[Mk] ', new Buffer(hmacKey, 'hex')); + logger.debug('[Ek] ', new Buffer(aesKey, 'hex')); + logger.debug('[Mk] ', new Buffer(hmacKey, 'hex')); var iv = crypto.randomBytes(IVLength); var cipher = crypto.createCipheriv('aes-256-cfb', new Buffer(aesKey), iv); - // debug('MSG %j: ', msg); + logger.debug('MSG %j: ', msg); var encryptedBytes = cipher.update(msg); - // debug('encryptedBytes: ',JSON.stringify(encryptedBytes)); + logger.debug('encryptedBytes: ',JSON.stringify(encryptedBytes)); var EM = Buffer.concat([iv, encryptedBytes]); var D = self.hmac(hmacKey, EM); - // debug('[Rb] ', new Buffer(Rb,'hex').toString('hex')+' len='+Rb.length); - // debug('[EM] ', EM.toString('hex')); - // debug('[D] ', new Buffer(D).toString('hex')); + logger.debug('[Rb] ', new Buffer(Rb,'hex').toString('hex')+' len='+Rb.length); + logger.debug('[EM] ', EM.toString('hex')); + logger.debug('[D] ', new Buffer(D).toString('hex')); return Buffer.concat([new Buffer(Rb, 'hex'), EM, new Buffer(D)]); } eciesEncrypt(recipientPublicKey, msg) { var level = recipientPublicKey.ecparams.keylen; - // debug('=============> %d', level); + logger.debug('=============> %d', level); var EC = elliptic.ec; var curve = elliptic.curves['p' + level]; - // debug('=============> curve=%s', util.inspect(curve)); + logger.debug('=============> curve=%s', util.inspect(curve)); var ecdsa = new EC(curve); return this.eciesEncryptECDSA(ecdsa.keyFromPublic(recipientPublicKey.pubKeyHex, 'hex'), msg); @@ -320,17 +324,17 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { var iv = crypto.randomBytes(IVLength); var aes = new aesjs.ModeOfOperation.cfb(key, iv, IVLength); - debug('encryptedBytes: ', encryptedBytes); + logger.debug('encryptedBytes: ', encryptedBytes); //need to pad encryptedBytes to multiples of 16 var numMissingBytes = IVLength - (encryptedBytes.length % AESBlockLength); - debug('missingBytes: ', numMissingBytes); + logger.debug('missingBytes: ', numMissingBytes); if (numMissingBytes > 0) { encryptedBytes = Buffer.concat([encryptedBytes, new Buffer(numMissingBytes)]); } - debug('encryptedBytes: ', encryptedBytes); + logger.debug('encryptedBytes: ', encryptedBytes); var decryptedBytes = aes.decrypt(encryptedBytes); @@ -375,13 +379,13 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { hmac(key, bytes) { - debug('key: ', JSON.stringify(key)); - debug('bytes: ', JSON.stringify(bytes)); + logger.debug('HMAC key: ', JSON.stringify(key)); + logger.debug('bytes to digest: ', JSON.stringify(bytes)); var hmac = new sjcl.misc.hmac(bytesToBits(key), this._hashFunctionKeyDerivation); hmac.update(bytesToBits(bytes)); var result = hmac.digest(); - debug('result: ', bitsToBytes(result)); + logger.debug('HMAC digest: ', bitsToBytes(result)); return bitsToBytes(result); } @@ -391,7 +395,6 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { } hash(bytes) { - debug('bytes: ', JSON.stringify(bytes)); return this._hashFunction(bytes); } @@ -422,19 +425,19 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { switch (this._suite) { case 'sha3-256': - debug('Using sha3-256'); + logger.debug('Using sha3-256'); this._hashFunction = sha3_256; this._hashFunctionKeyDerivation = hashPrimitives.hash_sha3_256; this._hashOutputSize = 32; break; case 'sha3-384': - debug('Using sha3-384'); + logger.debug('Using sha3-384'); this._hashFunction = sha3_384; this._hashFunctionKeyDerivation = hashPrimitives.hash_sha3_384; this._hashOutputSize = 48; break; case 'sha2-256': - debug('Using sha2-256'); + logger.debug('Using sha2-256'); this._hashFunction = hashPrimitives.sha2_256; this._hashFunctionKeyDerivation = hashPrimitives.hash_sha2_256; this._hashOutputSize = 32; @@ -506,12 +509,13 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { } static _CBCDecrypt(key, bytes) { - debug('key length: ', key.length); - debug('bytes length: ', bytes.length); + logger.debug('Decrypt key length: ', key.length); + logger.debug('Decrypt bytes length: ', bytes.length); + var iv = bytes.slice(0, BlockSize); - debug('iv length: ', iv.length); + logger.debug('Decrypt iv length: ', iv.length); var encryptedBytes = bytes.slice(BlockSize); - debug('encrypted bytes length: ', encryptedBytes.length); + logger.debug('encrypted bytes length: ', encryptedBytes.length); var decryptedBlocks = []; var decryptedBytes; @@ -523,10 +527,10 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { var end = BlockSize; while (end <= encryptedBytes.length) { let aesCbc = new aesjs.ModeOfOperation.cbc(key, iv); - debug('start|end', start, end); + logger.debug('start|end', start, end); var encryptedBlock = encryptedBytes.slice(start, end); var decryptedBlock = aesCbc.decrypt(encryptedBlock); - debug('decryptedBlock: ', decryptedBlock); + logger.debug('decryptedBlock: ', decryptedBlock); decryptedBlocks.push(decryptedBlock); //iv for next round equals previous block iv = encryptedBlock; @@ -541,7 +545,7 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { decryptedBytes = aesCbc.decrypt(encryptedBytes); } - debug('decrypted bytes: ', JSON.stringify(decryptedBytes)); + logger.debug('decrypted bytes: ', JSON.stringify(decryptedBytes)); return decryptedBytes; @@ -551,10 +555,8 @@ var CryptoSuite_ECDSA_SHA = class extends api.CryptoSuite { //last byte is the number of padded bytes var padding = bytes.readUInt8(bytes.length - 1); - debug('padding: ', padding); //should check padded bytes, but just going to extract var unpadded = bytes.slice(0, bytes.length - padding); - debug('unpadded bytes: ', JSON.stringify(unpadded)); return unpadded; } diff --git a/lib/impl/MemberServices.js b/lib/impl/MemberServices.js index b921910e10..214869eecf 100644 --- a/lib/impl/MemberServices.js +++ b/lib/impl/MemberServices.js @@ -24,6 +24,8 @@ var path = require('path'); var grpc = require('grpc'); var _caProto = grpc.load(path.join(__dirname, '../../lib/protos/ca.proto')).protos; +var logger = utils.getLogger('MemberServices.js'); + /** * This is the default implementation of a member services client. * @@ -51,6 +53,12 @@ var MemberServices = class extends api.MemberServices { this._tcapClient = new _caProto.TCAP(ep.addr, ep.creds, options); this._tlscapClient = new _caProto.TLSCAP(ep.addr, ep.creds, options); this.cryptoPrimitives = utils.getCryptoSuite(); + + logger.info('Successfully constructed member service client: endpoint - %j, crypto - %s/%s/%s', + ep, + this.cryptoPrimitives.getPublicKeyAlgorithm(), + this.cryptoPrimitives.getSecurityLevel(), + this.cryptoPrimitives.getHashAlgorithm()); } /** @@ -105,13 +113,13 @@ var MemberServices = class extends api.MemberServices { return new Promise(function(resolve, reject) { if (!req.enrollmentID) { - reject(new Error('missing req.enrollmentID')); - return; + logger.error('Invalid register request, missing enrollmentID'); + return reject(new Error('missing req.enrollmentID')); } if (!registrar) { - reject(new Error('chain registrar is not set')); - return; + logger.error('Invalid register call, missing registrar parameter'); + return reject(new Error('chain registrar is not set')); } var protoReq = new _caProto.RegisterUserReq(); @@ -135,7 +143,7 @@ var MemberServices = class extends api.MemberServices { // Sign the registration request var buf = protoReq.toBuffer(); - var signKey = self.cryptoPrimitives.getKeyPairForSigning(registrar.getEnrollment().key, 'hex'); + var signKey = self.cryptoPrimitives.getKeyPairForSigning(registrar.getEnrollment().privateKey, 'hex'); var sig = self.cryptoPrimitives.sign(signKey, buf); protoReq.setSig( new _caProto.Signature( { @@ -148,8 +156,10 @@ var MemberServices = class extends api.MemberServices { // Send the registration request self._ecaaClient.registerUser(protoReq, function (err, token) { if (err) { - reject(err); + logger.error('Received error from server on the register request. %s', err); + return reject(err); } else { + logger.info('Successfully registered user "%s"', protoReq.getId().id); return resolve(token ? token.tok.toString() : null); } }); @@ -157,7 +167,7 @@ var MemberServices = class extends api.MemberServices { } /** - * Enroll the member and return an opaque member object + * Enroll the member and return an opaque member object. * @param req Enrollment request with the following fields: name, enrollmentSecret * @returns Promise for [Enrollment]{@link module:api.Enrollment} * @ignore @@ -167,17 +177,17 @@ var MemberServices = class extends api.MemberServices { return new Promise(function(resolve, reject) { if (!req.enrollmentID) { - reject(new Error('req.enrollmentID is not set')); - return; + logger.error('Invalid enroll request, missing enrollmentID'); + return reject(new Error('req.enrollmentID is not set')); } if (!req.enrollmentSecret) { - reject(new Error('req.enrollmentSecret is not set')); - return; + logger.error('Invalid enroll request, missing enrollmentSecret'); + return reject(new Error('req.enrollmentSecret is not set')); } - // generate key pairs for signing and encryption - // 1) signing key + // generate certificate pairs for signing and encryption + // 1) signature verifcation key var signingKeyPair = self.cryptoPrimitives.generateKeyPair(); var spki = new asn1.x509.SubjectPublicKeyInfo(signingKeyPair.pubKeyObj); // 2) encryption key @@ -191,7 +201,7 @@ var MemberServices = class extends api.MemberServices { eCertCreateRequest.setId({id: req.enrollmentID}); eCertCreateRequest.setTok({tok: new Buffer(req.enrollmentSecret)}); - // public signing key (ecdsa) + // public signature verification key var signPubKey = new _caProto.PublicKey( { type: _caProto.CryptoType[self.cryptoPrimitives.getPublicKeyAlgorithm()], @@ -199,7 +209,7 @@ var MemberServices = class extends api.MemberServices { }); eCertCreateRequest.setSign(signPubKey); - // public encryption key (ecdsa) + // public encryption key var encPubKey = new _caProto.PublicKey( { type: _caProto.CryptoType[self.cryptoPrimitives.getPublicKeyAlgorithm()], @@ -209,16 +219,17 @@ var MemberServices = class extends api.MemberServices { self._ecapClient.createCertificatePair(eCertCreateRequest, function (err, eCertCreateResp) { if (err) { - reject(err); - return; + logger.error('Failed to receive challenge response from CA. Error: %s', err.stack ? err.stack : err); + return reject(err); } + // response from the member service on a certificate request is a challenge to prove + // possession of the private key. Use the private key to decrypt the token that has + // been encrypted by the member service with the encryption public key included in the + // certificate request var cipherText = eCertCreateResp.tok.tok; var decryptedTokBytes = self.cryptoPrimitives.asymmetricDecrypt(encryptionKeyPair.prvKeyObj, cipherText); - //debug(decryptedTokBytes); - // debug(decryptedTokBytes.toString()); - // debug('decryptedTokBytes [%s]', decryptedTokBytes.toString()); eCertCreateRequest.setTok({tok: decryptedTokBytes}); eCertCreateRequest.setSig(null); @@ -237,16 +248,13 @@ var MemberServices = class extends api.MemberServices { )); self._ecapClient.createCertificatePair(eCertCreateRequest, function (err, eCertCreateResp) { if (err) { - reject(err); - return; + logger.error('Failed to receive certificate issuance response from CA. Error: %s', err.stack ? err.stack : err); + return reject(err); } - var enrollment = { - key: signingKeyPair.prvKeyObj.prvKeyHex, - cert: eCertCreateResp.certs.sign.toString('hex'), - chainKey: eCertCreateResp.pkchain.toString('hex') - }; - // debug('cert:\n\n',enrollment.cert) + var enrollment = new api.Enrollment(signingKeyPair.prvKeyObj.prvKeyHex, eCertCreateResp.certs.sign.toString('hex'), eCertCreateResp.pkchain.toString('hex')); + + logger.info('Successfully enrolled user "%s"', eCertCreateRequest.getId().id); return resolve(enrollment); }); }); diff --git a/package.json b/package.json index ac2723d5f2..ce46a24368 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "asn1js": "^1.2.12", "bn.js": "^4.11.3", "crypto": "0.0.3", - "debug": "^2.2.0", "elliptic": "^6.2.3", "events": "^1.1.0", "fs": "0.0.2", diff --git a/test/unit/headless-tests.js b/test/unit/headless-tests.js index eba1d6c4c0..f2033a9ba3 100644 --- a/test/unit/headless-tests.js +++ b/test/unit/headless-tests.js @@ -455,6 +455,61 @@ test('Member constructor set get tests', function(t) { }); +test('Member sendDeploymentProposal() tests', function(t) { + var m = new Member('does not matter', _chain); + + var p1 = m.sendDeploymentProposal({ + chaincodePath: 'blah', + fcn: 'init' + }).then(function() { + t.fail('Should not have been able to resolve the promise because of missing "endorserUrl" parameter'); + }).catch(function(err) { + if (err.message === 'missing endorserUrl in Deployment proposal request') { + t.pass('Successfully caught missing endorserUrl error'); + } else { + t.fail('Failed to catch the missing endorserUrl error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p2 = m.sendDeploymentProposal({ + endorserUrl: 'blah', + fcn: 'init' + }).then(function() { + t.fail('Should not have been able to resolve the promise because of missing "chaincodePath" parameter'); + }).catch(function(err) { + if (err.message === 'missing chaincodePath in Deployment proposal request') { + t.pass('Successfully caught missing chaincodePath error'); + } else { + t.fail('Failed to catch the missing chaincodePath error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p3 = m.sendDeploymentProposal({ + endorserUrl: 'blah', + chaincodePath: 'blah' + }).then(function() { + t.fail('Should not have been able to resolve the promise because of missing "fcn" parameter'); + }).catch(function(err) { + if (err.message === 'missing fcn in Deployment proposal request') { + t.pass('Successfully caught missing fcn error'); + } else { + t.fail('Failed to catch the missing fcn error. Error: ' + err.stack ? err.stask : err); + } + }); + + Promise.all([p1, p2, p3]) + .then( + function(data) { + t.end(); + } + ).catch( + function(err) { + t.fail(err.stack ? err.stack : err); + t.end(); + } + ); +}); + test('CryptoSuite_ECDSA_SHA constructor tests', function(t) { cryptoUtils = utils.getCryptoSuite();