1package org.bouncycastle.jcajce.provider.symmetric.util;
2
3import java.lang.reflect.Method;
4import java.security.InvalidAlgorithmParameterException;
5import java.security.spec.AlgorithmParameterSpec;
6
7import javax.crypto.SecretKey;
8// BEGIN android-added
9import javax.crypto.spec.IvParameterSpec;
10// END android-added
11import javax.crypto.spec.PBEKeySpec;
12import javax.crypto.spec.PBEParameterSpec;
13
14import org.bouncycastle.crypto.CipherParameters;
15import org.bouncycastle.crypto.PBEParametersGenerator;
16// BEGIN android-added
17import org.bouncycastle.crypto.digests.AndroidDigestFactory;
18// END android-added
19// BEGIN android-removed
20// import org.bouncycastle.crypto.digests.GOST3411Digest;
21// import org.bouncycastle.crypto.digests.MD2Digest;
22// import org.bouncycastle.crypto.digests.RIPEMD160Digest;
23// import org.bouncycastle.crypto.digests.TigerDigest;
24// END android-removed
25import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
26import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
27import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
28import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
29import org.bouncycastle.crypto.params.DESParameters;
30import org.bouncycastle.crypto.params.KeyParameter;
31import org.bouncycastle.crypto.params.ParametersWithIV;
32// BEGIN android-removed
33// import org.bouncycastle.crypto.util.DigestFactory;
34// END android-removed
35
36public interface PBE
37{
38    //
39    // PBE Based encryption constants - by default we do PKCS12 with SHA-1
40    //
41    static final int        MD5          = 0;
42    static final int        SHA1         = 1;
43    // BEGIN android-removed
44    // static final int        RIPEMD160    = 2;
45    // static final int        TIGER        = 3;
46    // END android-removed
47    static final int        SHA256       = 4;
48    // BEGIN android-removed
49    // static final int        MD2          = 5;
50    // static final int        GOST3411     = 6;
51    // END android-removed
52    static final int        SHA224       = 7;
53    static final int        SHA384       = 8;
54    static final int        SHA512       = 9;
55
56    static final int        PKCS5S1      = 0;
57    static final int        PKCS5S2      = 1;
58    static final int        PKCS12       = 2;
59    static final int        OPENSSL      = 3;
60    static final int        PKCS5S1_UTF8 = 4;
61    static final int        PKCS5S2_UTF8 = 5;
62
63
64    /**
65     * uses the appropriate mixer to generate the key and IV if necessary.
66     */
67    static class Util
68    {
69        static private PBEParametersGenerator makePBEGenerator(
70            int                     type,
71            int                     hash)
72        {
73            PBEParametersGenerator  generator;
74
75            if (type == PKCS5S1 || type == PKCS5S1_UTF8)
76            {
77                switch (hash)
78                {
79                // BEGIN android-removed
80                // case MD2:
81                //     generator = new PKCS5S1ParametersGenerator(new MD2Digest());
82                //     break;
83                // END android-removed
84                case MD5:
85                    // BEGIN android-changed
86                    generator = new PKCS5S1ParametersGenerator(AndroidDigestFactory.getMD5());
87                    // END android-changed
88                    break;
89                case SHA1:
90                    // BEGIN android-changed
91                    generator = new PKCS5S1ParametersGenerator(AndroidDigestFactory.getSHA1());
92                    // END android-changed
93                    break;
94                default:
95                    throw new IllegalStateException("PKCS5 scheme 1 only supports MD2, MD5 and SHA1.");
96                }
97            }
98            else if (type == PKCS5S2 || type == PKCS5S2_UTF8)
99            {
100                switch (hash)
101                {
102                // BEGIN android-removed
103                // case MD2:
104                //     generator = new PKCS5S2ParametersGenerator(new MD2Digest());
105                //     break;
106                // END android-removed
107                case MD5:
108                    // BEGIN android-changed
109                    generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getMD5());
110                    // END android-changed
111                    break;
112
113                case SHA1:
114                    // BEGIN android-changed
115                    generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA1());
116                    // END android-changed
117                    break;
118                // BEGIN android-removed
119                // case RIPEMD160:
120                //     generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest());
121                //     break;
122                // case TIGER:
123                //     generator = new PKCS5S2ParametersGenerator(new TigerDigest());
124                //     break;
125                // END android-removed
126                // BEGIN android-added
127                case SHA224:
128                    generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA224());
129                    break;
130                // END android-added
131                case SHA256:
132                    // BEGIN android-changed
133                    generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA256());
134                    // END android-changed
135                    break;
136                // BEGIN android-added
137                case SHA384:
138                    generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA384());
139                    break;
140                case SHA512:
141                    generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA512());
142                    break;
143                // END android-added
144                default:
145                    throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption.");
146                }
147            }
148            else if (type == PKCS12)
149            {
150                switch (hash)
151                {
152                // BEGIN android-removed
153                // case MD2:
154                //     generator = new PKCS12ParametersGenerator(new MD2Digest());
155                //     break;
156                // END android-removed
157                case MD5:
158                    // BEGIN android-changed
159                    generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getMD5());
160                    // END android-changed
161                    break;
162                case SHA1:
163                    // BEGIN android-changed
164                    generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
165                    // END android-changed
166                    break;
167                // BEGIN android-removed
168                // case RIPEMD160:
169                //     generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
170                //     break;
171                // case TIGER:
172                //     generator = new PKCS12ParametersGenerator(new TigerDigest());
173                //     break;
174                // END android-removed
175                // BEGIN android-added
176                case SHA224:
177                    generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA224());
178                    break;
179                // END android-added
180                case SHA256:
181                    // BEGIN android-changed
182                    generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA256());
183                    // END android-changed
184                    break;
185                // BEGIN android-added
186                case SHA384:
187                    generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA384());
188                    break;
189                case SHA512:
190                    generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA512());
191                    break;
192                default:
193                    throw new IllegalStateException("unknown digest scheme for PBE encryption.");
194                }
195            }
196            else
197            {
198                generator = new OpenSSLPBEParametersGenerator();
199            }
200
201            return generator;
202        }
203
204        /**
205         * construct a key and iv (if necessary) suitable for use with a
206         * Cipher.
207         */
208        public static CipherParameters makePBEParameters(
209            byte[] pbeKey,
210            int scheme,
211            int digest,
212            int keySize,
213            int ivSize,
214            AlgorithmParameterSpec spec,
215            String targetAlgorithm)
216            throws InvalidAlgorithmParameterException
217        {
218            if ((spec == null) || !(spec instanceof PBEParameterSpec))
219            {
220                throw new InvalidAlgorithmParameterException("Need a PBEParameter spec with a PBE key.");
221            }
222
223            PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
224            PBEParametersGenerator  generator = makePBEGenerator(scheme, digest);
225            byte[]                  key = pbeKey;
226            CipherParameters        param;
227
228//            if (pbeKey.shouldTryWrongPKCS12())
229//            {
230//                key = new byte[2];
231//            }
232
233            generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
234
235            if (ivSize != 0)
236            {
237                param = generator.generateDerivedParameters(keySize, ivSize);
238                // BEGIN ANDROID-ADDED
239                // PKCS5S2 doesn't specify that the IV must be generated from the password. If the
240                // IV is passed as a parameter, use it.
241                AlgorithmParameterSpec parameterSpecFromPBEParameterSpec =
242                        getParameterSpecFromPBEParameterSpec(pbeParam);
243                if ((scheme == PKCS5S2 || scheme == PKCS5S2_UTF8)
244                        && parameterSpecFromPBEParameterSpec instanceof IvParameterSpec) {
245                    ParametersWithIV parametersWithIV = (ParametersWithIV) param;
246                    IvParameterSpec ivParameterSpec =
247                            (IvParameterSpec) parameterSpecFromPBEParameterSpec;
248                    param = new ParametersWithIV(
249                            (KeyParameter) parametersWithIV.getParameters(),
250                            ivParameterSpec.getIV());
251                }
252                // END ANDROID-ADDED
253            }
254            else
255            {
256                param = generator.generateDerivedParameters(keySize);
257            }
258
259            if (targetAlgorithm.startsWith("DES"))
260            {
261                if (param instanceof ParametersWithIV)
262                {
263                    KeyParameter    kParam = (KeyParameter)((ParametersWithIV)param).getParameters();
264
265                    DESParameters.setOddParity(kParam.getKey());
266                }
267                else
268                {
269                    KeyParameter    kParam = (KeyParameter)param;
270
271                    DESParameters.setOddParity(kParam.getKey());
272                }
273            }
274
275            return param;
276        }
277
278        /**
279         * construct a key and iv (if necessary) suitable for use with a
280         * Cipher.
281         */
282        public static CipherParameters makePBEParameters(
283            BCPBEKey pbeKey,
284            AlgorithmParameterSpec spec,
285            String targetAlgorithm)
286        {
287            if ((spec == null) || !(spec instanceof PBEParameterSpec))
288            {
289                throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key.");
290            }
291
292            PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
293            PBEParametersGenerator  generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest());
294            byte[]                  key = pbeKey.getEncoded();
295            CipherParameters        param;
296
297            if (pbeKey.shouldTryWrongPKCS12())
298            {
299                key = new byte[2];
300            }
301
302            generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
303
304            if (pbeKey.getIvSize() != 0)
305            {
306                param = generator.generateDerivedParameters(pbeKey.getKeySize(), pbeKey.getIvSize());
307                // BEGIN ANDROID-ADDED
308                // PKCS5S2 doesn't specify that the IV must be generated from the password. If the
309                // IV is passed as a parameter, use it.
310                AlgorithmParameterSpec parameterSpecFromPBEParameterSpec =
311                        getParameterSpecFromPBEParameterSpec(pbeParam);
312                if ((pbeKey.getType() == PKCS5S2 || pbeKey.getType() == PKCS5S2_UTF8)
313                        && parameterSpecFromPBEParameterSpec instanceof IvParameterSpec) {
314                    ParametersWithIV parametersWithIV = (ParametersWithIV) param;
315                    IvParameterSpec ivParameterSpec =
316                            (IvParameterSpec) parameterSpecFromPBEParameterSpec;
317                    param = new ParametersWithIV(
318                            (KeyParameter) parametersWithIV.getParameters(),
319                            ivParameterSpec.getIV());
320                }
321                // END ANDROID-ADDED
322            }
323            else
324            {
325                param = generator.generateDerivedParameters(pbeKey.getKeySize());
326            }
327
328            if (targetAlgorithm.startsWith("DES"))
329            {
330                if (param instanceof ParametersWithIV)
331                {
332                    KeyParameter    kParam = (KeyParameter)((ParametersWithIV)param).getParameters();
333
334                    DESParameters.setOddParity(kParam.getKey());
335                }
336                else
337                {
338                    KeyParameter    kParam = (KeyParameter)param;
339
340                    DESParameters.setOddParity(kParam.getKey());
341                }
342            }
343
344            return param;
345        }
346
347        /**
348         * generate a PBE based key suitable for a MAC algorithm, the
349         * key size is chosen according the MAC size, or the hashing algorithm,
350         * whichever is greater.
351         */
352        public static CipherParameters makePBEMacParameters(
353            BCPBEKey pbeKey,
354            AlgorithmParameterSpec spec)
355        {
356            if ((spec == null) || !(spec instanceof PBEParameterSpec))
357            {
358                throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key.");
359            }
360
361            PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
362            PBEParametersGenerator  generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest());
363            byte[]                  key = pbeKey.getEncoded();
364            CipherParameters        param;
365
366            generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
367
368            param = generator.generateDerivedMacParameters(pbeKey.getKeySize());
369
370            return param;
371        }
372
373        /**
374         * generate a PBE based key suitable for a MAC algorithm, the
375         * key size is chosen according the MAC size, or the hashing algorithm,
376         * whichever is greater.
377         */
378        public static CipherParameters makePBEMacParameters(
379            PBEKeySpec keySpec,
380            int type,
381            int hash,
382            int keySize)
383        {
384            PBEParametersGenerator  generator = makePBEGenerator(type, hash);
385            byte[]                  key;
386            CipherParameters        param;
387
388            key = convertPassword(type, keySpec);
389
390            generator.init(key, keySpec.getSalt(), keySpec.getIterationCount());
391
392            param = generator.generateDerivedMacParameters(keySize);
393
394            for (int i = 0; i != key.length; i++)
395            {
396                key[i] = 0;
397            }
398
399            return param;
400        }
401
402        /**
403         * construct a key and iv (if necessary) suitable for use with a
404         * Cipher.
405         */
406        public static CipherParameters makePBEParameters(
407            PBEKeySpec keySpec,
408            int type,
409            int hash,
410            int keySize,
411            int ivSize)
412        {
413            PBEParametersGenerator  generator = makePBEGenerator(type, hash);
414            byte[]                  key;
415            CipherParameters        param;
416
417            key = convertPassword(type, keySpec);
418
419            generator.init(key, keySpec.getSalt(), keySpec.getIterationCount());
420
421            if (ivSize != 0)
422            {
423                param = generator.generateDerivedParameters(keySize, ivSize);
424            }
425            else
426            {
427                param = generator.generateDerivedParameters(keySize);
428            }
429
430            for (int i = 0; i != key.length; i++)
431            {
432                key[i] = 0;
433            }
434
435            return param;
436        }
437
438        /**
439         * generate a PBE based key suitable for a MAC algorithm, the
440         * key size is chosen according the MAC size, or the hashing algorithm,
441         * whichever is greater.
442         */
443        public static CipherParameters makePBEMacParameters(
444            SecretKey key,
445            int type,
446            int hash,
447            int keySize,
448            PBEParameterSpec pbeSpec)
449        {
450            PBEParametersGenerator  generator = makePBEGenerator(type, hash);
451            CipherParameters        param;
452
453            byte[] keyBytes = key.getEncoded();
454
455            generator.init(key.getEncoded(), pbeSpec.getSalt(), pbeSpec.getIterationCount());
456
457            param = generator.generateDerivedMacParameters(keySize);
458
459            for (int i = 0; i != keyBytes.length; i++)
460            {
461                keyBytes[i] = 0;
462            }
463
464            return param;
465        }
466
467        // BEGIN android-added
468        /**
469         * Invokes the method {@link PBEParameterSpec#getParameterSpec()} via reflection.
470         *
471         * Needed as the method was introduced in Java 1.8 and Bouncycastle level is 1.5.
472         *
473         * @return the parameter spec, or null if the method is not available.
474         */
475        public static AlgorithmParameterSpec getParameterSpecFromPBEParameterSpec(
476                PBEParameterSpec pbeParameterSpec) {
477            try {
478                Method getParameterSpecMethod = PBE.class.getClassLoader()
479                        .loadClass("javax.crypto.spec.PBEParameterSpec")
480                        .getMethod("getParameterSpec");
481                return (AlgorithmParameterSpec) getParameterSpecMethod.invoke(pbeParameterSpec);
482            } catch (Exception e) {
483                return null;
484            }
485        }
486        // END android-added
487
488
489        private static byte[] convertPassword(int type, PBEKeySpec keySpec)
490        {
491            byte[] key;
492
493            if (type == PKCS12)
494            {
495                key = PBEParametersGenerator.PKCS12PasswordToBytes(keySpec.getPassword());
496            }
497            else if (type == PKCS5S2_UTF8 || type == PKCS5S1_UTF8)
498            {
499                key = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(keySpec.getPassword());
500            }
501            else
502            {
503                key = PBEParametersGenerator.PKCS5PasswordToBytes(keySpec.getPassword());
504            }
505            return key;
506        }
507    }
508}
509