1/*
2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.crypto.provider;
27
28import java.security.*;
29import java.security.spec.*;
30import javax.crypto.*;
31import javax.crypto.spec.*;
32
33/**
34 * This class represents password-based encryption as defined by the PKCS #5
35 * standard.
36 * These algorithms implement PBE with HmacSHA1/HmacSHA2-family and AES-CBC.
37 * Padding is done as described in PKCS #5.
38 *
39 * @author Jan Luehe
40 *
41 *
42 * @see javax.crypto.Cipher
43 */
44abstract class PBES2Core extends CipherSpi {
45
46    private static final int DEFAULT_SALT_LENGTH = 20;
47    private static final int DEFAULT_COUNT = 4096;
48
49    // the encapsulated cipher
50    private final CipherCore cipher;
51    private final int keyLength; // in bits
52    private final int blkSize; // in bits
53    private final PBKDF2Core kdf;
54    private final String pbeAlgo;
55    private final String cipherAlgo;
56    private int iCount = DEFAULT_COUNT;
57    private byte[] salt = null;
58    private IvParameterSpec ivSpec = null;
59
60    /**
61     * Creates an instance of PBE Scheme 2 according to the selected
62     * password-based key derivation function and encryption scheme.
63     */
64    PBES2Core(String kdfAlgo, String cipherAlgo, int keySize)
65        throws NoSuchAlgorithmException, NoSuchPaddingException {
66
67        this.cipherAlgo = cipherAlgo;
68        keyLength = keySize * 8;
69        pbeAlgo = "PBEWith" + kdfAlgo + "And" + cipherAlgo + "_" + keyLength;
70
71        if (cipherAlgo.equals("AES")) {
72            blkSize = AESConstants.AES_BLOCK_SIZE;
73            cipher = new CipherCore(new AESCrypt(), blkSize);
74
75            switch(kdfAlgo) {
76            case "HmacSHA1":
77                kdf = new PBKDF2Core.HmacSHA1();
78                break;
79            case "HmacSHA224":
80                kdf = new PBKDF2Core.HmacSHA224();
81                break;
82            case "HmacSHA256":
83                kdf = new PBKDF2Core.HmacSHA256();
84                break;
85            case "HmacSHA384":
86                kdf = new PBKDF2Core.HmacSHA384();
87                break;
88            case "HmacSHA512":
89                kdf = new PBKDF2Core.HmacSHA512();
90                break;
91            default:
92                throw new NoSuchAlgorithmException(
93                    "No Cipher implementation for " + kdfAlgo);
94            }
95        } else {
96            throw new NoSuchAlgorithmException("No Cipher implementation for " +
97                                               pbeAlgo);
98        }
99        cipher.setMode("CBC");
100        cipher.setPadding("PKCS5Padding");
101    }
102
103    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
104        if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
105            throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
106        }
107    }
108
109    protected void engineSetPadding(String paddingScheme)
110        throws NoSuchPaddingException {
111        if ((paddingScheme != null) &&
112            (!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
113            throw new NoSuchPaddingException("Invalid padding scheme: " +
114                                             paddingScheme);
115        }
116    }
117
118    protected int engineGetBlockSize() {
119        return blkSize;
120    }
121
122    protected int engineGetOutputSize(int inputLen) {
123        return cipher.getOutputSize(inputLen);
124    }
125
126    protected byte[] engineGetIV() {
127        return cipher.getIV();
128    }
129
130    protected AlgorithmParameters engineGetParameters() {
131        AlgorithmParameters params = null;
132        if (salt == null) {
133            // generate random salt and use default iteration count
134            salt = new byte[DEFAULT_SALT_LENGTH];
135            SunJCE.getRandom().nextBytes(salt);
136            iCount = DEFAULT_COUNT;
137        }
138        if (ivSpec == null) {
139            // generate random IV
140            byte[] ivBytes = new byte[blkSize];
141            SunJCE.getRandom().nextBytes(ivBytes);
142            ivSpec = new IvParameterSpec(ivBytes);
143        }
144        PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount, ivSpec);
145        try {
146            params = AlgorithmParameters.getInstance(pbeAlgo,
147                SunJCE.getInstance());
148            params.init(pbeSpec);
149        } catch (NoSuchAlgorithmException nsae) {
150            // should never happen
151            throw new RuntimeException("SunJCE called, but not configured");
152        } catch (InvalidParameterSpecException ipse) {
153            // should never happen
154            throw new RuntimeException("PBEParameterSpec not supported");
155        }
156        return params;
157    }
158
159    protected void engineInit(int opmode, Key key, SecureRandom random)
160        throws InvalidKeyException {
161        try {
162            engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
163        } catch (InvalidAlgorithmParameterException ie) {
164            InvalidKeyException ike =
165                new InvalidKeyException("requires PBE parameters");
166            ike.initCause(ie);
167            throw ike;
168        }
169    }
170
171    protected void engineInit(int opmode, Key key,
172                              AlgorithmParameterSpec params,
173                              SecureRandom random)
174        throws InvalidKeyException, InvalidAlgorithmParameterException {
175
176        if ((key == null) ||
177            (key.getEncoded() == null) ||
178            !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
179            throw new InvalidKeyException("Missing password");
180        }
181
182        // TBD: consolidate the salt, ic and IV parameter checks below
183
184        // Extract salt and iteration count from the key, if present
185        if (key instanceof javax.crypto.interfaces.PBEKey) {
186            salt = ((javax.crypto.interfaces.PBEKey)key).getSalt();
187            if (salt != null && salt.length < 8) {
188                throw new InvalidAlgorithmParameterException(
189                    "Salt must be at least 8 bytes long");
190            }
191            iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount();
192            if (iCount == 0) {
193                iCount = DEFAULT_COUNT;
194            } else if (iCount < 0) {
195                throw new InvalidAlgorithmParameterException(
196                    "Iteration count must be a positive number");
197            }
198        }
199
200        // Extract salt, iteration count and IV from the params, if present
201        if (params == null) {
202            if (salt == null) {
203                // generate random salt and use default iteration count
204                salt = new byte[DEFAULT_SALT_LENGTH];
205                random.nextBytes(salt);
206                iCount = DEFAULT_COUNT;
207            }
208            if ((opmode == Cipher.ENCRYPT_MODE) ||
209                        (opmode == Cipher.WRAP_MODE)) {
210                // generate random IV
211                byte[] ivBytes = new byte[blkSize];
212                random.nextBytes(ivBytes);
213                ivSpec = new IvParameterSpec(ivBytes);
214            }
215        } else {
216            if (!(params instanceof PBEParameterSpec)) {
217                throw new InvalidAlgorithmParameterException
218                    ("Wrong parameter type: PBE expected");
219            }
220            // salt and iteration count from the params take precedence
221            byte[] specSalt = ((PBEParameterSpec) params).getSalt();
222            if (specSalt != null && specSalt.length < 8) {
223                throw new InvalidAlgorithmParameterException(
224                    "Salt must be at least 8 bytes long");
225            }
226            salt = specSalt;
227            int specICount = ((PBEParameterSpec) params).getIterationCount();
228            if (specICount == 0) {
229                specICount = DEFAULT_COUNT;
230            } else if (specICount < 0) {
231                throw new InvalidAlgorithmParameterException(
232                    "Iteration count must be a positive number");
233            }
234            iCount = specICount;
235
236            AlgorithmParameterSpec specParams =
237                ((PBEParameterSpec) params).getParameterSpec();
238            if (specParams != null) {
239                if (specParams instanceof IvParameterSpec) {
240                    ivSpec = (IvParameterSpec)specParams;
241                } else {
242                    throw new InvalidAlgorithmParameterException(
243                        "Wrong parameter type: IV expected");
244                }
245            } else if ((opmode == Cipher.ENCRYPT_MODE) ||
246                        (opmode == Cipher.WRAP_MODE)) {
247                // generate random IV
248                byte[] ivBytes = new byte[blkSize];
249                random.nextBytes(ivBytes);
250                ivSpec = new IvParameterSpec(ivBytes);
251            } else {
252                throw new InvalidAlgorithmParameterException(
253                    "Missing parameter type: IV expected");
254            }
255        }
256
257        SecretKeySpec cipherKey = null;
258        byte[] derivedKey = null;
259        byte[] passwdBytes = key.getEncoded();
260        char[] passwdChars = new char[passwdBytes.length];
261
262        for (int i=0; i<passwdChars.length; i++)
263            passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
264
265        PBEKeySpec pbeSpec =
266            new PBEKeySpec(passwdChars, salt, iCount, keyLength);
267            // password char[] was cloned in PBEKeySpec constructor,
268            // so we can zero it out here
269        java.util.Arrays.fill(passwdChars, ' ');
270        java.util.Arrays.fill(passwdBytes, (byte)0x00);
271
272        SecretKey s = null;
273
274        try {
275            s = kdf.engineGenerateSecret(pbeSpec);
276
277        } catch (InvalidKeySpecException ikse) {
278            InvalidKeyException ike =
279                new InvalidKeyException("Cannot construct PBE key");
280            ike.initCause(ikse);
281            throw ike;
282        }
283        derivedKey = s.getEncoded();
284        cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
285
286        // initialize the underlying cipher
287        cipher.init(opmode, cipherKey, ivSpec, random);
288    }
289
290    protected void engineInit(int opmode, Key key, AlgorithmParameters params,
291                              SecureRandom random)
292        throws InvalidKeyException, InvalidAlgorithmParameterException {
293        AlgorithmParameterSpec pbeSpec = null;
294        if (params != null) {
295            try {
296                pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
297            } catch (InvalidParameterSpecException ipse) {
298                throw new InvalidAlgorithmParameterException(
299                    "Wrong parameter type: PBE expected");
300            }
301        }
302        engineInit(opmode, key, pbeSpec, random);
303    }
304
305    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
306        return cipher.update(input, inputOffset, inputLen);
307    }
308
309    protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
310                               byte[] output, int outputOffset)
311        throws ShortBufferException {
312        return cipher.update(input, inputOffset, inputLen,
313                             output, outputOffset);
314    }
315
316    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
317        throws IllegalBlockSizeException, BadPaddingException {
318        return cipher.doFinal(input, inputOffset, inputLen);
319    }
320
321    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
322                                byte[] output, int outputOffset)
323        throws ShortBufferException, IllegalBlockSizeException,
324               BadPaddingException {
325        return cipher.doFinal(input, inputOffset, inputLen,
326                              output, outputOffset);
327    }
328
329    protected int engineGetKeySize(Key key) throws InvalidKeyException {
330        return keyLength;
331    }
332
333    protected byte[] engineWrap(Key key)
334        throws IllegalBlockSizeException, InvalidKeyException {
335        return cipher.wrap(key);
336    }
337
338    protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
339                               int wrappedKeyType)
340        throws InvalidKeyException, NoSuchAlgorithmException {
341        byte[] encodedKey;
342        return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
343                             wrappedKeyType);
344    }
345
346    public static final class HmacSHA1AndAES_128 extends PBES2Core {
347        public HmacSHA1AndAES_128()
348            throws NoSuchAlgorithmException, NoSuchPaddingException {
349            super("HmacSHA1", "AES", 16);
350        }
351    }
352
353    public static final class HmacSHA224AndAES_128 extends PBES2Core {
354        public HmacSHA224AndAES_128()
355            throws NoSuchAlgorithmException, NoSuchPaddingException {
356            super("HmacSHA224", "AES", 16);
357        }
358    }
359
360    public static final class HmacSHA256AndAES_128 extends PBES2Core {
361        public HmacSHA256AndAES_128()
362            throws NoSuchAlgorithmException, NoSuchPaddingException {
363            super("HmacSHA256", "AES", 16);
364        }
365    }
366
367    public static final class HmacSHA384AndAES_128 extends PBES2Core {
368        public HmacSHA384AndAES_128()
369            throws NoSuchAlgorithmException, NoSuchPaddingException {
370            super("HmacSHA384", "AES", 16);
371        }
372    }
373
374    public static final class HmacSHA512AndAES_128 extends PBES2Core {
375        public HmacSHA512AndAES_128()
376            throws NoSuchAlgorithmException, NoSuchPaddingException {
377            super("HmacSHA512", "AES", 16);
378        }
379    }
380
381    public static final class HmacSHA1AndAES_256 extends PBES2Core {
382        public HmacSHA1AndAES_256()
383            throws NoSuchAlgorithmException, NoSuchPaddingException {
384            super("HmacSHA1", "AES", 32);
385        }
386    }
387
388    public static final class HmacSHA224AndAES_256 extends PBES2Core {
389        public HmacSHA224AndAES_256()
390            throws NoSuchAlgorithmException, NoSuchPaddingException {
391            super("HmacSHA224", "AES", 32);
392        }
393    }
394
395    public static final class HmacSHA256AndAES_256 extends PBES2Core {
396        public HmacSHA256AndAES_256()
397            throws NoSuchAlgorithmException, NoSuchPaddingException {
398            super("HmacSHA256", "AES", 32);
399        }
400    }
401
402    public static final class HmacSHA384AndAES_256 extends PBES2Core {
403        public HmacSHA384AndAES_256()
404            throws NoSuchAlgorithmException, NoSuchPaddingException {
405            super("HmacSHA384", "AES", 32);
406        }
407    }
408
409    public static final class HmacSHA512AndAES_256 extends PBES2Core {
410        public HmacSHA512AndAES_256()
411            throws NoSuchAlgorithmException, NoSuchPaddingException {
412            super("HmacSHA512", "AES", 32);
413        }
414    }
415}
416