Cleanup RSA and util code

This commit is contained in:
Tankred Hase 2016-02-03 13:52:18 +07:00
parent 55bd9757e7
commit 4d325ca65c
3 changed files with 78 additions and 36 deletions

View File

@ -29,6 +29,9 @@
"before": true, "before": true,
"beforeEach": true, "beforeEach": true,
"after": true, "after": true,
"afterEach": true "afterEach": true,
"escape": true,
"unescape": true,
"asmCrypto": true
} }
} }

View File

@ -24,6 +24,8 @@
* @module crypto/public_key/rsa * @module crypto/public_key/rsa
*/ */
'use strict';
var BigInteger = require('./jsbn.js'), var BigInteger = require('./jsbn.js'),
util = require('../../util.js'), util = require('../../util.js'),
random = require('../random.js'), random = require('../random.js'),
@ -119,7 +121,7 @@ function RSA() {
// "empty" RSA key constructor // "empty" RSA key constructor
function keyObject() { function KeyObject() {
this.n = null; this.n = null;
this.e = 0; this.e = 0;
this.ee = null; this.ee = null;
@ -167,8 +169,8 @@ function RSA() {
}; };
keys = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); keys = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
if (!(typeof keys.then === 'function')) { // IE11 KeyOperation if (typeof keys.then !== 'function') { // IE11 KeyOperation
keys = convertKeyOperation(keys, 'Error generating RSA key pair.'); keys = util.promisifyIE11Op(keys, 'Error generating RSA key pair.');
} }
} }
@ -185,15 +187,15 @@ function RSA() {
// export the generated keys as JsonWebKey (JWK) // export the generated keys as JsonWebKey (JWK)
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
var key = webCrypto.exportKey('jwk', keypair.privateKey); var key = webCrypto.exportKey('jwk', keypair.privateKey);
if (!(typeof key.then === 'function')) { // IE11 KeyOperation if (typeof key.then !== 'function') { // IE11 KeyOperation
key = convertKeyOperation(key, 'Error exporting RSA key pair.'); key = util.promisifyIE11Op(key, 'Error exporting RSA key pair.');
} }
return key; return key;
} }
function decodeKey(jwk) { function decodeKey(jwk) {
// map JWK parameters to local BigInteger type system // map JWK parameters to local BigInteger type system
var key = new keyObject(); var key = new KeyObject();
key.n = toBigInteger(jwk.n); key.n = toBigInteger(jwk.n);
key.ee = new BigInteger(E, 16); key.ee = new BigInteger(E, 16);
key.d = toBigInteger(jwk.d); key.d = toBigInteger(jwk.d);
@ -210,23 +212,12 @@ function RSA() {
return key; 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 // JS code
// //
return new Promise(function(resolve) { return new Promise(function(resolve) {
var key = new keyObject(); var key = new KeyObject();
var rng = new SecureRandom(); var rng = new SecureRandom();
var qs = B >> 1; var qs = B >> 1;
key.e = parseInt(E, 16); key.e = parseInt(E, 16);
@ -235,14 +226,16 @@ function RSA() {
for (;;) { for (;;) {
for (;;) { for (;;) {
key.p = new BigInteger(B - qs, 1, rng); 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; break;
} }
}
for (;;) { for (;;) {
key.q = new BigInteger(qs, 1, rng); 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; break;
} }
}
if (key.p.compareTo(key.q) <= 0) { if (key.p.compareTo(key.q) <= 0) {
var t = key.p; var t = key.p;
key.p = key.q; key.p = key.q;
@ -270,7 +263,7 @@ function RSA() {
this.verify = verify; this.verify = verify;
this.sign = sign; this.sign = sign;
this.generate = generate; this.generate = generate;
this.keyObject = keyObject; this.keyObject = KeyObject;
} }
module.exports = RSA; module.exports = RSA;

View File

@ -69,12 +69,15 @@ module.exports = {
var i = 0; var i = 0;
while (c < e) { while (c < e) {
h = str.charCodeAt(c++).toString(16); h = str.charCodeAt(c++).toString(16);
while (h.length < 2) h = "0" + h; while (h.length < 2) {
h = "0" + h;
}
r.push(" " + h); r.push(" " + h);
i++; i++;
if (i % 32 === 0) if (i % 32 === 0) {
r.push("\n "); r.push("\n ");
} }
}
return r.join(''); return r.join('');
}, },
@ -84,15 +87,18 @@ module.exports = {
* @return {String} String containing the hexadecimal values * @return {String} String containing the hexadecimal values
*/ */
hexstrdump: function (str) { hexstrdump: function (str) {
if (str === null) if (str === null) {
return ""; return "";
}
var r = []; var r = [];
var e = str.length; var e = str.length;
var c = 0; var c = 0;
var h; var h;
while (c < e) { while (c < e) {
h = str.charCodeAt(c++).toString(16); h = str.charCodeAt(c++).toString(16);
while (h.length < 2) h = "0" + h; while (h.length < 2) {
h = "0" + h;
}
r.push("" + h); r.push("" + h);
} }
return r.join(''); return r.join('');
@ -105,8 +111,9 @@ module.exports = {
*/ */
hex2bin: function (hex) { hex2bin: function (hex) {
var str = ''; 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)); str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return str; return str;
}, },
@ -122,7 +129,9 @@ module.exports = {
var h; var h;
while (c < e) { while (c < e) {
h = str[c++].toString(16); h = str[c++].toString(16);
while (h.length < 2) h = "0" + h; while (h.length < 2) {
h = "0" + h;
}
r.push("" + h); r.push("" + h);
} }
return r.join(''); return r.join('');
@ -189,7 +198,7 @@ module.exports = {
str2Uint8Array: function (str) { str2Uint8Array: function (str) {
// Uncomment for debugging // 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'); throw new Error('str2Uint8Array: Data must be in the form of a string');
} }
@ -342,8 +351,9 @@ module.exports = {
getLeftNBits: function (string, bitcount) { getLeftNBits: function (string, bitcount) {
var rest = bitcount % 8; var rest = bitcount % 8;
if (rest === 0) if (rest === 0) {
return string.substring(0, bitcount / 8); return string.substring(0, bitcount / 8);
}
var bytes = (bitcount - rest) / 8 + 1; var bytes = (bitcount - rest) / 8 + 1;
var result = string.substring(0, bytes); var result = string.substring(0, bytes);
return this.shiftRight(result, 8 - rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF); 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. * @return {String} Resulting string.
*/ */
shiftRight: function (value, bitcount) { shiftRight: function (value, bitcount) {
var temp = util.str2bin(value); var temp = this.str2bin(value);
if (bitcount % 8 !== 0) { if (bitcount % 8 !== 0) {
for (var i = temp.length - 1; i >= 0; i--) { for (var i = temp.length - 1; i >= 0; i--) {
temp[i] >>= bitcount % 8; temp[i] >>= bitcount % 8;
if (i > 0) if (i > 0) {
temp[i] |= (temp[i - 1] << (8 - (bitcount % 8))) & 0xFF; temp[i] |= (temp[i - 1] << (8 - (bitcount % 8))) & 0xFF;
} }
}
} else { } else {
return value; return value;
} }
return util.bin2str(temp); return this.bin2str(temp);
}, },
/** /**
@ -413,5 +424,40 @@ module.exports = {
return window.msCrypto.subtle; 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);
};
});
}
}; };