millerRabin set to 40 iterations; doc fixes

This commit is contained in:
Mahrud Sayrafi 2018-02-25 02:38:51 -05:00
parent 7a3a75a7df
commit 2e95335825
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
4 changed files with 53 additions and 32 deletions

View File

@ -280,7 +280,7 @@ export default {
return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]);
}); });
default: default:
throw new Error('Invalid public key encryption algorithm.'); throw new Error('Invalid public key algorithm.');
} }
}, },

View File

@ -15,7 +15,7 @@
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// Algorithms for probabilistic prime generation // Algorithms for probabilistic random prime generation
/** /**
* @requires bn.js * @requires bn.js
@ -30,7 +30,14 @@ export default {
randomProbablePrime, isProbablePrime, fermat, millerRabin randomProbablePrime, isProbablePrime, fermat, millerRabin
}; };
function randomProbablePrime(bits, e) { /**
* Probabilistic random number generator
* @param {Integer} bits Bit length of the prime
* @param {BN} e Optional RSA exponent to check against the prime
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @return BN
*/
function randomProbablePrime(bits, e, k) {
const min = new BN(1).shln(bits - 1); const min = new BN(1).shln(bits - 1);
let n = random.getRandomBN(min, min.shln(1)); let n = random.getRandomBN(min, min.shln(1));
@ -38,7 +45,7 @@ function randomProbablePrime(bits, e) {
n.iaddn(1); // force odd n.iaddn(1); // force odd
} }
while (!isProbablePrime(n, e)) { while (!isProbablePrime(n, e, k)) {
n.iaddn(2); n.iaddn(2);
// If reached the maximum, go back to the minimum. // If reached the maximum, go back to the minimum.
if (n.bitLength() > bits) { if (n.bitLength() > bits) {
@ -48,14 +55,21 @@ function randomProbablePrime(bits, e) {
return n; return n;
} }
function isProbablePrime(n, e) { /**
* Probabilistic primality testing
* @param {BN} n Number to test
* @param {BN} e Optional RSA exponent to check against the prime
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @return {boolean}
*/
function isProbablePrime(n, e, k) {
if (e && !n.subn(1).gcd(e).eqn(1)) { if (e && !n.subn(1).gcd(e).eqn(1)) {
return false; return false;
} }
if (!fermat(n)) { if (!fermat(n)) {
return false; return false;
} }
if (!millerRabin(n)) { if (!millerRabin(n, k)) {
return false; return false;
} }
return true; return true;
@ -64,6 +78,9 @@ function isProbablePrime(n, e) {
/** /**
* Tests whether n is probably prime or not using Fermat's test with b = 2. * Tests whether n is probably prime or not using Fermat's test with b = 2.
* Fails if b^(n-1) mod n === 1. * Fails if b^(n-1) mod n === 1.
* @param {BN} n Number to test
* @param {Integer} b Optional Fermat test base
* @return {boolean}
*/ */
function fermat(n, b) { function fermat(n, b) {
b = b || new BN(2); b = b || new BN(2);
@ -103,33 +120,38 @@ function fermat(n, b) {
/** /**
* Tests whether n is probably prime or not using the Miller-Rabin test. * Tests whether n is probably prime or not using the Miller-Rabin test.
* See HAC Remark 4.28. * See HAC Remark 4.28.
* @param {BN} n Number to test
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @param {Function} cb Optional callback function to call with random witnesses
* @return {boolean}
*/ */
function millerRabin(n, k, cb) { function millerRabin(n, k, cb) {
var len = n.bitLength(); const len = n.bitLength();
var red = BN.mont(n); const red = BN.mont(n);
var rone = new BN(1).toRed(red); const rone = new BN(1).toRed(red);
if (!k) if (!k)
k = Math.max(1, (len / 48) | 0); k = Math.max(1, (len / 48) | 0);
// Find d and s, (n - 1) = (2 ^ s) * d; // Find d and s, (n - 1) = (2 ^ s) * d;
var n1 = n.subn(1); const n1 = n.subn(1);
for (var s = 0; !n1.testn(s); s++) {} let s = 0;
var d = n.shrn(s); while (!n1.testn(s)) { s++; }
const d = n.shrn(s);
var rn1 = n1.toRed(red); const rn1 = n1.toRed(red);
var prime = true;
for (; k > 0; k--) { for (; k > 0; k--) {
var a = random.getRandomBN(new BN(2), n1); let a = random.getRandomBN(new BN(2), n1);
if (cb) if (cb)
cb(a); cb(a);
var x = a.toRed(red).redPow(d); let x = a.toRed(red).redPow(d);
if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) if (x.cmp(rone) === 0 || x.cmp(rn1) === 0)
continue; continue;
for (var i = 1; i < s; i++) { let i;
for (i = 1; i < s; i++) {
x = x.redSqr(); x = x.redSqr();
if (x.cmp(rone) === 0) if (x.cmp(rone) === 0)
@ -142,5 +164,5 @@ function millerRabin(n, k, cb) {
return false; return false;
} }
return prime; return true;
}; };

View File

@ -19,7 +19,6 @@
/** /**
* @requires bn.js * @requires bn.js
* @requires asmcrypto.js
* @requires crypto/public_key/prime * @requires crypto/public_key/prime
* @requires crypto/random * @requires crypto/random
* @requires config * @requires config
@ -135,7 +134,10 @@ export default {
/** /**
* Generate a new random private key B bits long with public exponent E * Generate a new random private key B bits long with public exponent E
* @param {Integer} B RSA bit length * @param {Integer} B RSA bit length
* @param {String} E RSA public exponent in hex * @param {String} E RSA public exponent in hex string
* @return {{n: BN, e: BN, d: BN,
p: BN, q: BN, u: BN}} RSA public modulus, RSA public exponent, RSA private exponent,
RSA private prime p, RSA private prime q, u = q ** -1 mod p
*/ */
generate: async function(B, E) { generate: async function(B, E) {
let key; let key;
@ -193,13 +195,13 @@ export default {
} }
while (true) { while (true) {
let p = prime.randomProbablePrime(B - (B >> 1), E); // 40 iterations of the Miller-Rabin test
let q = prime.randomProbablePrime(B >> 1, E); // See https://stackoverflow.com/a/6330138 for justification
let p = prime.randomProbablePrime(B - (B >> 1), E, 40);
let q = prime.randomProbablePrime(B >> 1, E, 40);
if (p.cmp(q) < 0) { if (p.cmp(q) < 0) {
const t = p; [p, q] = [q, p];
p = q;
q = t;
} }
const phi = p.subn(1).mul(q.subn(1)); const phi = p.subn(1).mul(q.subn(1));
@ -214,5 +216,7 @@ export default {
u: p.invm(q) u: p.invm(q)
}; };
} }
} },
prime: prime
}; };

View File

@ -1,5 +1,4 @@
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
const AES_CFB = require('asmcrypto.js/asmcrypto.all.js').AES_CFB;
const chai = require('chai'); const chai = require('chai');
chai.use(require('chai-as-promised')); chai.use(require('chai-as-promised'));
@ -297,11 +296,7 @@ describe('API functional testing', function() {
const prefix = util.concatUint8Array([rndm, repeat]); const prefix = util.concatUint8Array([rndm, repeat]);
const symmencData = crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false); const symmencData = crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false);
const symmencData2 = AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey); const decrypted = crypto.cfb.decrypt(algo, symmKey, symmencData, false);
let decrypted = AES_CFB.decrypt(symmencData, symmKey);
decrypted = decrypted.subarray(crypto.cipher[algo].blockSize + 2, decrypted.length);
expect(util.Uint8Array2str(symmencData)).to.equal(util.Uint8Array2str(symmencData2));
const text = util.Uint8Array2str(decrypted); const text = util.Uint8Array2str(decrypted);
expect(text).to.equal(plaintext); expect(text).to.equal(plaintext);