2cbac54ba1693f9f0c8a40a005bdc35c2226ca4b
[org.ibex.core.git] / src / org / bouncycastle / crypto / engines / RSAEngine.java
1 package org.bouncycastle.crypto.engines;
2
3 import java.math.BigInteger;
4
5 import org.bouncycastle.crypto.CipherParameters;
6 import org.bouncycastle.crypto.DataLengthException;
7 import org.bouncycastle.crypto.AsymmetricBlockCipher;
8 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
9 import org.bouncycastle.crypto.params.RSAKeyParameters;
10 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
11
12 /**
13  * this does your basic RSA algorithm.
14  */
15 public class RSAEngine
16     implements AsymmetricBlockCipher
17 {
18     private RSAKeyParameters        key;
19     private boolean                 forEncryption;
20
21     /**
22      * initialise the RSA engine.
23      *
24      * @param forEncryption treu if we are encrypting, false otherwise.
25      * @param param the necessary RSA key parameters.
26      */
27     public void init(
28         boolean             forEncryption,
29         CipherParameters    param)
30     {
31         this.key = (RSAKeyParameters)param;
32         this.forEncryption = forEncryption;
33     }
34
35     /**
36      * Return the maximum size for an input block to this engine.
37      * For RSA this is always one byte less than the key size on
38      * encryption, and the same length as the key size on decryption.
39      *
40      * @return maximum size for an input block.
41      */
42     public int getInputBlockSize()
43     {
44         int     bitSize = key.getModulus().bitLength();
45
46         if (forEncryption)
47         {
48             if ((bitSize % 8) == 0)
49             {
50                 return bitSize / 8 - 1;
51             }
52
53             return bitSize / 8;
54         }
55         else
56         {
57             return (bitSize + 7) / 8;
58         }
59     }
60
61     /**
62      * Return the maximum size for an output block to this engine.
63      * For RSA this is always one byte less than the key size on
64      * decryption, and the same length as the key size on encryption.
65      *
66      * @return maximum size for an input block.
67      */
68     public int getOutputBlockSize()
69     {
70         int     bitSize = key.getModulus().bitLength();
71
72         if (forEncryption)
73         {
74             return ((bitSize - 1) + 7) / 8;
75         }
76         else
77         {
78             return (bitSize - 7) / 8;
79         }
80     }
81
82     /**
83      * Process a single block using the basic RSA algorithm.
84      *
85      * @param in the input array.
86      * @param inOff the offset into the input buffer where the data starts.
87      * @param inLen the length of the data to be processed.
88      * @return the result of the RSA process.
89      * @exception DataLengthException the input block is too large.
90      */
91     public byte[] processBlock(
92         byte[]  in,
93         int     inOff,
94         int     inLen)
95     {
96         if (inLen > (getInputBlockSize() + 1))
97         {
98             throw new DataLengthException("input too large for RSA cipher.\n");
99         }
100         else if (inLen == (getInputBlockSize() + 1) && (in[inOff] & 0x80) != 0)
101         {
102             throw new DataLengthException("input too large for RSA cipher.\n");
103         }
104
105         byte[]  block;
106
107         if (inOff != 0 || inLen != in.length)
108         {
109             block = new byte[inLen];
110
111             System.arraycopy(in, inOff, block, 0, inLen);
112         }
113         else
114         {
115             block = in;
116         }
117
118         BigInteger  input = new BigInteger(1, block);
119         byte[]      output;
120
121         if (key instanceof RSAPrivateCrtKeyParameters)
122         {
123             //
124             // we have the extra factors, use the Chinese Remainder Theorem - the author
125             // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for 
126             // advice regarding the expression of this.
127             //
128             RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
129
130             BigInteger d = crtKey.getExponent();
131             BigInteger p = crtKey.getP();
132             BigInteger q = crtKey.getQ();
133             BigInteger dP = crtKey.getDP();
134             BigInteger dQ = crtKey.getDQ();
135             BigInteger qInv = crtKey.getQInv();
136     
137             BigInteger mP, mQ, h, m;
138     
139             // mP = ((input mod p) ^ dP)) mod p
140             mP = (input.remainder(p)).modPow(dP, p);
141     
142             // mQ = ((input mod q) ^ dQ)) mod q
143             mQ = (input.remainder(q)).modPow(dQ, q);
144     
145             // h = qInv * (mP - mQ) mod p
146             h = mP.subtract(mQ);
147             h = h.multiply(qInv);
148             h = h.mod(p);               // mod (in Java) returns the positive residual
149     
150             // m = h * q + mQ
151             m = h.multiply(q);
152             m = m.add(mQ);
153     
154             output = m.toByteArray();
155         }
156         else
157         {
158             output = input.modPow(
159                         key.getExponent(), key.getModulus()).toByteArray();
160         }
161
162         if (forEncryption)
163         {
164             if (output[0] == 0 && output.length > getOutputBlockSize())        // have ended up with an extra zero byte, copy down.
165             {
166                 byte[]  tmp = new byte[output.length - 1];
167
168                 System.arraycopy(output, 1, tmp, 0, tmp.length);
169
170                 return tmp;
171             }
172
173             if (output.length < getOutputBlockSize())     // have ended up with less bytes than normal, lengthen
174             {
175                 byte[]  tmp = new byte[getOutputBlockSize()];
176
177                 System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
178
179                 return tmp;
180             }
181         }
182         else
183         {
184             if (output[0] == 0)        // have ended up with an extra zero byte, copy down.
185             {
186                 byte[]  tmp = new byte[output.length - 1];
187
188                 System.arraycopy(output, 1, tmp, 0, tmp.length);
189
190                 return tmp;
191             }
192         }
193         return output;
194     }
195 }