1 package org.bouncycastle.jcajce.provider.symmetric.util;
2 
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Method;
5 import java.nio.ByteBuffer;
6 import java.security.AlgorithmParameters;
7 import java.security.InvalidAlgorithmParameterException;
8 import java.security.InvalidKeyException;
9 import java.security.InvalidParameterException;
10 import java.security.Key;
11 import java.security.NoSuchAlgorithmException;
12 import java.security.SecureRandom;
13 import java.security.spec.AlgorithmParameterSpec;
14 
15 import javax.crypto.BadPaddingException;
16 import javax.crypto.Cipher;
17 import javax.crypto.IllegalBlockSizeException;
18 import javax.crypto.NoSuchPaddingException;
19 import javax.crypto.SecretKey;
20 import javax.crypto.ShortBufferException;
21 import javax.crypto.spec.IvParameterSpec;
22 import 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 
28 import org.bouncycastle.asn1.cms.GCMParameters;
29 import org.bouncycastle.crypto.BlockCipher;
30 import org.bouncycastle.crypto.BufferedBlockCipher;
31 import org.bouncycastle.crypto.CipherParameters;
32 import org.bouncycastle.crypto.DataLengthException;
33 import org.bouncycastle.crypto.InvalidCipherTextException;
34 import org.bouncycastle.crypto.OutputLengthException;
35 import org.bouncycastle.crypto.modes.AEADBlockCipher;
36 import org.bouncycastle.crypto.modes.CBCBlockCipher;
37 import org.bouncycastle.crypto.modes.CCMBlockCipher;
38 import org.bouncycastle.crypto.modes.CFBBlockCipher;
39 import 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
44 import 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
49 import 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
54 import org.bouncycastle.crypto.modes.SICBlockCipher;
55 import org.bouncycastle.crypto.paddings.BlockCipherPadding;
56 import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
57 import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
58 import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
59 import org.bouncycastle.crypto.paddings.TBCPadding;
60 import org.bouncycastle.crypto.paddings.X923Padding;
61 import org.bouncycastle.crypto.paddings.ZeroBytePadding;
62 import org.bouncycastle.crypto.params.AEADParameters;
63 import org.bouncycastle.crypto.params.KeyParameter;
64 import org.bouncycastle.crypto.params.ParametersWithIV;
65 import org.bouncycastle.crypto.params.ParametersWithRandom;
66 // BEGIN android-removed
67 // import org.bouncycastle.crypto.params.ParametersWithSBox;
68 // END android-removed
69 import 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
75 import org.bouncycastle.jce.provider.BouncyCastleProvider;
76 import org.bouncycastle.util.Strings;
77 
78 public 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 
lookup(String className)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 
BaseBlockCipher( BlockCipher engine)130     protected BaseBlockCipher(
131         BlockCipher engine)
132     {
133         baseEngine = engine;
134 
135         cipher = new BufferedGenericBlockCipher(engine);
136     }
137 
BaseBlockCipher( BlockCipherProvider provider)138     protected BaseBlockCipher(
139         BlockCipherProvider provider)
140     {
141         baseEngine = provider.get();
142         engineProvider = provider;
143 
144         cipher = new BufferedGenericBlockCipher(provider.get());
145     }
146 
BaseBlockCipher( AEADBlockCipher engine)147     protected BaseBlockCipher(
148         AEADBlockCipher engine)
149     {
150         baseEngine = engine.getUnderlyingCipher();
151         ivLength = baseEngine.getBlockSize();
152         cipher = new AEADGenericBlockCipher(engine);
153     }
154 
BaseBlockCipher( org.bouncycastle.crypto.BlockCipher engine, int ivLength)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 
BaseBlockCipher( BufferedBlockCipher engine, int ivLength)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 
engineGetBlockSize()175     protected int engineGetBlockSize()
176     {
177         return baseEngine.getBlockSize();
178     }
179 
engineGetIV()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 
engineGetKeySize( Key key)190     protected int engineGetKeySize(
191         Key     key)
192     {
193         return key.getEncoded().length * 8;
194     }
195 
engineGetOutputSize( int inputLen)196     protected int engineGetOutputSize(
197         int     inputLen)
198     {
199         return cipher.getOutputSize(inputLen);
200     }
201 
engineGetParameters()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 
engineSetMode( String mode)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 
engineSetPadding( String padding)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 
engineInit( int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)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 
engineInit( int opmode, Key key, AlgorithmParameters params, SecureRandom random)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 
engineInit( int opmode, Key key, SecureRandom random)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 
engineUpdateAAD(byte[] input, int offset, int length)769     protected void engineUpdateAAD(byte[] input, int offset, int length)
770     {
771         cipher.updateAAD(input, offset, length);
772     }
773 
engineUpdateAAD(ByteBuffer bytebuffer)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 
engineUpdate( byte[] input, int inputOffset, int inputLen)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 
engineUpdate( byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)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 
engineDoFinal( byte[] input, int inputOffset, int inputLen)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 
engineDoFinal( byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)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 
isAEADModeName( String modeName)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     {
init(boolean forEncryption, CipherParameters params)911         public void init(boolean forEncryption, CipherParameters params)
912             throws IllegalArgumentException;
913 
wrapOnNoPadding()914         public boolean wrapOnNoPadding();
915 
getAlgorithmName()916         public String getAlgorithmName();
917 
getUnderlyingCipher()918         public org.bouncycastle.crypto.BlockCipher getUnderlyingCipher();
919 
getOutputSize(int len)920         public int getOutputSize(int len);
921 
getUpdateOutputSize(int len)922         public int getUpdateOutputSize(int len);
923 
updateAAD(byte[] input, int offset, int length)924         public void updateAAD(byte[] input, int offset, int length);
925 
processByte(byte in, byte[] out, int outOff)926         public int processByte(byte in, byte[] out, int outOff)
927             throws DataLengthException;
928 
processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)929         public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
930             throws DataLengthException;
931 
doFinal(byte[] out, int outOff)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 
BufferedGenericBlockCipher(BufferedBlockCipher cipher)942         BufferedGenericBlockCipher(BufferedBlockCipher cipher)
943         {
944             this.cipher = cipher;
945         }
946 
BufferedGenericBlockCipher(org.bouncycastle.crypto.BlockCipher cipher)947         BufferedGenericBlockCipher(org.bouncycastle.crypto.BlockCipher cipher)
948         {
949             this.cipher = new PaddedBufferedBlockCipher(cipher);
950         }
951 
BufferedGenericBlockCipher(org.bouncycastle.crypto.BlockCipher cipher, BlockCipherPadding padding)952         BufferedGenericBlockCipher(org.bouncycastle.crypto.BlockCipher cipher, BlockCipherPadding padding)
953         {
954             this.cipher = new PaddedBufferedBlockCipher(cipher, padding);
955         }
956 
init(boolean forEncryption, CipherParameters params)957         public void init(boolean forEncryption, CipherParameters params)
958             throws IllegalArgumentException
959         {
960             cipher.init(forEncryption, params);
961         }
962 
wrapOnNoPadding()963         public boolean wrapOnNoPadding()
964         {
965             return !(cipher instanceof CTSBlockCipher);
966         }
967 
getAlgorithmName()968         public String getAlgorithmName()
969         {
970             return cipher.getUnderlyingCipher().getAlgorithmName();
971         }
972 
getUnderlyingCipher()973         public org.bouncycastle.crypto.BlockCipher getUnderlyingCipher()
974         {
975             return cipher.getUnderlyingCipher();
976         }
977 
getOutputSize(int len)978         public int getOutputSize(int len)
979         {
980             return cipher.getOutputSize(len);
981         }
982 
getUpdateOutputSize(int len)983         public int getUpdateOutputSize(int len)
984         {
985             return cipher.getUpdateOutputSize(len);
986         }
987 
updateAAD(byte[] input, int offset, int length)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 
processByte(byte in, byte[] out, int outOff)993         public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
994         {
995             return cipher.processByte(in, out, outOff);
996         }
997 
processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)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 
doFinal(byte[] out, int outOff)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 
findExceptionConstructor(Class clazz)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 
AEADGenericBlockCipher(AEADBlockCipher cipher)1047         AEADGenericBlockCipher(AEADBlockCipher cipher)
1048         {
1049             this.cipher = cipher;
1050         }
1051 
init(boolean forEncryption, CipherParameters params)1052         public void init(boolean forEncryption, CipherParameters params)
1053             throws IllegalArgumentException
1054         {
1055             cipher.init(forEncryption, params);
1056         }
1057 
getAlgorithmName()1058         public String getAlgorithmName()
1059         {
1060             return cipher.getUnderlyingCipher().getAlgorithmName();
1061         }
1062 
wrapOnNoPadding()1063         public boolean wrapOnNoPadding()
1064         {
1065             return false;
1066         }
1067 
getUnderlyingCipher()1068         public org.bouncycastle.crypto.BlockCipher getUnderlyingCipher()
1069         {
1070             return cipher.getUnderlyingCipher();
1071         }
1072 
getOutputSize(int len)1073         public int getOutputSize(int len)
1074         {
1075             return cipher.getOutputSize(len);
1076         }
1077 
getUpdateOutputSize(int len)1078         public int getUpdateOutputSize(int len)
1079         {
1080             return cipher.getUpdateOutputSize(len);
1081         }
1082 
updateAAD(byte[] input, int offset, int length)1083         public void updateAAD(byte[] input, int offset, int length)
1084         {
1085             cipher.processAADBytes(input, offset, length);
1086         }
1087 
processByte(byte in, byte[] out, int outOff)1088         public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
1089         {
1090             return cipher.processByte(in, out, outOff);
1091         }
1092 
processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)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 
doFinal(byte[] out, int outOff)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