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