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