OAEPEncoding.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
1package org.bouncycastle.crypto.encodings;
2
3import org.bouncycastle.crypto.AsymmetricBlockCipher;
4import org.bouncycastle.crypto.CipherParameters;
5import org.bouncycastle.crypto.Digest;
6import org.bouncycastle.crypto.InvalidCipherTextException;
7// BEGIN android-changed
8import org.bouncycastle.crypto.digests.AndroidDigestFactory;
9// END android-changed
10import org.bouncycastle.crypto.params.ParametersWithRandom;
11
12import java.security.SecureRandom;
13
14/**
15 * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2.
16 */
17public class OAEPEncoding
18    implements AsymmetricBlockCipher
19{
20    private byte[]                  defHash;
21    private Digest                  hash;
22    private Digest                  mgf1Hash;
23
24    private AsymmetricBlockCipher   engine;
25    private SecureRandom            random;
26    private boolean                 forEncryption;
27
28    public OAEPEncoding(
29        AsymmetricBlockCipher   cipher)
30    {
31        // BEGIN android-changed
32        this(cipher, AndroidDigestFactory.getSHA1(), null);
33        // END android-changed
34    }
35
36    public OAEPEncoding(
37        AsymmetricBlockCipher       cipher,
38        Digest                      hash)
39    {
40        this(cipher, hash, null);
41    }
42
43    public OAEPEncoding(
44        AsymmetricBlockCipher       cipher,
45        Digest                      hash,
46        byte[]                      encodingParams)
47    {
48        this(cipher, hash, hash, encodingParams);
49    }
50
51    public OAEPEncoding(
52        AsymmetricBlockCipher       cipher,
53        Digest                      hash,
54        Digest                      mgf1Hash,
55        byte[]                      encodingParams)
56    {
57        this.engine = cipher;
58        this.hash = hash;
59        this.mgf1Hash = mgf1Hash;
60        this.defHash = new byte[hash.getDigestSize()];
61
62        if (encodingParams != null)
63        {
64            hash.update(encodingParams, 0, encodingParams.length);
65        }
66
67        hash.doFinal(defHash, 0);
68    }
69
70    public AsymmetricBlockCipher getUnderlyingCipher()
71    {
72        return engine;
73    }
74
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
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
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
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
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     */
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        //
260        for (int i = 0; i != defHash.length; i++)
261        {
262            if (defHash[i] != block[defHash.length + i])
263            {
264                throw new InvalidCipherTextException("data hash wrong");
265            }
266        }
267
268        //
269        // find the data block
270        //
271        int start;
272
273        for (start = 2 * defHash.length; start != block.length; start++)
274        {
275            if (block[start] != 0)
276            {
277                break;
278            }
279        }
280
281        if (start >= (block.length - 1) || block[start] != 1)
282        {
283            throw new InvalidCipherTextException("data start wrong " + start);
284        }
285
286        start++;
287
288        //
289        // extract the data block
290        //
291        byte[]  output = new byte[block.length - start];
292
293        System.arraycopy(block, start, output, 0, output.length);
294
295        return output;
296    }
297
298    /**
299     * int to octet string.
300     */
301    private void ItoOSP(
302        int     i,
303        byte[]  sp)
304    {
305        sp[0] = (byte)(i >>> 24);
306        sp[1] = (byte)(i >>> 16);
307        sp[2] = (byte)(i >>> 8);
308        sp[3] = (byte)(i >>> 0);
309    }
310
311    /**
312     * mask generator function, as described in PKCS1v2.
313     */
314    private byte[] maskGeneratorFunction1(
315        byte[]  Z,
316        int     zOff,
317        int     zLen,
318        int     length)
319    {
320        byte[]  mask = new byte[length];
321        byte[]  hashBuf = new byte[mgf1Hash.getDigestSize()];
322        byte[]  C = new byte[4];
323        int     counter = 0;
324
325        hash.reset();
326
327        do
328        {
329            ItoOSP(counter, C);
330
331            mgf1Hash.update(Z, zOff, zLen);
332            mgf1Hash.update(C, 0, C.length);
333            mgf1Hash.doFinal(hashBuf, 0);
334
335            System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length);
336        }
337        while (++counter < (length / hashBuf.length));
338
339        if ((counter * hashBuf.length) < length)
340        {
341            ItoOSP(counter, C);
342
343            mgf1Hash.update(Z, zOff, zLen);
344            mgf1Hash.update(C, 0, C.length);
345            mgf1Hash.doFinal(hashBuf, 0);
346
347            System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length));
348        }
349
350        return mask;
351    }
352}
353