// See file LICENSE for more information. library impl.key_generator.rsa_key_generator; import 'package:pointycastle/api.dart'; import 'package:pointycastle/asymmetric/api.dart'; import 'package:pointycastle/key_generators/api.dart'; import 'package:pointycastle/src/registry/registry.dart'; bool _testBit(BigInt i, int n) { return (i & (BigInt.one << n)) != BigInt.zero; } class RSAKeyGenerator implements KeyGenerator { static final FactoryConfig factoryConfig = StaticFactoryConfig(KeyGenerator, 'RSA', () => RSAKeyGenerator()); late SecureRandom _random; late RSAKeyGeneratorParameters _params; @override String get algorithmName => 'RSA'; @override void init(CipherParameters params) { if (params is ParametersWithRandom) { _random = params.random; _params = params.parameters as RSAKeyGeneratorParameters; } else { _random = SecureRandom(); _params = params as RSAKeyGeneratorParameters; } if (_params.bitStrength < 12) { throw ArgumentError('key bit strength cannot be smaller than 12'); } if (!_testBit(_params.publicExponent, 0)) { throw ArgumentError('Public exponent cannot be even'); } } @override AsymmetricKeyPair generateKeyPair() { BigInt p, q, n, e; // p and q values should have a length of half the strength in bits var strength = _params.bitStrength; var pbitlength = (strength + 1) ~/ 2; var qbitlength = strength - pbitlength; var mindiffbits = strength ~/ 3; e = _params.publicExponent; // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes) // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm") // generate p, prime and (p-1) relatively prime to e while (true) { p = generateProbablePrime(pbitlength, 1, _random); if (p % e == BigInt.one) { continue; } if (!_isProbablePrime(p, _params.certainty)) { continue; } if (e.gcd(p - BigInt.one) == BigInt.one) { break; } } // generate a modulus of the required length while (true) { // generate q, prime and (q-1) relatively prime to e, and not equal to p while (true) { q = generateProbablePrime(qbitlength, 1, _random); if ((q - p).abs().bitLength < mindiffbits) { continue; } if (q % e == BigInt.one) { continue; } if (!_isProbablePrime(q, _params.certainty)) { continue; } if (e.gcd(q - BigInt.one) == BigInt.one) { break; } } // calculate the modulus n = p * q; if (n.bitLength == _params.bitStrength) { break; } // if we get here our primes aren't big enough, make the largest of the two p and try again p = (p.compareTo(q) > 0) ? p : q; } // Swap p and q if necessary if (p < q) { var swap = p; p = q; q = swap; } // calculate the private exponent var pSub1 = p - BigInt.one; var qSub1 = q - BigInt.one; var phi = pSub1 * qSub1; var d = e.modInverse(phi); // ignore: deprecated_member_use_from_same_package return AsymmetricKeyPair(RSAPublicKey(n, e), RSAPrivateKey(n, d, p, q, e)); } } /// [List] of low primes final List _lowprimes = [ BigInt.from(2), BigInt.from(3), BigInt.from(5), BigInt.from(7), BigInt.from(11), BigInt.from(13), BigInt.from(17), BigInt.from(19), BigInt.from(23), BigInt.from(29), BigInt.from(31), BigInt.from(37), BigInt.from(41), BigInt.from(43), BigInt.from(47), BigInt.from(53), BigInt.from(59), BigInt.from(61), BigInt.from(67), BigInt.from(71), BigInt.from(73), BigInt.from(79), BigInt.from(83), BigInt.from(89), BigInt.from(97), BigInt.from(101), BigInt.from(103), BigInt.from(107), BigInt.from(109), BigInt.from(113), BigInt.from(127), BigInt.from(131), BigInt.from(137), BigInt.from(139), BigInt.from(149), BigInt.from(151), BigInt.from(157), BigInt.from(163), BigInt.from(167), BigInt.from(173), BigInt.from(179), BigInt.from(181), BigInt.from(191), BigInt.from(193), BigInt.from(197), BigInt.from(199), BigInt.from(211), BigInt.from(223), BigInt.from(227), BigInt.from(229), BigInt.from(233), BigInt.from(239), BigInt.from(241), BigInt.from(251), BigInt.from(257), BigInt.from(263), BigInt.from(269), BigInt.from(271), BigInt.from(277), BigInt.from(281), BigInt.from(283), BigInt.from(293), BigInt.from(307), BigInt.from(311), BigInt.from(313), BigInt.from(317), BigInt.from(331), BigInt.from(337), BigInt.from(347), BigInt.from(349), BigInt.from(353), BigInt.from(359), BigInt.from(367), BigInt.from(373), BigInt.from(379), BigInt.from(383), BigInt.from(389), BigInt.from(397), BigInt.from(401), BigInt.from(409), BigInt.from(419), BigInt.from(421), BigInt.from(431), BigInt.from(433), BigInt.from(439), BigInt.from(443), BigInt.from(449), BigInt.from(457), BigInt.from(461), BigInt.from(463), BigInt.from(467), BigInt.from(479), BigInt.from(487), BigInt.from(491), BigInt.from(499), BigInt.from(503), BigInt.from(509) ]; final BigInt _lplim = (BigInt.one << 26) ~/ _lowprimes.last; final BigInt _bigTwo = BigInt.from(2); /// return index of lowest 1-bit in x, x < 2^31 int _lbit(BigInt x) { // Implementation borrowed from bignum.BigIntegerDartvm. if (x == BigInt.zero) return -1; var r = 0; while ((x & BigInt.from(0xffffffff)) == BigInt.zero) { x >>= 32; r += 32; } if ((x & BigInt.from(0xffff)) == BigInt.zero) { x >>= 16; r += 16; } if ((x & BigInt.from(0xff)) == BigInt.zero) { x >>= 8; r += 8; } if ((x & BigInt.from(0xf)) == BigInt.zero) { x >>= 4; r += 4; } if ((x & BigInt.from(3)) == BigInt.zero) { x >>= 2; r += 2; } if ((x & BigInt.one) == BigInt.zero) ++r; return r; } /// true if probably prime (HAC 4.24, Miller-Rabin) */ bool _millerRabin(BigInt b, int t) { // Implementation borrowed from bignum.BigIntegerDartvm. var n1 = b - BigInt.one; var k = _lbit(n1); if (k <= 0) return false; var r = n1 >> k; t = (t + 1) >> 1; if (t > _lowprimes.length) t = _lowprimes.length; BigInt a; for (var i = 0; i < t; ++i) { a = _lowprimes[i]; var y = a.modPow(r, b); if (y.compareTo(BigInt.one) != 0 && y.compareTo(n1) != 0) { var j = 1; while (j++ < k && y.compareTo(n1) != 0) { y = y.modPow(_bigTwo, b); if (y.compareTo(BigInt.one) == 0) return false; } if (y.compareTo(n1) != 0) return false; } } return true; } /// test primality with certainty >= 1-.5^t */ bool _isProbablePrime(BigInt b, int t) { // Implementation borrowed from bignum.BigIntegerDartvm. int i; var x = b.abs(); if (b <= _lowprimes.last) { for (i = 0; i < _lowprimes.length; ++i) { if (b == _lowprimes[i]) return true; } return false; } if (x.isEven) return false; i = 1; while (i < _lowprimes.length) { var m = _lowprimes[i], j = i + 1; while (j < _lowprimes.length && m < _lplim) { m *= _lowprimes[j++]; } m = x % m; while (i < j) { if (m % _lowprimes[i++] == BigInt.zero) { return false; } } } return _millerRabin(x, t); } BigInt generateProbablePrime(int bitLength, int certainty, SecureRandom rnd) { if (bitLength < 2) { return BigInt.one; } var candidate = rnd.nextBigInteger(bitLength); // force MSB set if (!_testBit(candidate, bitLength - 1)) { candidate |= BigInt.one << (bitLength - 1); } // force odd if (candidate.isEven) { candidate += BigInt.one; } while (!_isProbablePrime(candidate, certainty)) { candidate += _bigTwo; if (candidate.bitLength > bitLength) { candidate -= BigInt.one << (bitLength - 1); } } return candidate; }