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