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