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