package org.bouncycastle.crypto.engines;

import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * this does your basic RSA algorithm with blinding
 */
public class RSABlindedEngine
    implements AsymmetricBlockCipher
{
    private static BigInteger ZERO = BigInteger.valueOf(0);

    private RSACoreEngine    core = new RSACoreEngine();
    private RSAKeyParameters key;
    private SecureRandom     random;

    /**
     * initialise the RSA engine.
     *
     * @param forEncryption true if we are encrypting, false otherwise.
     * @param param the necessary RSA key parameters.
     */
    public void init(
        boolean             forEncryption,
        CipherParameters    param)
    {
        core.init(forEncryption, param);

        if (param instanceof ParametersWithRandom)
        {
            ParametersWithRandom    rParam = (ParametersWithRandom)param;

            key = (RSAKeyParameters)rParam.getParameters();
            random = rParam.getRandom();
        }
        else
        {
            key = (RSAKeyParameters)param;
            random = new SecureRandom();
        }
    }

    /**
     * Return the maximum size for an input block to this engine.
     * For RSA this is always one byte less than the key size on
     * encryption, and the same length as the key size on decryption.
     *
     * @return maximum size for an input block.
     */
    public int getInputBlockSize()
    {
        return core.getInputBlockSize();
    }

    /**
     * Return the maximum size for an output block to this engine.
     * For RSA this is always one byte less than the key size on
     * decryption, and the same length as the key size on encryption.
     *
     * @return maximum size for an output block.
     */
    public int getOutputBlockSize()
    {
        return core.getOutputBlockSize();
    }

    /**
     * Process a single block using the basic RSA algorithm.
     *
     * @param in the input array.
     * @param inOff the offset into the input buffer where the data starts.
     * @param inLen the length of the data to be processed.
     * @return the result of the RSA process.
     * @exception DataLengthException the input block is too large.
     */
    public byte[] processBlock(
        byte[]  in,
        int     inOff,
        int     inLen)
    {
        if (key == null)
        {
            throw new IllegalStateException("RSA engine not initialised");
        }

        if (key instanceof RSAPrivateCrtKeyParameters)
        {
            RSAPrivateCrtKeyParameters k = (RSAPrivateCrtKeyParameters)key;

            if (k.getPublicExponent() != null)   // can't do blinding without a public exponent
            {
                BigInteger input = core.convertInput(in, inOff, inLen);
                BigInteger m = k.getModulus();
                BigInteger r = calculateR(m);
                BigInteger result = core.processBlock(r.modPow(k.getPublicExponent(), m).multiply(input).mod(m));

                return core.convertOutput(result.multiply(r.modInverse(m)).mod(m));
            }

            return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen)));
        }
        else
        {
            return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen)));
        }
    }

    /*
     * calculate a random mess-with-their-heads value.
     */
    private BigInteger calculateR(
        BigInteger m)
    {
        int max = m.bitLength() - 1; // must be less than m.bitLength()
        int min = max / 2;
        int length = ((random.nextInt() & 0xff) * ((max - min) / 0xff)) + min;
        BigInteger factor = new BigInteger(length, random);

        while (factor.equals(ZERO))
        {
            factor = new BigInteger(length, random);
        }

        return factor;
    }
}

@@ -371,16 +371,58 @@
 ameters;
+%0Aimport org.bouncycastle.util.BigIntegers;
 %0A%0Aimport
@@ -639,20 +639,19 @@
 Integer 
-ZERO
+ONE
  = BigIn
@@ -664,17 +664,17 @@
 valueOf(
-0
+1
 );%0A%0A    
@@ -2811,32 +2811,124 @@
 d%22);%0A        %7D%0A%0A
+        BigInteger input = core.convertInput(in, inOff, inLen);%0A%0A        BigInteger result;%0A
         if (key 
@@ -3065,20 +3065,31 @@
         
-if (
+BigInteger e = 
 k.getPub
@@ -3101,16 +3101,35 @@
 ponent()
+;%0A            if (e
  != null
@@ -3224,50 +3224,25 @@
 ger 
-input = core.convertInput(in, inOff, inLen
+m = k.getModulus(
 );%0A 
@@ -3271,74 +3271,76 @@
 ger 
-m = k.getModulus();%0A                BigInteger r = calculateR(
+r = BigIntegers.createRandomInRange(ONE, m.subtract(ONE), rando
 m);%0A
+%0A
     
@@ -3366,65 +3366,33 @@
 ger 
-result = core.processBlock(r.modPow(k.getPublicExponent()
+blindedInput = r.modPow(e
 , m)
@@ -3414,19 +3414,17 @@
 ).mod(m)
-);%0A
+;
 %0A       
@@ -3436,194 +3436,77 @@
     
-return core.convertOutput(result.multiply(r.modInverse(m)).mod(m));%0A            %7D%0A%0A            return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen))
+BigInteger blindedResult = core.processBlock(blindedInput
 );%0A
+%0A
         
 %7D%0A  
@@ -3497,34 +3497,32 @@
 );%0A%0A        
-%7D%0A
         
 else%0A       
@@ -3513,115 +3513,40 @@
     
-else%0A        %7B%0A            return core.convertOutput(core.processBlock(core.convertInput(in, inOff, inLen))
+BigInteger rInv = r.modInverse(m
 );%0A 
@@ -3556,149 +3556,75 @@
     
-%7D%0A
     
-%7D%0A%0A
     
-/*%0A     * calculate a random mess-with-their-heads value.%0A     */%0A    private BigInteger calculateR(%0A        BigInteger m)%0A
+result = blindedResult.multiply(rInv).mod(m);%0A        
     
-%7B
+%7D
 %0A   
@@ -3632,335 +3632,173 @@
     
-int max = m.bitLength() - 1; // must be less than m.bitLength()%0A
+    else%0A        
     
+%7B%0A
     
-int min = max / 2;%0A        int length = ((random.nextInt() & 0xff) * ((max - min) / 0xff)) + min;%0A        BigInteger factor = new BigInteger(length, random);%0A%0A        while (factor.equals(ZERO))%0A        %7B%0A            factor = new BigInteger(length, random
+            result = core.processBlock(input);%0A            %7D%0A        %7D%0A        else%0A        %7B%0A            result = core.processBlock(input
 );%0A 
@@ -3826,14 +3826,34 @@
 urn 
-factor
+core.convertOutput(result)
 ;%0A  

Improve selection of random r value for blinding