1package org.bouncycastle.jce.provider;
2
3import java.security.AlgorithmParameters;
4import java.security.InvalidAlgorithmParameterException;
5import java.security.InvalidKeyException;
6import java.security.InvalidParameterException;
7import java.security.Key;
8import java.security.NoSuchAlgorithmException;
9import java.security.SecureRandom;
10import java.security.spec.AlgorithmParameterSpec;
11
12import javax.crypto.BadPaddingException;
13import javax.crypto.Cipher;
14import javax.crypto.CipherSpi;
15import javax.crypto.IllegalBlockSizeException;
16import javax.crypto.NoSuchPaddingException;
17import javax.crypto.SecretKey;
18import javax.crypto.ShortBufferException;
19import javax.crypto.spec.IvParameterSpec;
20import javax.crypto.spec.PBEParameterSpec;
21// BEGIN android-removed
22// import javax.crypto.spec.RC2ParameterSpec;
23// import javax.crypto.spec.RC5ParameterSpec;
24// END android-removed
25
26import org.bouncycastle.crypto.BlockCipher;
27import org.bouncycastle.crypto.BufferedBlockCipher;
28import org.bouncycastle.crypto.CipherParameters;
29import org.bouncycastle.crypto.DataLengthException;
30import org.bouncycastle.crypto.InvalidCipherTextException;
31import org.bouncycastle.crypto.engines.AESFastEngine;
32import org.bouncycastle.crypto.engines.DESEngine;
33// BEGIN android-removed
34// import org.bouncycastle.crypto.engines.GOST28147Engine;
35// END android-removed
36import org.bouncycastle.crypto.engines.RC2Engine;
37import org.bouncycastle.crypto.engines.TwofishEngine;
38import org.bouncycastle.crypto.modes.AEADBlockCipher;
39import org.bouncycastle.crypto.modes.CBCBlockCipher;
40import org.bouncycastle.crypto.modes.CCMBlockCipher;
41import org.bouncycastle.crypto.modes.CFBBlockCipher;
42import org.bouncycastle.crypto.modes.CTSBlockCipher;
43// BEGIN android-removed
44// import org.bouncycastle.crypto.modes.EAXBlockCipher;
45// END android-removed
46import org.bouncycastle.crypto.modes.GCMBlockCipher;
47// BEGIN android-removed
48// import org.bouncycastle.crypto.modes.GOFBBlockCipher;
49// END android-removed
50import org.bouncycastle.crypto.modes.OFBBlockCipher;
51// BEGIN android-removed
52// import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
53// import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
54// END android-removed
55import org.bouncycastle.crypto.modes.SICBlockCipher;
56import org.bouncycastle.crypto.paddings.BlockCipherPadding;
57import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
58import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
59import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
60import org.bouncycastle.crypto.paddings.TBCPadding;
61import org.bouncycastle.crypto.paddings.X923Padding;
62import org.bouncycastle.crypto.paddings.ZeroBytePadding;
63import org.bouncycastle.crypto.params.KeyParameter;
64import org.bouncycastle.crypto.params.ParametersWithIV;
65import org.bouncycastle.crypto.params.ParametersWithRandom;
66// BEGIN android-removed
67// import org.bouncycastle.crypto.params.ParametersWithSBox;
68// import org.bouncycastle.crypto.params.RC2Parameters;
69// import org.bouncycastle.crypto.params.RC5Parameters;
70// END android-removed
71import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
72import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
73// BEGIN android-removed
74// import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
75// END android-removed
76import org.bouncycastle.jce.spec.RepeatedSecretKeySpec;
77import org.bouncycastle.util.Strings;
78
79public class JCEBlockCipher
80    extends CipherSpi
81    implements PBE
82{
83    //
84    // specs we can handle.
85    //
86    private Class[]                 availableSpecs =
87                                    {
88                                        // BEGIN android-removed
89                                        // RC2ParameterSpec.class,
90                                        // RC5ParameterSpec.class,
91                                        // END android-removed
92                                        IvParameterSpec.class,
93                                        PBEParameterSpec.class,
94                                        // BEGIN android-removed
95                                        // GOST28147ParameterSpec.class
96                                        // END android-removed
97                                    };
98
99    private BlockCipher             baseEngine;
100    private GenericBlockCipher      cipher;
101    private ParametersWithIV        ivParam;
102
103    private int                     ivLength = 0;
104
105    private boolean                 padded;
106
107    private PBEParameterSpec        pbeSpec = null;
108    private String                  pbeAlgorithm = null;
109
110    private String                  modeName = null;
111
112    private AlgorithmParameters engineParams;
113
114    protected JCEBlockCipher(
115        BlockCipher engine)
116    {
117        baseEngine = engine;
118
119        cipher = new BufferedGenericBlockCipher(engine);
120    }
121
122    protected JCEBlockCipher(
123        BlockCipher engine,
124        int         ivLength)
125    {
126        baseEngine = engine;
127
128        this.cipher = new BufferedGenericBlockCipher(engine);
129        this.ivLength = ivLength / 8;
130    }
131
132    protected JCEBlockCipher(
133        BufferedBlockCipher engine,
134        int                 ivLength)
135    {
136        baseEngine = engine.getUnderlyingCipher();
137
138        this.cipher = new BufferedGenericBlockCipher(engine);
139        this.ivLength = ivLength / 8;
140    }
141
142    protected int engineGetBlockSize()
143    {
144        return baseEngine.getBlockSize();
145    }
146
147    protected byte[] engineGetIV()
148    {
149        return (ivParam != null) ? ivParam.getIV() : null;
150    }
151
152    protected int engineGetKeySize(
153        Key     key)
154    {
155        return key.getEncoded().length * 8;
156    }
157
158    protected int engineGetOutputSize(
159        int     inputLen)
160    {
161        return cipher.getOutputSize(inputLen);
162    }
163
164    protected AlgorithmParameters engineGetParameters()
165    {
166        if (engineParams == null)
167        {
168            if (pbeSpec != null)
169            {
170                try
171                {
172                    engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
173                    engineParams.init(pbeSpec);
174                }
175                catch (Exception e)
176                {
177                    return null;
178                }
179            }
180            else if (ivParam != null)
181            {
182                String  name = cipher.getUnderlyingCipher().getAlgorithmName();
183
184                if (name.indexOf('/') >= 0)
185                {
186                    name = name.substring(0, name.indexOf('/'));
187                }
188
189                try
190                {
191                    engineParams = AlgorithmParameters.getInstance(name, BouncyCastleProvider.PROVIDER_NAME);
192                    engineParams.init(ivParam.getIV());
193                }
194                catch (Exception e)
195                {
196                    throw new RuntimeException(e.toString());
197                }
198            }
199        }
200
201        return engineParams;
202    }
203
204    protected void engineSetMode(
205        String  mode)
206        throws NoSuchAlgorithmException
207    {
208        modeName = Strings.toUpperCase(mode);
209
210        if (modeName.equals("ECB"))
211        {
212            ivLength = 0;
213            cipher = new BufferedGenericBlockCipher(baseEngine);
214        }
215        else if (modeName.equals("CBC"))
216        {
217            ivLength = baseEngine.getBlockSize();
218            cipher = new BufferedGenericBlockCipher(
219                            new CBCBlockCipher(baseEngine));
220        }
221        else if (modeName.startsWith("OFB"))
222        {
223            ivLength = baseEngine.getBlockSize();
224            if (modeName.length() != 3)
225            {
226                int wordSize = Integer.parseInt(modeName.substring(3));
227
228                cipher = new BufferedGenericBlockCipher(
229                                new OFBBlockCipher(baseEngine, wordSize));
230            }
231            else
232            {
233                cipher = new BufferedGenericBlockCipher(
234                        new OFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
235            }
236        }
237        else if (modeName.startsWith("CFB"))
238        {
239            ivLength = baseEngine.getBlockSize();
240            if (modeName.length() != 3)
241            {
242                int wordSize = Integer.parseInt(modeName.substring(3));
243
244                cipher = new BufferedGenericBlockCipher(
245                                new CFBBlockCipher(baseEngine, wordSize));
246            }
247            else
248            {
249                cipher = new BufferedGenericBlockCipher(
250                        new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize()));
251            }
252        }
253        // BEGIN android-removed
254        // else if (modeName.startsWith("PGP"))
255        // {
256        //     boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV");
257        //
258        //     ivLength = baseEngine.getBlockSize();
259        //     cipher = new BufferedGenericBlockCipher(
260        //         new PGPCFBBlockCipher(baseEngine, inlineIV));
261        // }
262        // else if (modeName.equalsIgnoreCase("OpenPGPCFB"))
263        // {
264        //     ivLength = 0;
265        //     cipher = new BufferedGenericBlockCipher(
266        //         new OpenPGPCFBBlockCipher(baseEngine));
267        // }
268        // END android-removed
269        else if (modeName.startsWith("SIC"))
270        {
271            ivLength = baseEngine.getBlockSize();
272            if (ivLength < 16)
273            {
274                throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)");
275            }
276            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
277                        new SICBlockCipher(baseEngine)));
278        }
279        else if (modeName.startsWith("CTR"))
280        {
281            ivLength = baseEngine.getBlockSize();
282            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
283                        new SICBlockCipher(baseEngine)));
284        }
285        // BEGIN android-removed
286        // else if (modeName.startsWith("GOFB"))
287        // {
288        //     ivLength = baseEngine.getBlockSize();
289        //     cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
290        //                 new GOFBBlockCipher(baseEngine)));
291        // }
292        // END android-removed
293        else if (modeName.startsWith("CTS"))
294        {
295            ivLength = baseEngine.getBlockSize();
296            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine)));
297        }
298        else if (modeName.startsWith("CCM"))
299        {
300            ivLength = baseEngine.getBlockSize();
301            cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
302        }
303        // BEGIN android-removed
304        // else if (modeName.startsWith("EAX"))
305        // {
306        //     ivLength = baseEngine.getBlockSize();
307        //     cipher = new AEADGenericBlockCipher(new EAXBlockCipher(baseEngine));
308        // }
309        // END android-removed
310        else if (modeName.startsWith("GCM"))
311        {
312            ivLength = baseEngine.getBlockSize();
313            cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine));
314        }
315        else
316        {
317            throw new NoSuchAlgorithmException("can't support mode " + mode);
318        }
319    }
320
321    protected void engineSetPadding(
322        String  padding)
323    throws NoSuchPaddingException
324    {
325        String  paddingName = Strings.toUpperCase(padding);
326
327        if (paddingName.equals("NOPADDING"))
328        {
329            if (cipher.wrapOnNoPadding())
330            {
331                cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher()));
332            }
333        }
334        else if (paddingName.equals("WITHCTS"))
335        {
336            cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher()));
337        }
338        else
339        {
340            padded = true;
341
342            if (isAEADModeName(modeName))
343            {
344                throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes.");
345            }
346            else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING"))
347            {
348                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher());
349            }
350            else if (paddingName.equals("ZEROBYTEPADDING"))
351            {
352                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding());
353            }
354            else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING"))
355            {
356                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding());
357            }
358            else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING"))
359            {
360                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new X923Padding());
361            }
362            else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING"))
363            {
364                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding());
365            }
366            else if (paddingName.equals("TBCPADDING"))
367            {
368                cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding());
369            }
370            else
371            {
372                throw new NoSuchPaddingException("Padding " + padding + " unknown.");
373            }
374        }
375    }
376
377    protected void engineInit(
378        int                     opmode,
379        Key                     key,
380        AlgorithmParameterSpec  params,
381        SecureRandom            random)
382        throws InvalidKeyException, InvalidAlgorithmParameterException
383    {
384        CipherParameters        param;
385
386        this.pbeSpec = null;
387        this.pbeAlgorithm = null;
388        this.engineParams = null;
389
390        //
391        // basic key check
392        //
393        if (!(key instanceof SecretKey))
394        {
395            throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption.");
396        }
397
398        // BEGIN android-removed
399        // //
400        // // for RC5-64 we must have some default parameters
401        // //
402        // if (params == null && baseEngine.getAlgorithmName().startsWith("RC5-64"))
403        // {
404        //     throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in.");
405        // }
406        // END android-removed
407
408        //
409        // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it).
410        //
411        if (key instanceof BCPBEKey)
412        {
413            BCPBEKey k = (BCPBEKey)key;
414
415            if (k.getOID() != null)
416            {
417                pbeAlgorithm = k.getOID().getId();
418            }
419            else
420            {
421                pbeAlgorithm = k.getAlgorithm();
422            }
423
424            if (k.getParam() != null)
425            {
426                param = k.getParam();
427                pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount());
428            }
429            else if (params instanceof PBEParameterSpec)
430            {
431                pbeSpec = (PBEParameterSpec)params;
432                param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName());
433            }
434            else
435            {
436                throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set.");
437            }
438
439            if (param instanceof ParametersWithIV)
440            {
441                ivParam = (ParametersWithIV)param;
442            }
443        }
444        else if (params == null)
445        {
446            param = new KeyParameter(key.getEncoded());
447        }
448        else if (params instanceof IvParameterSpec)
449        {
450            if (ivLength != 0)
451            {
452                IvParameterSpec p = (IvParameterSpec)params;
453
454                if (p.getIV().length != ivLength && !isAEADModeName(modeName))
455                {
456                    throw new InvalidAlgorithmParameterException("IV must be " + ivLength + " bytes long.");
457                }
458
459                if (key instanceof RepeatedSecretKeySpec)
460                {
461                    param = new ParametersWithIV(null, p.getIV());
462                    ivParam = (ParametersWithIV)param;
463                }
464                else
465                {
466                    param = new ParametersWithIV(new KeyParameter(key.getEncoded()), p.getIV());
467                    ivParam = (ParametersWithIV)param;
468                }
469            }
470            else
471            {
472                if (modeName != null && modeName.equals("ECB"))
473                {
474                    throw new InvalidAlgorithmParameterException("ECB mode does not use an IV");
475                }
476
477                param = new KeyParameter(key.getEncoded());
478            }
479        }
480        // BEGIN android-removed
481        // else if (params instanceof GOST28147ParameterSpec)
482        // {
483        //     GOST28147ParameterSpec    gost28147Param = (GOST28147ParameterSpec)params;
484        //
485        //     param = new ParametersWithSBox(
486        //                new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox());
487        //
488        //     if (gost28147Param.getIV() != null && ivLength != 0)
489        //     {
490        //         param = new ParametersWithIV(param, gost28147Param.getIV());
491        //         ivParam = (ParametersWithIV)param;
492        //     }
493        // }
494        // else if (params instanceof RC2ParameterSpec)
495        // {
496        //     RC2ParameterSpec    rc2Param = (RC2ParameterSpec)params;
497        //
498        //     param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits());
499        //
500        //     if (rc2Param.getIV() != null && ivLength != 0)
501        //     {
502        //         param = new ParametersWithIV(param, rc2Param.getIV());
503        //         ivParam = (ParametersWithIV)param;
504        //     }
505        // }
506        // else if (params instanceof RC5ParameterSpec)
507        // {
508        //     RC5ParameterSpec    rc5Param = (RC5ParameterSpec)params;
509        //
510        //     param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds());
511        //     if (baseEngine.getAlgorithmName().startsWith("RC5"))
512        //     {
513        //         if (baseEngine.getAlgorithmName().equals("RC5-32"))
514        //         {
515        //             if (rc5Param.getWordSize() != 32)
516        //             {
517        //                 throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 32 not " + rc5Param.getWordSize() + ".");
518        //             }
519        //         }
520        //         else if (baseEngine.getAlgorithmName().equals("RC5-64"))
521        //         {
522        //             if (rc5Param.getWordSize() != 64)
523        //             {
524        //                 throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 64 not " + rc5Param.getWordSize() + ".");
525        //             }
526        //         }
527        //     }
528        //     else
529        //     {
530        //         throw new InvalidAlgorithmParameterException("RC5 parameters passed to a cipher that is not RC5.");
531        //     }
532        //     if ((rc5Param.getIV() != null) && (ivLength != 0))
533        //     {
534        //         param = new ParametersWithIV(param, rc5Param.getIV());
535        //         ivParam = (ParametersWithIV)param;
536        //     }
537        // }
538        // END android-removed
539        else
540        {
541            throw new InvalidAlgorithmParameterException("unknown parameter type.");
542        }
543
544        if ((ivLength != 0) && !(param instanceof ParametersWithIV))
545        {
546            SecureRandom    ivRandom = random;
547
548            if (ivRandom == null)
549            {
550                ivRandom = new SecureRandom();
551            }
552
553            if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE))
554            {
555                byte[]  iv = new byte[ivLength];
556
557                ivRandom.nextBytes(iv);
558                param = new ParametersWithIV(param, iv);
559                ivParam = (ParametersWithIV)param;
560            }
561            else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0)
562            {
563                throw new InvalidAlgorithmParameterException("no IV set when one expected");
564            }
565        }
566
567        if (random != null && padded)
568        {
569            param = new ParametersWithRandom(param, random);
570        }
571
572        try
573        {
574            switch (opmode)
575            {
576            case Cipher.ENCRYPT_MODE:
577            case Cipher.WRAP_MODE:
578                cipher.init(true, param);
579                break;
580            case Cipher.DECRYPT_MODE:
581            case Cipher.UNWRAP_MODE:
582                cipher.init(false, param);
583                break;
584            default:
585                throw new InvalidParameterException("unknown opmode " + opmode + " passed");
586            }
587        }
588        catch (Exception e)
589        {
590            throw new InvalidKeyException(e.getMessage());
591        }
592    }
593
594    protected void engineInit(
595        int                 opmode,
596        Key                 key,
597        AlgorithmParameters params,
598        SecureRandom        random)
599    throws InvalidKeyException, InvalidAlgorithmParameterException
600    {
601        AlgorithmParameterSpec  paramSpec = null;
602
603        if (params != null)
604        {
605            for (int i = 0; i != availableSpecs.length; i++)
606            {
607                try
608                {
609                    paramSpec = params.getParameterSpec(availableSpecs[i]);
610                    break;
611                }
612                catch (Exception e)
613                {
614                    // try again if possible
615                }
616            }
617
618            if (paramSpec == null)
619            {
620                throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString());
621            }
622        }
623
624        engineInit(opmode, key, paramSpec, random);
625
626        engineParams = params;
627    }
628
629    protected void engineInit(
630        int                 opmode,
631        Key                 key,
632        SecureRandom        random)
633        throws InvalidKeyException
634    {
635        try
636        {
637            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
638        }
639        catch (InvalidAlgorithmParameterException e)
640        {
641            throw new InvalidKeyException(e.getMessage());
642        }
643    }
644
645    protected byte[] engineUpdate(
646        byte[]  input,
647        int     inputOffset,
648        int     inputLen)
649    {
650        int     length = cipher.getUpdateOutputSize(inputLen);
651
652        if (length > 0)
653        {
654                byte[]  out = new byte[length];
655
656                int len = cipher.processBytes(input, inputOffset, inputLen, out, 0);
657
658                if (len == 0)
659                {
660                    return null;
661                }
662                else if (len != out.length)
663                {
664                    byte[]  tmp = new byte[len];
665
666                    System.arraycopy(out, 0, tmp, 0, len);
667
668                    return tmp;
669                }
670
671                return out;
672        }
673
674        cipher.processBytes(input, inputOffset, inputLen, null, 0);
675
676        return null;
677    }
678
679    protected int engineUpdate(
680        byte[]  input,
681        int     inputOffset,
682        int     inputLen,
683        byte[]  output,
684        int     outputOffset)
685        throws ShortBufferException
686    {
687        try
688        {
689            return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
690        }
691        catch (DataLengthException e)
692        {
693            throw new ShortBufferException(e.getMessage());
694        }
695    }
696
697    protected byte[] engineDoFinal(
698        byte[]  input,
699        int     inputOffset,
700        int     inputLen)
701        throws IllegalBlockSizeException, BadPaddingException
702    {
703        int     len = 0;
704        byte[]  tmp = new byte[engineGetOutputSize(inputLen)];
705
706        if (inputLen != 0)
707        {
708            len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0);
709        }
710
711        try
712        {
713            len += cipher.doFinal(tmp, len);
714        }
715        catch (DataLengthException e)
716        {
717            throw new IllegalBlockSizeException(e.getMessage());
718        }
719        catch (InvalidCipherTextException e)
720        {
721            throw new BadPaddingException(e.getMessage());
722        }
723
724        if (len == tmp.length)
725        {
726            return tmp;
727        }
728
729        byte[]  out = new byte[len];
730
731        System.arraycopy(tmp, 0, out, 0, len);
732
733        return out;
734    }
735
736    protected int engineDoFinal(
737        byte[]  input,
738        int     inputOffset,
739        int     inputLen,
740        byte[]  output,
741        int     outputOffset)
742        throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
743    {
744        // BEGIN android-note
745        // added ShortBufferException to the throws statement
746        // END android-note
747        int     len = 0;
748
749        // BEGIN android-added
750        int outputLen = cipher.getOutputSize(inputLen);
751
752        if (outputLen + outputOffset > output.length) {
753            throw new ShortBufferException("need at least " + outputLen + " bytes");
754        }
755        // BEGIN android-added
756
757        if (inputLen != 0)
758        {
759                len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset);
760        }
761
762        try
763        {
764            return (len + cipher.doFinal(output, outputOffset + len));
765        }
766        catch (DataLengthException e)
767        {
768            throw new IllegalBlockSizeException(e.getMessage());
769        }
770        catch (InvalidCipherTextException e)
771        {
772            throw new BadPaddingException(e.getMessage());
773        }
774    }
775
776    private boolean isAEADModeName(
777        String modeName)
778    {
779        return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName);
780    }
781
782    /*
783     * The ciphers that inherit from us.
784     */
785
786    /**
787     * DES
788     */
789    static public class DES
790        extends JCEBlockCipher
791    {
792        public DES()
793        {
794            super(new DESEngine());
795        }
796    }
797
798    // BEGIN android-removed
799    // /**
800    //  * DESCBC
801    //  */
802    // static public class DESCBC
803    //     extends JCEBlockCipher
804    // {
805    //     public DESCBC()
806    //     {
807    //         super(new CBCBlockCipher(new DESEngine()), 64);
808    //     }
809    // }
810    //
811    // /**
812    //  *  GOST28147
813    //  */
814    // static public class GOST28147
815    //     extends JCEBlockCipher
816    // {
817    //     public GOST28147()
818    //     {
819    //         super(new GOST28147Engine());
820    //     }
821    // }
822    //
823    // static public class GOST28147cbc
824    //     extends JCEBlockCipher
825    // {
826    //     public GOST28147cbc()
827    //     {
828    //         super(new CBCBlockCipher(new GOST28147Engine()), 64);
829    //     }
830    // }
831    //
832    // /**
833    //  * RC2
834    //  */
835    // static public class RC2
836    //     extends JCEBlockCipher
837    // {
838    //     public RC2()
839    //     {
840    //         super(new RC2Engine());
841    //     }
842    // }
843    //
844    // /**
845    //  * RC2CBC
846    //  */
847    // static public class RC2CBC
848    //     extends JCEBlockCipher
849    // {
850    //     public RC2CBC()
851    //     {
852    //         super(new CBCBlockCipher(new RC2Engine()), 64);
853    //     }
854    // }
855    // END android-removed
856
857    /**
858     * PBEWithMD5AndDES
859     */
860    static public class PBEWithMD5AndDES
861        extends JCEBlockCipher
862    {
863        public PBEWithMD5AndDES()
864        {
865            super(new CBCBlockCipher(new DESEngine()));
866        }
867    }
868
869    /**
870     * PBEWithMD5AndRC2
871     */
872    static public class PBEWithMD5AndRC2
873        extends JCEBlockCipher
874    {
875        public PBEWithMD5AndRC2()
876        {
877            super(new CBCBlockCipher(new RC2Engine()));
878        }
879    }
880
881    /**
882     * PBEWithSHA1AndDES
883     */
884    static public class PBEWithSHA1AndDES
885        extends JCEBlockCipher
886    {
887        public PBEWithSHA1AndDES()
888        {
889            super(new CBCBlockCipher(new DESEngine()));
890        }
891    }
892
893    /**
894     * PBEWithSHA1AndRC2
895     */
896    static public class PBEWithSHA1AndRC2
897        extends JCEBlockCipher
898    {
899        public PBEWithSHA1AndRC2()
900        {
901            super(new CBCBlockCipher(new RC2Engine()));
902        }
903    }
904
905
906
907    /**
908     * PBEWithSHAAnd128BitRC2-CBC
909     */
910    static public class PBEWithSHAAnd128BitRC2
911        extends JCEBlockCipher
912    {
913        public PBEWithSHAAnd128BitRC2()
914        {
915            super(new CBCBlockCipher(new RC2Engine()));
916        }
917    }
918
919    /**
920     * PBEWithSHAAnd40BitRC2-CBC
921     */
922    static public class PBEWithSHAAnd40BitRC2
923        extends JCEBlockCipher
924    {
925        public PBEWithSHAAnd40BitRC2()
926        {
927            super(new CBCBlockCipher(new RC2Engine()));
928        }
929    }
930
931    /**
932     * PBEWithSHAAndTwofish-CBC
933     */
934    static public class PBEWithSHAAndTwofish
935        extends JCEBlockCipher
936    {
937        public PBEWithSHAAndTwofish()
938        {
939            super(new CBCBlockCipher(new TwofishEngine()));
940        }
941    }
942
943    /**
944     * PBEWithAES-CBC
945     */
946    static public class PBEWithAESCBC
947        extends JCEBlockCipher
948    {
949        public PBEWithAESCBC()
950        {
951            super(new CBCBlockCipher(new AESFastEngine()));
952        }
953    }
954
955    static private interface GenericBlockCipher
956    {
957        public void init(boolean forEncryption, CipherParameters params)
958            throws IllegalArgumentException;
959
960        public boolean wrapOnNoPadding();
961
962        public String getAlgorithmName();
963
964        public BlockCipher getUnderlyingCipher();
965
966        public int getOutputSize(int len);
967
968        public int getUpdateOutputSize(int len);
969
970        public int processByte(byte in, byte[] out, int outOff)
971            throws DataLengthException;
972
973        public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
974            throws DataLengthException;
975
976        public int doFinal(byte[] out, int outOff)
977            throws IllegalStateException, InvalidCipherTextException;
978    }
979
980    private static class BufferedGenericBlockCipher
981        implements GenericBlockCipher
982    {
983        private BufferedBlockCipher cipher;
984
985        BufferedGenericBlockCipher(BufferedBlockCipher cipher)
986        {
987            this.cipher = cipher;
988        }
989
990        BufferedGenericBlockCipher(BlockCipher cipher)
991        {
992            this.cipher = new PaddedBufferedBlockCipher(cipher);
993        }
994
995        BufferedGenericBlockCipher(BlockCipher cipher, BlockCipherPadding padding)
996        {
997            this.cipher = new PaddedBufferedBlockCipher(cipher, padding);
998        }
999
1000        public void init(boolean forEncryption, CipherParameters params)
1001            throws IllegalArgumentException
1002        {
1003            cipher.init(forEncryption, params);
1004        }
1005
1006        public boolean wrapOnNoPadding()
1007        {
1008            return !(cipher instanceof CTSBlockCipher);
1009        }
1010
1011        public String getAlgorithmName()
1012        {
1013            return cipher.getUnderlyingCipher().getAlgorithmName();
1014        }
1015
1016        public BlockCipher getUnderlyingCipher()
1017        {
1018            return cipher.getUnderlyingCipher();
1019        }
1020
1021        public int getOutputSize(int len)
1022        {
1023            return cipher.getOutputSize(len);
1024        }
1025
1026        public int getUpdateOutputSize(int len)
1027        {
1028            return cipher.getUpdateOutputSize(len);
1029        }
1030
1031        public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
1032        {
1033            return cipher.processByte(in, out, outOff);
1034        }
1035
1036        public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
1037        {
1038            return cipher.processBytes(in, inOff, len, out, outOff);
1039        }
1040
1041        public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
1042        {
1043            return cipher.doFinal(out, outOff);
1044        }
1045    }
1046
1047    private static class AEADGenericBlockCipher
1048        implements GenericBlockCipher
1049    {
1050        private AEADBlockCipher cipher;
1051
1052        AEADGenericBlockCipher(AEADBlockCipher cipher)
1053        {
1054            this.cipher = cipher;
1055        }
1056
1057        public void init(boolean forEncryption, CipherParameters params)
1058            throws IllegalArgumentException
1059        {
1060            cipher.init(forEncryption, params);
1061        }
1062
1063        public String getAlgorithmName()
1064        {
1065            return cipher.getUnderlyingCipher().getAlgorithmName();
1066        }
1067
1068        public boolean wrapOnNoPadding()
1069        {
1070            return false;
1071        }
1072
1073        public BlockCipher getUnderlyingCipher()
1074        {
1075            return cipher.getUnderlyingCipher();
1076        }
1077
1078        public int getOutputSize(int len)
1079        {
1080            return cipher.getOutputSize(len);
1081        }
1082
1083        public int getUpdateOutputSize(int len)
1084        {
1085            return cipher.getUpdateOutputSize(len);
1086        }
1087
1088        public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
1089        {
1090            return cipher.processByte(in, out, outOff);
1091        }
1092
1093        public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException
1094        {
1095            return cipher.processBytes(in, inOff, len, out, outOff);
1096        }
1097
1098        public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException
1099        {
1100            return cipher.doFinal(out, outOff);
1101        }
1102    }
1103}
1104