diff --git a/.jshintrc b/.jshintrc index f01e0f03..086caa58 100644 --- a/.jshintrc +++ b/.jshintrc @@ -29,6 +29,9 @@ "before": true, "beforeEach": true, "after": true, - "afterEach": true + "afterEach": true, + "escape": true, + "unescape": true, + "asmCrypto": true } } \ No newline at end of file diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 3a8e8d3b..39efa2d1 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -24,6 +24,8 @@ * @module crypto/public_key/rsa */ +'use strict'; + var BigInteger = require('./jsbn.js'), util = require('../../util.js'), random = require('../random.js'), @@ -119,7 +121,7 @@ function RSA() { // "empty" RSA key constructor - function keyObject() { + function KeyObject() { this.n = null; this.e = 0; this.ee = null; @@ -165,10 +167,10 @@ function RSA() { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - + keys = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); - if (!(typeof keys.then === 'function')) { // IE11 KeyOperation - keys = convertKeyOperation(keys, 'Error generating RSA key pair.'); + if (typeof keys.then !== 'function') { // IE11 KeyOperation + keys = util.promisifyIE11Op(keys, 'Error generating RSA key pair.'); } } @@ -185,15 +187,15 @@ function RSA() { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 var key = webCrypto.exportKey('jwk', keypair.privateKey); - if (!(typeof key.then === 'function')) { // IE11 KeyOperation - key = convertKeyOperation(key, 'Error exporting RSA key pair.'); + if (typeof key.then !== 'function') { // IE11 KeyOperation + key = util.promisifyIE11Op(key, 'Error exporting RSA key pair.'); } return key; } function decodeKey(jwk) { // map JWK parameters to local BigInteger type system - var key = new keyObject(); + var key = new KeyObject(); key.n = toBigInteger(jwk.n); key.ee = new BigInteger(E, 16); key.d = toBigInteger(jwk.d); @@ -210,23 +212,12 @@ function RSA() { return key; } - function convertKeyOperation(keyop, errmsg) { - return new Promise(function(resolve, reject) { - keyop.onerror = function (err) { - reject(new Error(errmsg)); - }; - keyop.oncomplete = function (e) { - resolve(e.target.result); - }; - }); - } - // // JS code // return new Promise(function(resolve) { - var key = new keyObject(); + var key = new KeyObject(); var rng = new SecureRandom(); var qs = B >> 1; key.e = parseInt(E, 16); @@ -235,13 +226,15 @@ function RSA() { for (;;) { for (;;) { key.p = new BigInteger(B - qs, 1, rng); - if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) + if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) { break; + } } for (;;) { key.q = new BigInteger(qs, 1, rng); - if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) + if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) { break; + } } if (key.p.compareTo(key.q) <= 0) { var t = key.p; @@ -270,7 +263,7 @@ function RSA() { this.verify = verify; this.sign = sign; this.generate = generate; - this.keyObject = keyObject; + this.keyObject = KeyObject; } module.exports = RSA; diff --git a/src/util.js b/src/util.js index a0d7af54..73427e8d 100644 --- a/src/util.js +++ b/src/util.js @@ -69,11 +69,14 @@ module.exports = { var i = 0; while (c < e) { h = str.charCodeAt(c++).toString(16); - while (h.length < 2) h = "0" + h; + while (h.length < 2) { + h = "0" + h; + } r.push(" " + h); i++; - if (i % 32 === 0) + if (i % 32 === 0) { r.push("\n "); + } } return r.join(''); }, @@ -84,15 +87,18 @@ module.exports = { * @return {String} String containing the hexadecimal values */ hexstrdump: function (str) { - if (str === null) + if (str === null) { return ""; + } var r = []; var e = str.length; var c = 0; var h; while (c < e) { h = str.charCodeAt(c++).toString(16); - while (h.length < 2) h = "0" + h; + while (h.length < 2) { + h = "0" + h; + } r.push("" + h); } return r.join(''); @@ -105,8 +111,9 @@ module.exports = { */ hex2bin: function (hex) { var str = ''; - for (var i = 0; i < hex.length; i += 2) + for (var i = 0; i < hex.length; i += 2) { str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } return str; }, @@ -122,7 +129,9 @@ module.exports = { var h; while (c < e) { h = str[c++].toString(16); - while (h.length < 2) h = "0" + h; + while (h.length < 2) { + h = "0" + h; + } r.push("" + h); } return r.join(''); @@ -189,7 +198,7 @@ module.exports = { str2Uint8Array: function (str) { // Uncomment for debugging - if(!(typeof str === 'string') && !String.prototype.isPrototypeOf(str)) { + if(typeof str !== 'string' && !String.prototype.isPrototypeOf(str)) { throw new Error('str2Uint8Array: Data must be in the form of a string'); } @@ -227,7 +236,7 @@ module.exports = { * @param {Array} Array of Uint8Arrays to concatenate * @return {Uint8array} Concatenated array */ - concatUint8Array: function (arrays) { + concatUint8Array: function (arrays) { var totalLength = 0; arrays.forEach(function (element) { @@ -248,7 +257,7 @@ module.exports = { }); return result; - }, + }, /** * Deep copy Uint8Array @@ -342,8 +351,9 @@ module.exports = { getLeftNBits: function (string, bitcount) { var rest = bitcount % 8; - if (rest === 0) + if (rest === 0) { return string.substring(0, bitcount / 8); + } var bytes = (bitcount - rest) / 8 + 1; var result = string.substring(0, bytes); return this.shiftRight(result, 8 - rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF); @@ -357,17 +367,18 @@ module.exports = { * @return {String} Resulting string. */ shiftRight: function (value, bitcount) { - var temp = util.str2bin(value); + var temp = this.str2bin(value); if (bitcount % 8 !== 0) { for (var i = temp.length - 1; i >= 0; i--) { temp[i] >>= bitcount % 8; - if (i > 0) + if (i > 0) { temp[i] |= (temp[i - 1] << (8 - (bitcount % 8))) & 0xFF; + } } } else { return value; } - return util.bin2str(temp); + return this.bin2str(temp); }, /** @@ -413,5 +424,40 @@ module.exports = { return window.msCrypto.subtle; } } + }, + + /** + * Wraps a generic synchronous function in an ES6 Promise. + * @param {Function} fn The function to be wrapped + * @return {Function} The function wrapped in a Promise + */ + promisify: function(fn) { + return function() { + var args = arguments; + return new Promise(function(resolve) { + var result = fn.apply(null, args); + resolve(result); + }); + }; + }, + + /** + * Converts an IE11 web crypro api result to a promise. + * This is required since IE11 implements an old version of the + * Web Crypto specification that does not use promises. + * @param {Object} cryptoOp The return value of an IE11 web cryptro api call + * @param {String} errmsg An error message for a specific operation + * @return {Promise} The resulting Promise + */ + promisifyIE11Op: function(cryptoOp, errmsg) { + return new Promise(function(resolve, reject) { + cryptoOp.onerror = function () { + reject(new Error(errmsg)); + }; + cryptoOp.oncomplete = function (e) { + resolve(e.target.result); + }; + }); } + };