116f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giropackage org.bouncycastle.crypto.generators;
216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giroimport java.math.BigInteger;
453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
516f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giroimport org.bouncycastle.crypto.AsymmetricCipherKeyPair;
616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giroimport org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
716f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giroimport org.bouncycastle.crypto.KeyGenerationParameters;
816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giroimport org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
916f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giroimport org.bouncycastle.crypto.params.RSAKeyParameters;
1016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giroimport org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
11bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giroimport org.bouncycastle.math.Primes;
1253b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giroimport org.bouncycastle.math.ec.WNafUtil;
1316f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
1416f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro/**
1516f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro * an RSA key pair generator.
1616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro */
1716f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giropublic class RSAKeyPairGenerator
1816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    implements AsymmetricCipherKeyPairGenerator
1916f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro{
2016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    private static final BigInteger ONE = BigInteger.valueOf(1);
2116f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
2216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    private RSAKeyGenerationParameters param;
23bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    private int iterations;
2416f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
2553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro    public void init(KeyGenerationParameters param)
2616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    {
2716f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro        this.param = (RSAKeyGenerationParameters)param;
28bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        this.iterations = getNumberOfIterations(this.param.getStrength(), this.param.getCertainty());
2916f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    }
3016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
3116f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    public AsymmetricCipherKeyPair generateKeyPair()
3216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    {
3353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        AsymmetricCipherKeyPair result = null;
3453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        boolean done = false;
3516f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
36bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        //
37bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        // p and q values should have a length of half the strength in bits
38bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        //
39bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        int strength = param.getStrength();
40bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        int pbitlength = (strength + 1) / 2;
41bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        int qbitlength = strength - pbitlength;
42bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        int mindiffbits = (strength / 2) - 100;
43bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
44bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        if (mindiffbits < strength / 3)
4553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        {
46bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            mindiffbits = strength / 3;
47bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        }
4816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
49bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        int minWeight = strength >> 2;
5016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
51bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        // d lower bound is 2^(strength / 2)
52bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        BigInteger dLowerBound = BigInteger.valueOf(2).pow(strength / 2);
53bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        // squared bound (sqrt(2)*2^(nlen/2-1))^2
54bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        BigInteger squaredBound = ONE.shiftLeft(strength - 1);
55bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        // 2^(nlen/2 - 100)
56bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        BigInteger minDiff = ONE.shiftLeft(mindiffbits);
5716f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
58bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        while (!done)
59bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        {
60bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            BigInteger p, q, n, d, e, pSub1, qSub1, gcd, lcm;
6116f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
62bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            e = param.getPublicExponent();
63bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
64bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            p = chooseRandomPrime(pbitlength, e, squaredBound);
6553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
6653b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            //
6753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            // generate a modulus of the required length
6816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            //
69bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            for (; ; )
7016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            {
71bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                q = chooseRandomPrime(qbitlength, e, squaredBound);
7216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
7353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                // p and q should not be too close together (or equal!)
7453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                BigInteger diff = q.subtract(p).abs();
75bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                if (diff.bitLength() < mindiffbits || diff.compareTo(minDiff) <= 0)
7616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                {
7716f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                    continue;
7816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                }
7953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
8053b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                //
8153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                // calculate the modulus
8253b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                //
8353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                n = p.multiply(q);
8453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
8553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                if (n.bitLength() != strength)
8616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                {
8753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                    //
8853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                    // if we get here our primes aren't big enough, make the largest
8953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                    // of the two p and try again
9053b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                    //
9153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                    p = p.max(q);
9216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                    continue;
9316f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                }
9453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
9553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro	            /*
96bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                 * Require a minimum weight of the NAF representation, since low-weight composites may
9753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro	             * be weak against a version of the number-field-sieve for factoring.
9853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro	             *
9953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro	             * See "The number field sieve for integers of low weight", Oliver Schirokauer.
10053b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro	             */
10153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                if (WNafUtil.getNafWeight(n) < minWeight)
10216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                {
103bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                    p = chooseRandomPrime(pbitlength, e, squaredBound);
10416f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                    continue;
10516f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro                }
10653b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
10753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                break;
10853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            }
10953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
11053b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            if (p.compareTo(q) < 0)
11153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            {
112bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                gcd = p;
11353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                p = q;
114bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                q = gcd;
11516f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            }
11616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
11753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            pSub1 = p.subtract(ONE);
11853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            qSub1 = q.subtract(ONE);
119bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            gcd = pSub1.gcd(qSub1);
120bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            lcm = pSub1.divide(gcd).multiply(qSub1);
12153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro
12216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            //
12353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            // calculate the private exponent
12416f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            //
12553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            d = e.modInverse(lcm);
12616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
127bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            if (d.compareTo(dLowerBound) <= 0)
12816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            {
12953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                continue;
13053b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            }
13153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            else
13253b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            {
13353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                done = true;
13453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            }
13516f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
13616f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            //
13753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            // calculate the CRT factors
13816f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro            //
13953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            BigInteger dP, dQ, qInv;
14016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
14153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            dP = d.remainder(pSub1);
14253b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            dQ = d.remainder(qSub1);
14353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            qInv = q.modInverse(p);
14416f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
14553b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            result = new AsymmetricCipherKeyPair(
14653b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                new RSAKeyParameters(false, n, e),
14753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
14853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        }
14916f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
15053b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        return result;
15153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro    }
15216f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
15353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro    /**
15453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro     * Choose a random prime value for use with RSA
155bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro     *
15653b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro     * @param bitlength the bit-length of the returned prime
157bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro     * @param e         the RSA public exponent
158bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro     * @return A prime p, with (p-1) relatively prime to e
15953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro     */
160bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    protected BigInteger chooseRandomPrime(int bitlength, BigInteger e, BigInteger sqrdBound)
16153b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro    {
162bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        for (int i = 0; i != 5 * bitlength; i++)
16353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        {
16453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            BigInteger p = new BigInteger(bitlength, 1, param.getRandom());
165bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
16653b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            if (p.mod(e).equals(ONE))
16753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            {
16853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                continue;
16953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            }
170bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
171bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            if (p.multiply(p).compareTo(sqrdBound) < 0)
172bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            {
173bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                continue;
174bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            }
175bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
176bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            if (!isProbablePrime(p))
17753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            {
17853b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                continue;
17953b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            }
18016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro
181bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            if (!e.gcd(p.subtract(ONE)).equals(ONE))
18253b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            {
18353b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro                continue;
18453b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            }
185bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
18653b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro            return p;
18753b61f9fe9d58034fcc7021137e92460f91b70ceSergio Giro        }
188bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
189bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        throw new IllegalStateException("unable to generate prime number for RSA key");
190bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    }
191bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
192bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    protected boolean isProbablePrime(BigInteger x)
193bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    {
194bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        /*
195bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro         * Primes class for FIPS 186-4 C.3 primality checking
196bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro         */
197bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        return !Primes.hasAnySmallFactors(x) && Primes.isMRProbablePrime(x, param.getRandom(), iterations);
198bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    }
199bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro
200bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    private static int getNumberOfIterations(int bits, int certainty)
201bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro    {
202bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        /*
203bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro         * NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
204bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro         * certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
205bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro         * are added at the "worst case rate" for the excess.
206bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro         */
207bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        if (bits >= 1536)
208bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        {
209bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            return  certainty <= 100 ? 3
210bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   certainty <= 128 ? 4
211bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   4 + (certainty - 128 + 1) / 2;
212bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        }
213bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        else if (bits >= 1024)
214bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        {
215bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            return  certainty <= 100 ? 4
216bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   certainty <= 112 ? 5
217bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   5 + (certainty - 112 + 1) / 2;
218bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        }
219bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        else if (bits >= 512)
220bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        {
221bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            return  certainty <= 80  ? 5
222bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   certainty <= 100 ? 7
223bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   7 + (certainty - 100 + 1) / 2;
224bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        }
225bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        else
226bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        {
227bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro            return  certainty <= 80  ? 40
228bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro                :   40 + (certainty - 80 + 1) / 2;
229bdb7b3d37025690a0434040b4e0d0623d9fa74afSergio Giro        }
23016f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro    }
23116f9ee464b68937f45d009d9c1b0eb9b544a8deeSergio Giro}
232