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') {