diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js
new file mode 100644
index 00000000..75cec019
--- /dev/null
+++ b/src/crypto/public_key/prime.js
@@ -0,0 +1,135 @@
+// OpenPGP.js - An OpenPGP implementation in javascript
+// Copyright (C) 2018 Proton Technologies AG
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3.0 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// Algorithms for probabilistic primality testing
+
+/**
+ * @requires bn.js
+ * @requires crypto/random
+ * @module crypto/public_key/prime
+ */
+
+import BN from 'bn.js';
+import random from '../random';
+
+function randomProbablePrime(b) {
+ let n;
+ const min = new BN(1).shln(b-1);
+ do {
+ n = random.getRandomBN(min, min.shln(1));
+ if (n.isEven()) {
+ n.iaddn(1); // force odd
+ }
+ } while (!isProbablePrime(n));
+// this.dAddOffset(2, 0);
+// if (this.bitLength() > b)
+// this.subTo(BigInteger.ONE.shiftLeft(b - 1), this);
+}
+
+function isProbablePrime(n) {
+ if (!fermat(n)) {
+ return false;
+ }
+ if (!millerRabin(n)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Tests whether n is probably prime or not using Fermat's test with b = 2.
+ * Fails if b^(n-1) mod n === 1.
+ */
+export function fermat(n, b) {
+ b = b || new BN(2);
+ return b.toRed(BN.mont(n)).redPow(n.subn(1)).cmpn(1) === 0;
+}
+
+
+// Miller-Rabin - Miller Rabin algorithm for primality test
+// Copyright Fedor Indutny, 2014.
+//
+// This software is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// Adapted on Jan 2018 from version 4.0.1 at https://github.com/indutny/miller-rabin
+// TODO check this against jsbn's bnpMillerRabin
+// TODO implement fixed base Miller-Rabin; for instance by writing a function that
+// picks a number within the given range from a precomputed list of primes.
+
+/**
+ * Tests whether n is probably prime or not using the Miller-Rabin test.
+ * See HAC Remark 4.28.
+ */
+export function millerRabin(n, k, cb) {
+ var len = n.bitLength();
+ var red = BN.mont(n);
+ var rone = new BN(1).toRed(red);
+
+ if (!k)
+ k = Math.max(1, (len / 48) | 0);
+
+ // Find d and s, (n - 1) = (2 ^ s) * d;
+ var n1 = n.subn(1);
+ for (var s = 0; !n1.testn(s); s++) {}
+ var d = n.shrn(s);
+
+ var rn1 = n1.toRed(red);
+
+ var prime = true;
+ for (; k > 0; k--) {
+ var a = random.getRandomBN(new BN(2), n1);
+ if (cb)
+ cb(a);
+
+ var x = a.toRed(red).redPow(d);
+ if (x.cmp(rone) === 0 || x.cmp(rn1) === 0)
+ continue;
+
+ for (var i = 1; i < s; i++) {
+ x = x.redSqr();
+
+ if (x.cmp(rone) === 0)
+ return false;
+ if (x.cmp(rn1) === 0)
+ break;
+ }
+
+ if (i === s)
+ return false;
+ }
+
+ return prime;
+};
diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js
index 6427b359..2e86b7d5 100644
--- a/src/crypto/public_key/rsa.js
+++ b/src/crypto/public_key/rsa.js
@@ -20,6 +20,7 @@
/**
* @requires bn.js
* @requires asmcrypto.js
+ * @requires crypto/public_key/prime
* @requires crypto/random
* @requires config
* @requires util
@@ -31,6 +32,7 @@ import BN from 'bn.js';
import { RSA } from 'asmcrypto.js/src/rsa/exports-keygen';
import { RSA_RAW } from 'asmcrypto.js/src/rsa/exports-raw';
import { random as asmcrypto_random } from 'asmcrypto.js/src/random/exports';
+import prime from './prime';
import random from '../random';
import config from '../../config';
import util from '../../util';
diff --git a/src/crypto/random.js b/src/crypto/random.js
index a97ca8c8..20e2b039 100644
--- a/src/crypto/random.js
+++ b/src/crypto/random.js
@@ -97,7 +97,7 @@ export default {
},
/**
- * Create a secure random MPI in specified range
+ * Create a secure random MPI that is greater than or equal to min and less than max.
* @param {module:type/mpi} min Lower bound, included
* @param {module:type/mpi} max Upper bound, excluded
* @return {module:BN} Random MPI
@@ -107,18 +107,19 @@ export default {
throw new Error('Illegal parameter value: max <= min');
}
+ let r;
const modulus = max.sub(min);
const length = modulus.byteLength();
- let r = new BN(this.getRandomBytes(length));
+
// Using a while loop is necessary to avoid bias
- while (r.cmp(modulus) >= 0) {
+ do {
r = new BN(this.getRandomBytes(length));
- }
+ } while (r.cmp(modulus) >= 0);
+
return r.iadd(min);
},
randomBuffer: new RandomBuffer()
-
};
/**
diff --git a/src/type/mpi.js b/src/type/mpi.js
index 03c6f517..811b9873 100644
--- a/src/type/mpi.js
+++ b/src/type/mpi.js
@@ -21,8 +21,7 @@
// - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8)
/**
- * Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2})
- *
+ * Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2})
* Multiprecision integers (also called MPIs) are unsigned integers used
* to hold large integers such as the ones used in cryptographic
* calculations.
@@ -67,7 +66,7 @@ MPI.prototype.read = function (bytes, endian='be') {
}
const bits = (bytes[0] << 8) | bytes[1];
- const bytelen = Math.ceil(bits / 8);
+ const bytelen = (bits + 7) >>> 3;
const payload = bytes.subarray(2, 2 + bytelen);
if (endian === 'le') {