1package org.bouncycastle.jcajce.provider.symmetric.util;
2
3import java.security.AlgorithmParameters;
4import java.security.InvalidAlgorithmParameterException;
5import java.security.InvalidKeyException;
6import java.security.Key;
7import java.security.SecureRandom;
8import java.security.spec.AlgorithmParameterSpec;
9
10import javax.crypto.Cipher;
11import javax.crypto.NoSuchPaddingException;
12import javax.crypto.SecretKey;
13import javax.crypto.ShortBufferException;
14import javax.crypto.spec.IvParameterSpec;
15import javax.crypto.spec.PBEParameterSpec;
16// BEGIN android-removed
17// import javax.crypto.spec.RC2ParameterSpec;
18// import javax.crypto.spec.RC5ParameterSpec;
19// END android-removed
20
21import org.bouncycastle.crypto.BlockCipher;
22import org.bouncycastle.crypto.CipherParameters;
23import org.bouncycastle.crypto.DataLengthException;
24import org.bouncycastle.crypto.StreamBlockCipher;
25import org.bouncycastle.crypto.StreamCipher;
26import org.bouncycastle.crypto.params.KeyParameter;
27import org.bouncycastle.crypto.params.ParametersWithIV;
28import org.bouncycastle.jce.provider.BouncyCastleProvider;
29
30public class BaseStreamCipher
31    extends BaseWrapCipher
32    implements PBE
33{
34    //
35    // specs we can handle.
36    //
37    private Class[]                 availableSpecs =
38                                    {
39                                        // BEGIN android-removed
40                                        // RC2ParameterSpec.class,
41                                        // RC5ParameterSpec.class,
42                                        // END android-removed
43                                        IvParameterSpec.class,
44                                        PBEParameterSpec.class
45                                    };
46
47    private StreamCipher       cipher;
48    private ParametersWithIV   ivParam;
49
50    private int                     ivLength = 0;
51
52    private PBEParameterSpec        pbeSpec = null;
53    private String                  pbeAlgorithm = null;
54
55    protected BaseStreamCipher(
56        StreamCipher engine,
57        int ivLength)
58    {
59        cipher = engine;
60        this.ivLength = ivLength;
61    }
62
63    protected BaseStreamCipher(
64        BlockCipher engine,
65        int ivLength)
66    {
67        this.ivLength = ivLength;
68
69        cipher = new StreamBlockCipher(engine);
70    }
71
72    protected int engineGetBlockSize()
73    {
74        return 0;
75    }
76
77    protected byte[] engineGetIV()
78    {
79        return (ivParam != null) ? ivParam.getIV() : null;
80    }
81
82    protected int engineGetKeySize(
83        Key     key)
84    {
85        return key.getEncoded().length * 8;
86    }
87
88    protected int engineGetOutputSize(
89        int     inputLen)
90    {
91        return inputLen;
92    }
93
94    protected AlgorithmParameters engineGetParameters()
95    {
96        if (engineParams == null)
97        {
98            if (pbeSpec != null)
99            {
100                try
101                {
102                    AlgorithmParameters engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
103                    engineParams.init(pbeSpec);
104
105                    return engineParams;
106                }
107                catch (Exception e)
108                {
109                    return null;
110                }
111            }
112        }
113
114        return engineParams;
115    }
116
117    /**
118     * should never be called.
119     */
120    protected void engineSetMode(
121        String  mode)
122    {
123        if (!mode.equalsIgnoreCase("ECB"))
124        {
125            throw new IllegalArgumentException("can't support mode " + mode);
126        }
127    }
128
129    /**
130     * should never be called.
131     */
132    protected void engineSetPadding(
133        String  padding)
134    throws NoSuchPaddingException
135    {
136        if (!padding.equalsIgnoreCase("NoPadding"))
137        {
138            throw new NoSuchPaddingException("Padding " + padding + " unknown.");
139        }
140    }
141
142    protected void engineInit(
143        int                     opmode,
144        Key                     key,
145        AlgorithmParameterSpec  params,
146        SecureRandom            random)
147        throws InvalidKeyException, InvalidAlgorithmParameterException
148    {
149        CipherParameters        param;
150
151        this.pbeSpec = null;
152        this.pbeAlgorithm = null;
153
154        this.engineParams = null;
155
156        //
157        // basic key check
158        //
159        if (!(key instanceof SecretKey))
160        {
161            throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption.");
162        }
163
164        if (key instanceof BCPBEKey)
165        {
166            BCPBEKey k = (BCPBEKey)key;
167
168            if (k.getOID() != null)
169            {
170                pbeAlgorithm = k.getOID().getId();
171            }
172            else
173            {
174                pbeAlgorithm = k.getAlgorithm();
175            }
176
177            if (k.getParam() != null)
178            {
179                param = k.getParam();
180                pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount());
181            }
182            else if (params instanceof PBEParameterSpec)
183            {
184                param = PBE.Util.makePBEParameters(k, params, cipher.getAlgorithmName());
185                pbeSpec = (PBEParameterSpec)params;
186            }
187            else
188            {
189                throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
190            }
191
192            if (k.getIvSize() != 0)
193            {
194                ivParam = (ParametersWithIV)param;
195            }
196        }
197        else if (params == null)
198        {
199            param = new KeyParameter(key.getEncoded());
200        }
201        else if (params instanceof IvParameterSpec)
202        {
203            param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
204            ivParam = (ParametersWithIV)param;
205        }
206        else
207        {
208            throw new IllegalArgumentException("unknown parameter type.");
209        }
210
211        if ((ivLength != 0) && !(param instanceof ParametersWithIV))
212        {
213            SecureRandom    ivRandom = random;
214
215            if (ivRandom == null)
216            {
217                ivRandom = new SecureRandom();
218            }
219
220            if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
221            {
222                byte[]  iv = new byte[ivLength];
223
224                ivRandom.nextBytes(iv);
225                param = new ParametersWithIV(param, iv);
226                ivParam = (ParametersWithIV)param;
227            }
228            else
229            {
230                throw new InvalidAlgorithmParameterException("no IV set when one expected");
231            }
232        }
233
234        switch (opmode)
235        {
236        case Cipher.ENCRYPT_MODE:
237        case Cipher.WRAP_MODE:
238            cipher.init(true, param);
239            break;
240        case Cipher.DECRYPT_MODE:
241        case Cipher.UNWRAP_MODE:
242            cipher.init(false, param);
243            break;
244        default:
245            System.out.println("eeek!");
246        }
247    }
248
249    protected void engineInit(
250        int                 opmode,
251        Key                 key,
252        AlgorithmParameters params,
253        SecureRandom        random)
254        throws InvalidKeyException, InvalidAlgorithmParameterException
255    {
256        AlgorithmParameterSpec  paramSpec = null;
257
258        if (params != null)
259        {
260            for (int i = 0; i != availableSpecs.length; i++)
261            {
262                try
263                {
264                    paramSpec = params.getParameterSpec(availableSpecs[i]);
265                    break;
266                }
267                catch (Exception e)
268                {
269                    continue;
270                }
271            }
272
273            if (paramSpec == null)
274            {
275                throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
276            }
277        }
278
279        engineInit(opmode, key, paramSpec, random);
280        engineParams = params;
281    }
282
283    protected void engineInit(
284        int                 opmode,
285        Key                 key,
286        SecureRandom        random)
287        throws InvalidKeyException
288    {
289        try
290        {
291            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
292        }
293        catch (InvalidAlgorithmParameterException e)
294        {
295            throw new InvalidKeyException(e.getMessage());
296        }
297    }
298
299    protected byte[] engineUpdate(
300        byte[]  input,
301        int     inputOffset,
302        int     inputLen)
303    {
304        byte[]  out = new byte[inputLen];
305
306        cipher.processBytes(input, inputOffset, inputLen, out, 0);
307
308        return out;
309    }
310
311    protected int engineUpdate(
312        byte[]  input,
313        int     inputOffset,
314        int     inputLen,
315        byte[]  output,
316        int     outputOffset)
317        throws ShortBufferException
318    {
319        try
320        {
321        cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
322
323        return inputLen;
324        }
325        catch (DataLengthException e)
326        {
327            throw new ShortBufferException(e.getMessage());
328        }
329    }
330
331    protected byte[] engineDoFinal(
332        byte[]  input,
333        int     inputOffset,
334        int     inputLen)
335    {
336        if (inputLen != 0)
337        {
338            byte[] out = engineUpdate(input, inputOffset, inputLen);
339
340            cipher.reset();
341
342            return out;
343        }
344
345        cipher.reset();
346
347        return new byte[0];
348    }
349
350    protected int engineDoFinal(
351        byte[]  input,
352        int     inputOffset,
353        int     inputLen,
354        byte[]  output,
355        int     outputOffset)
356    {
357        if (inputLen != 0)
358        {
359            cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
360        }
361
362        cipher.reset();
363
364        return inputLen;
365    }
366}
367