1 package org.bouncycastle.crypto.encodings;
2 
3 import java.security.SecureRandom;
4 
5 import org.bouncycastle.crypto.AsymmetricBlockCipher;
6 import org.bouncycastle.crypto.CipherParameters;
7 import org.bouncycastle.crypto.Digest;
8 import org.bouncycastle.crypto.InvalidCipherTextException;
9 // BEGIN android-changed
10 import org.bouncycastle.crypto.digests.AndroidDigestFactory;
11 // END android-changed
12 import org.bouncycastle.crypto.params.ParametersWithRandom;
13 
14 /**
15  * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
16  */
17 public class OAEPEncoding
18     implements AsymmetricBlockCipher
19 {
20     private byte[]                  defHash;
21     private Digest                  mgf1Hash;
22 
23     private AsymmetricBlockCipher   engine;
24     private SecureRandom            random;
25     private boolean                 forEncryption;
26 
OAEPEncoding( AsymmetricBlockCipher cipher)27     public OAEPEncoding(
28         AsymmetricBlockCipher   cipher)
29     {
30         // BEGIN android-changed
31         this(cipher, AndroidDigestFactory.getSHA1(), null);
32         // END android-changed
33     }
34 
OAEPEncoding( AsymmetricBlockCipher cipher, Digest hash)35     public OAEPEncoding(
36         AsymmetricBlockCipher       cipher,
37         Digest                      hash)
38     {
39         this(cipher, hash, null);
40     }
41 
OAEPEncoding( AsymmetricBlockCipher cipher, Digest hash, byte[] encodingParams)42     public OAEPEncoding(
43         AsymmetricBlockCipher       cipher,
44         Digest                      hash,
45         byte[]                      encodingParams)
46     {
47         this(cipher, hash, hash, encodingParams);
48     }
49 
OAEPEncoding( AsymmetricBlockCipher cipher, Digest hash, Digest mgf1Hash, byte[] encodingParams)50     public OAEPEncoding(
51         AsymmetricBlockCipher       cipher,
52         Digest                      hash,
53         Digest                      mgf1Hash,
54         byte[]                      encodingParams)
55     {
56         this.engine = cipher;
57         this.mgf1Hash = mgf1Hash;
58         this.defHash = new byte[hash.getDigestSize()];
59 
60         hash.reset();
61 
62         if (encodingParams != null)
63         {
64             hash.update(encodingParams, 0, encodingParams.length);
65         }
66 
67         hash.doFinal(defHash, 0);
68     }
69 
getUnderlyingCipher()70     public AsymmetricBlockCipher getUnderlyingCipher()
71     {
72         return engine;
73     }
74 
init( boolean forEncryption, CipherParameters param)75     public void init(
76         boolean             forEncryption,
77         CipherParameters    param)
78     {
79         if (param instanceof ParametersWithRandom)
80         {
81             ParametersWithRandom  rParam = (ParametersWithRandom)param;
82 
83             this.random = rParam.getRandom();
84         }
85         else
86         {
87             this.random = new SecureRandom();
88         }
89 
90         engine.init(forEncryption, param);
91 
92         this.forEncryption = forEncryption;
93     }
94 
getInputBlockSize()95     public int getInputBlockSize()
96     {
97         int     baseBlockSize = engine.getInputBlockSize();
98 
99         if (forEncryption)
100         {
101             return baseBlockSize - 1 - 2 * defHash.length;
102         }
103         else
104         {
105             return baseBlockSize;
106         }
107     }
108 
getOutputBlockSize()109     public int getOutputBlockSize()
110     {
111         int     baseBlockSize = engine.getOutputBlockSize();
112 
113         if (forEncryption)
114         {
115             return baseBlockSize;
116         }
117         else
118         {
119             return baseBlockSize - 1 - 2 * defHash.length;
120         }
121     }
122 
processBlock( byte[] in, int inOff, int inLen)123     public byte[] processBlock(
124         byte[]  in,
125         int     inOff,
126         int     inLen)
127         throws InvalidCipherTextException
128     {
129         if (forEncryption)
130         {
131             return encodeBlock(in, inOff, inLen);
132         }
133         else
134         {
135             return decodeBlock(in, inOff, inLen);
136         }
137     }
138 
encodeBlock( byte[] in, int inOff, int inLen)139     public byte[] encodeBlock(
140         byte[]  in,
141         int     inOff,
142         int     inLen)
143         throws InvalidCipherTextException
144     {
145         byte[]  block = new byte[getInputBlockSize() + 1 + 2 * defHash.length];
146 
147         //
148         // copy in the message
149         //
150         System.arraycopy(in, inOff, block, block.length - inLen, inLen);
151 
152         //
153         // add sentinel
154         //
155         block[block.length - inLen - 1] = 0x01;
156 
157         //
158         // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0)
159         //
160 
161         //
162         // add the hash of the encoding params.
163         //
164         System.arraycopy(defHash, 0, block, defHash.length, defHash.length);
165 
166         //
167         // generate the seed.
168         //
169         byte[]  seed = new byte[defHash.length];
170 
171         random.nextBytes(seed);
172 
173         //
174         // mask the message block.
175         //
176         byte[]  mask = maskGeneratorFunction1(seed, 0, seed.length, block.length - defHash.length);
177 
178         for (int i = defHash.length; i != block.length; i++)
179         {
180             block[i] ^= mask[i - defHash.length];
181         }
182 
183         //
184         // add in the seed
185         //
186         System.arraycopy(seed, 0, block, 0, defHash.length);
187 
188         //
189         // mask the seed.
190         //
191         mask = maskGeneratorFunction1(
192                         block, defHash.length, block.length - defHash.length, defHash.length);
193 
194         for (int i = 0; i != defHash.length; i++)
195         {
196             block[i] ^= mask[i];
197         }
198 
199         return engine.processBlock(block, 0, block.length);
200     }
201 
202     /**
203      * @exception InvalidCipherTextException if the decrypted block turns out to
204      * be badly formatted.
205      */
decodeBlock( byte[] in, int inOff, int inLen)206     public byte[] decodeBlock(
207         byte[]  in,
208         int     inOff,
209         int     inLen)
210         throws InvalidCipherTextException
211     {
212         byte[]  data = engine.processBlock(in, inOff, inLen);
213         byte[]  block;
214 
215         //
216         // as we may have zeros in our leading bytes for the block we produced
217         // on encryption, we need to make sure our decrypted block comes back
218         // the same size.
219         //
220         if (data.length < engine.getOutputBlockSize())
221         {
222             block = new byte[engine.getOutputBlockSize()];
223 
224             System.arraycopy(data, 0, block, block.length - data.length, data.length);
225         }
226         else
227         {
228             block = data;
229         }
230 
231         if (block.length < (2 * defHash.length) + 1)
232         {
233             throw new InvalidCipherTextException("data too short");
234         }
235 
236         //
237         // unmask the seed.
238         //
239         byte[] mask = maskGeneratorFunction1(
240                     block, defHash.length, block.length - defHash.length, defHash.length);
241 
242         for (int i = 0; i != defHash.length; i++)
243         {
244             block[i] ^= mask[i];
245         }
246 
247         //
248         // unmask the message block.
249         //
250         mask = maskGeneratorFunction1(block, 0, defHash.length, block.length - defHash.length);
251 
252         for (int i = defHash.length; i != block.length; i++)
253         {
254             block[i] ^= mask[i - defHash.length];
255         }
256 
257         //
258         // check the hash of the encoding params.
259         // long check to try to avoid this been a source of a timing attack.
260         //
261         boolean defHashWrong = false;
262 
263         for (int i = 0; i != defHash.length; i++)
264         {
265             if (defHash[i] != block[defHash.length + i])
266             {
267                 defHashWrong = true;
268             }
269         }
270 
271         if (defHashWrong)
272         {
273             throw new InvalidCipherTextException("data hash wrong");
274         }
275 
276         //
277         // find the data block
278         //
279         int start;
280 
281         for (start = 2 * defHash.length; start != block.length; start++)
282         {
283             if (block[start] != 0)
284             {
285                 break;
286             }
287         }
288 
289         if (start >= (block.length - 1) || block[start] != 1)
290         {
291             throw new InvalidCipherTextException("data start wrong " + start);
292         }
293 
294         start++;
295 
296         //
297         // extract the data block
298         //
299         byte[]  output = new byte[block.length - start];
300 
301         System.arraycopy(block, start, output, 0, output.length);
302 
303         return output;
304     }
305 
306     /**
307      * int to octet string.
308      */
ItoOSP( int i, byte[] sp)309     private void ItoOSP(
310         int     i,
311         byte[]  sp)
312     {
313         sp[0] = (byte)(i >>> 24);
314         sp[1] = (byte)(i >>> 16);
315         sp[2] = (byte)(i >>> 8);
316         sp[3] = (byte)(i >>> 0);
317     }
318 
319     /**
320      * mask generator function, as described in PKCS1v2.
321      */
maskGeneratorFunction1( byte[] Z, int zOff, int zLen, int length)322     private byte[] maskGeneratorFunction1(
323         byte[]  Z,
324         int     zOff,
325         int     zLen,
326         int     length)
327     {
328         byte[]  mask = new byte[length];
329         byte[]  hashBuf = new byte[mgf1Hash.getDigestSize()];
330         byte[]  C = new byte[4];
331         int     counter = 0;
332 
333         mgf1Hash.reset();
334 
335         while (counter < (length / hashBuf.length))
336         {
337             ItoOSP(counter, C);
338 
339             mgf1Hash.update(Z, zOff, zLen);
340             mgf1Hash.update(C, 0, C.length);
341             mgf1Hash.doFinal(hashBuf, 0);
342 
343             System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length);
344 
345             counter++;
346         }
347 
348         if ((counter * hashBuf.length) < length)
349         {
350             ItoOSP(counter, C);
351 
352             mgf1Hash.update(Z, zOff, zLen);
353             mgf1Hash.update(C, 0, C.length);
354             mgf1Hash.doFinal(hashBuf, 0);
355 
356             System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length));
357         }
358 
359         return mask;
360     }
361 }
362