1/*
2 * Copyright (c) 2002, 2013, 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 *
37 * @author Jan Luehe
38 *
39 *
40 * @see javax.crypto.Cipher
41 */
42final class PBES1Core {
43
44    // the encapsulated DES cipher
45    private CipherCore cipher;
46    private MessageDigest md;
47    private int blkSize;
48    private String algo = null;
49    private byte[] salt = null;
50    private int iCount = 10;
51
52    /**
53     * Creates an instance of PBE Cipher using the specified CipherSpi
54     * instance.
55     *
56     */
57    PBES1Core(String cipherAlg) throws NoSuchAlgorithmException,
58        NoSuchPaddingException {
59        algo = cipherAlg;
60        if (algo.equals("DES")) {
61            cipher = new CipherCore(new DESCrypt(),
62                                    DESConstants.DES_BLOCK_SIZE);
63        } else if (algo.equals("DESede")) {
64
65            cipher = new CipherCore(new DESedeCrypt(),
66                                    DESConstants.DES_BLOCK_SIZE);
67        } else {
68            throw new NoSuchAlgorithmException("No Cipher implementation " +
69                                               "for PBEWithMD5And" + algo);
70        }
71        cipher.setMode("CBC");
72        cipher.setPadding("PKCS5Padding");
73        // get instance of MD5
74        md = MessageDigest.getInstance("MD5");
75    }
76
77    /**
78     * Sets the mode of this cipher. This algorithm can only be run in CBC
79     * mode.
80     *
81     * @param mode the cipher mode
82     *
83     * @exception NoSuchAlgorithmException if the requested cipher mode is
84     * invalid
85     */
86    void setMode(String mode) throws NoSuchAlgorithmException {
87        cipher.setMode(mode);
88    }
89
90     /**
91     * Sets the padding mechanism of this cipher. This algorithm only uses
92     * PKCS #5 padding.
93     *
94     * @param padding the padding mechanism
95     *
96     * @exception NoSuchPaddingException if the requested padding mechanism
97     * is invalid
98     */
99    void setPadding(String paddingScheme) throws NoSuchPaddingException {
100        cipher.setPadding(paddingScheme);
101    }
102
103    /**
104     * Returns the block size (in bytes).
105     *
106     * @return the block size (in bytes)
107     */
108    int getBlockSize() {
109        return DESConstants.DES_BLOCK_SIZE;
110    }
111
112    /**
113     * Returns the length in bytes that an output buffer would need to be in
114     * order to hold the result of the next <code>update</code> or
115     * <code>doFinal</code> operation, given the input length
116     * <code>inputLen</code> (in bytes).
117     *
118     * <p>This call takes into account any unprocessed (buffered) data from a
119     * previous <code>update</code> call, and padding.
120     *
121     * <p>The actual output length of the next <code>update</code> or
122     * <code>doFinal</code> call may be smaller than the length returned by
123     * this method.
124     *
125     * @param inputLen the input length (in bytes)
126     *
127     * @return the required output buffer size (in bytes)
128     *
129     */
130    int getOutputSize(int inputLen) {
131        return cipher.getOutputSize(inputLen);
132    }
133
134    /**
135     * Returns the initialization vector (IV) in a new buffer.
136     *
137     * <p> This is useful in the case where a random IV has been created
138     * (see <a href = "#init">init</a>),
139     * or in the context of password-based encryption or
140     * decryption, where the IV is derived from a user-supplied password.
141     *
142     * @return the initialization vector in a new buffer, or null if the
143     * underlying algorithm does not use an IV, or if the IV has not yet
144     * been set.
145     */
146    byte[] getIV() {
147        return cipher.getIV();
148    }
149
150    /**
151     * Returns the parameters used with this cipher.
152     *
153     * <p>The returned parameters may be the same that were used to initialize
154     * this cipher, or may contain the default set of parameters or a set of
155     * randomly generated parameters used by the underlying cipher
156     * implementation (provided that the underlying cipher implementation
157     * uses a default set of parameters or creates new parameters if it needs
158     * parameters but was not initialized with any).
159     *
160     * @return the parameters used with this cipher, or null if this cipher
161     * does not use any parameters.
162     */
163    AlgorithmParameters getParameters() {
164        AlgorithmParameters params = null;
165        if (salt == null) {
166            salt = new byte[8];
167            SunJCE.getRandom().nextBytes(salt);
168        }
169        PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
170        try {
171            params = AlgorithmParameters.getInstance("PBEWithMD5And" +
172                (algo.equalsIgnoreCase("DES")? "DES":"TripleDES"),
173                SunJCE.getInstance());
174            params.init(pbeSpec);
175        } catch (NoSuchAlgorithmException nsae) {
176            // should never happen
177            throw new RuntimeException("SunJCE called, but not configured");
178        } catch (InvalidParameterSpecException ipse) {
179            // should never happen
180            throw new RuntimeException("PBEParameterSpec not supported");
181        }
182        return params;
183    }
184
185    /**
186     * Initializes this cipher with a key, a set of
187     * algorithm parameters, and a source of randomness.
188     * The cipher is initialized for one of the following four operations:
189     * encryption, decryption, key wrapping or key unwrapping, depending on
190     * the value of <code>opmode</code>.
191     *
192     * <p>If this cipher (including its underlying feedback or padding scheme)
193     * requires any random bytes, it will get them from <code>random</code>.
194     *
195     * @param opmode the operation mode of this cipher (this is one of
196     * the following:
197     * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
198     * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
199     * @param key the encryption key
200     * @param params the algorithm parameters
201     * @param random the source of randomness
202     *
203     * @exception InvalidKeyException if the given key is inappropriate for
204     * initializing this cipher
205     * @exception InvalidAlgorithmParameterException if the given algorithm
206     * parameters are inappropriate for this cipher
207     */
208    void init(int opmode, Key key, AlgorithmParameterSpec params,
209              SecureRandom random)
210        throws InvalidKeyException, InvalidAlgorithmParameterException {
211        if (((opmode == Cipher.DECRYPT_MODE) ||
212             (opmode == Cipher.UNWRAP_MODE)) && (params == null)) {
213            throw new InvalidAlgorithmParameterException("Parameters "
214                                                         + "missing");
215        }
216        if ((key == null) ||
217            (key.getEncoded() == null) ||
218            !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
219            throw new InvalidKeyException("Missing password");
220        }
221
222        if (params == null) {
223            // create random salt and use default iteration count
224            salt = new byte[8];
225            random.nextBytes(salt);
226        } else {
227            if (!(params instanceof PBEParameterSpec)) {
228                throw new InvalidAlgorithmParameterException
229                    ("Wrong parameter type: PBE expected");
230            }
231            salt = ((PBEParameterSpec) params).getSalt();
232            // salt must be 8 bytes long (by definition)
233            if (salt.length != 8) {
234                throw new InvalidAlgorithmParameterException
235                    ("Salt must be 8 bytes long");
236            }
237            iCount = ((PBEParameterSpec) params).getIterationCount();
238            if (iCount <= 0) {
239                throw new InvalidAlgorithmParameterException
240                    ("IterationCount must be a positive number");
241            }
242        }
243
244        byte[] derivedKey = deriveCipherKey(key);
245        // use all but the last 8 bytes as the key value
246        SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0,
247                                                    derivedKey.length-8, algo);
248        // use the last 8 bytes as the IV
249        IvParameterSpec ivSpec = new IvParameterSpec(derivedKey,
250                                                     derivedKey.length-8,
251                                                     8);
252        // initialize the underlying cipher
253        cipher.init(opmode, cipherKey, ivSpec, random);
254    }
255
256    private byte[] deriveCipherKey(Key key) {
257
258        byte[] result = null;
259        byte[] passwdBytes = key.getEncoded();
260
261        if (algo.equals("DES")) {
262            // P || S (password concatenated with salt)
263            byte[] concat = new byte[passwdBytes.length + salt.length];
264            System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length);
265            java.util.Arrays.fill(passwdBytes, (byte)0x00);
266            System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length);
267
268            // digest P || S with c iterations
269            byte[] toBeHashed = concat;
270            for (int i = 0; i < iCount; i++) {
271                md.update(toBeHashed);
272                toBeHashed = md.digest(); // this resets the digest
273            }
274            java.util.Arrays.fill(concat, (byte)0x00);
275            result = toBeHashed;
276        } else if (algo.equals("DESede")) {
277            // if the 2 salt halves are the same, invert one of them
278            int i;
279            for (i=0; i<4; i++) {
280                if (salt[i] != salt[i+4])
281                    break;
282            }
283            if (i==4) { // same, invert 1st half
284                for (i=0; i<2; i++) {
285                    byte tmp = salt[i];
286                    salt[i] = salt[3-i];
287                    salt[3-1] = tmp;
288                }
289            }
290
291            // Now digest each half (concatenated with password). For each
292            // half, go through the loop as many times as specified by the
293            // iteration count parameter (inner for loop).
294            // Concatenate the output from each digest round with the
295            // password, and use the result as the input to the next digest
296            // operation.
297            byte[] kBytes = null;
298            IvParameterSpec iv = null;
299            byte[] toBeHashed = null;
300            result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN +
301                              DESConstants.DES_BLOCK_SIZE];
302            for (i = 0; i < 2; i++) {
303                toBeHashed = new byte[salt.length/2];
304                System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0,
305                                 toBeHashed.length);
306                for (int j=0; j < iCount; j++) {
307                    md.update(toBeHashed);
308                    md.update(passwdBytes);
309                    toBeHashed = md.digest(); // this resets the digest
310                }
311                System.arraycopy(toBeHashed, 0, result, i*16,
312                                 toBeHashed.length);
313            }
314        }
315        return result;
316    }
317
318    void init(int opmode, Key key, AlgorithmParameters params,
319              SecureRandom random)
320        throws InvalidKeyException, InvalidAlgorithmParameterException {
321        PBEParameterSpec pbeSpec = null;
322        if (params != null) {
323            try {
324                pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
325            } catch (InvalidParameterSpecException ipse) {
326                throw new InvalidAlgorithmParameterException("Wrong parameter "
327                                                             + "type: PBE "
328                                                             + "expected");
329            }
330        }
331        init(opmode, key, pbeSpec, random);
332    }
333
334    /**
335     * Continues a multiple-part encryption or decryption operation
336     * (depending on how this cipher was initialized), processing another data
337     * part.
338     *
339     * <p>The first <code>inputLen</code> bytes in the <code>input</code>
340     * buffer, starting at <code>inputOffset</code>, are processed, and the
341     * result is stored in a new buffer.
342     *
343     * @param input the input buffer
344     * @param inputOffset the offset in <code>input</code> where the input
345     * starts
346     * @param inputLen the input length
347     *
348     * @return the new buffer with the result
349     *
350     */
351    byte[] update(byte[] input, int inputOffset, int inputLen) {
352        return cipher.update(input, inputOffset, inputLen);
353    }
354
355    /**
356     * Continues a multiple-part encryption or decryption operation
357     * (depending on how this cipher was initialized), processing another data
358     * part.
359     *
360     * <p>The first <code>inputLen</code> bytes in the <code>input</code>
361     * buffer, starting at <code>inputOffset</code>, are processed, and the
362     * result is stored in the <code>output</code> buffer, starting at
363     * <code>outputOffset</code>.
364     *
365     * @param input the input buffer
366     * @param inputOffset the offset in <code>input</code> where the input
367     * starts
368     * @param inputLen the input length
369     * @param output the buffer for the result
370     * @param outputOffset the offset in <code>output</code> where the result
371     * is stored
372     *
373     * @return the number of bytes stored in <code>output</code>
374     *
375     * @exception ShortBufferException if the given output buffer is too small
376     * to hold the result
377     */
378    int update(byte[] input, int inputOffset, int inputLen,
379               byte[] output, int outputOffset)
380        throws ShortBufferException {
381        return cipher.update(input, inputOffset, inputLen,
382                             output, outputOffset);
383    }
384
385    /**
386     * Encrypts or decrypts data in a single-part operation,
387     * or finishes a multiple-part operation.
388     * The data is encrypted or decrypted, depending on how this cipher was
389     * initialized.
390     *
391     * <p>The first <code>inputLen</code> bytes in the <code>input</code>
392     * buffer, starting at <code>inputOffset</code>, and any input bytes that
393     * may have been buffered during a previous <code>update</code> operation,
394     * are processed, with padding (if requested) being applied.
395     * The result is stored in a new buffer.
396     *
397     * <p>The cipher is reset to its initial state (uninitialized) after this
398     * call.
399     *
400     * @param input the input buffer
401     * @param inputOffset the offset in <code>input</code> where the input
402     * starts
403     * @param inputLen the input length
404     *
405     * @return the new buffer with the result
406     *
407     * @exception IllegalBlockSizeException if this cipher is a block cipher,
408     * no padding has been requested (only in encryption mode), and the total
409     * input length of the data processed by this cipher is not a multiple of
410     * block size
411     * @exception BadPaddingException if decrypting and padding is chosen,
412     * but the last input data does not have proper padding bytes.
413     */
414    byte[] doFinal(byte[] input, int inputOffset, int inputLen)
415        throws IllegalBlockSizeException, BadPaddingException {
416        return cipher.doFinal(input, inputOffset, inputLen);
417    }
418
419    /**
420     * Encrypts or decrypts data in a single-part operation,
421     * or finishes a multiple-part operation.
422     * The data is encrypted or decrypted, depending on how this cipher was
423     * initialized.
424     *
425     * <p>The first <code>inputLen</code> bytes in the <code>input</code>
426     * buffer, starting at <code>inputOffset</code>, and any input bytes that
427     * may have been buffered during a previous <code>update</code> operation,
428     * are processed, with padding (if requested) being applied.
429     * The result is stored in the <code>output</code> buffer, starting at
430     * <code>outputOffset</code>.
431     *
432     * <p>The cipher is reset to its initial state (uninitialized) after this
433     * call.
434     *
435     * @param input the input buffer
436     * @param inputOffset the offset in <code>input</code> where the input
437     * starts
438     * @param inputLen the input length
439     * @param output the buffer for the result
440     * @param outputOffset the offset in <code>output</code> where the result
441     * is stored
442     *
443     * @return the number of bytes stored in <code>output</code>
444     *
445     * @exception IllegalBlockSizeException if this cipher is a block cipher,
446     * no padding has been requested (only in encryption mode), and the total
447     * input length of the data processed by this cipher is not a multiple of
448     * block size
449     * @exception ShortBufferException if the given output buffer is too small
450     * to hold the result
451     * @exception BadPaddingException if decrypting and padding is chosen,
452     * but the last input data does not have proper padding bytes.
453     */
454    int doFinal(byte[] input, int inputOffset, int inputLen,
455                byte[] output, int outputOffset)
456        throws ShortBufferException, IllegalBlockSizeException,
457               BadPaddingException {
458        return cipher.doFinal(input, inputOffset, inputLen,
459                                    output, outputOffset);
460    }
461
462    /**
463     * Wrap a key.
464     *
465     * @param key the key to be wrapped.
466     *
467     * @return the wrapped key.
468     *
469     * @exception IllegalBlockSizeException if this cipher is a block
470     * cipher, no padding has been requested, and the length of the
471     * encoding of the key to be wrapped is not a
472     * multiple of the block size.
473     *
474     * @exception InvalidKeyException if it is impossible or unsafe to
475     * wrap the key with this cipher (e.g., a hardware protected key is
476     * being passed to a software only cipher).
477     */
478    byte[] wrap(Key key)
479        throws IllegalBlockSizeException, InvalidKeyException {
480        byte[] result = null;
481
482        try {
483            byte[] encodedKey = key.getEncoded();
484            if ((encodedKey == null) || (encodedKey.length == 0)) {
485                throw new InvalidKeyException("Cannot get an encoding of " +
486                                              "the key to be wrapped");
487            }
488
489            result = doFinal(encodedKey, 0, encodedKey.length);
490        } catch (BadPaddingException e) {
491            // Should never happen
492        }
493
494        return result;
495    }
496
497    /**
498     * Unwrap a previously wrapped key.
499     *
500     * @param wrappedKey the key to be unwrapped.
501     *
502     * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
503     *
504     * @param wrappedKeyType the type of the wrapped key.
505     * This is one of <code>Cipher.SECRET_KEY</code>,
506     * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
507     *
508     * @return the unwrapped key.
509     *
510     * @exception NoSuchAlgorithmException if no installed providers
511     * can create keys of type <code>wrappedKeyType</code> for the
512     * <code>wrappedKeyAlgorithm</code>.
513     *
514     * @exception InvalidKeyException if <code>wrappedKey</code> does not
515     * represent a wrapped key of type <code>wrappedKeyType</code> for
516     * the <code>wrappedKeyAlgorithm</code>.
517     */
518    Key unwrap(byte[] wrappedKey,
519               String wrappedKeyAlgorithm,
520               int wrappedKeyType)
521        throws InvalidKeyException, NoSuchAlgorithmException {
522        byte[] encodedKey;
523        try {
524            encodedKey = doFinal(wrappedKey, 0, wrappedKey.length);
525        } catch (BadPaddingException ePadding) {
526            throw new InvalidKeyException("The wrapped key is not padded " +
527                                          "correctly");
528        } catch (IllegalBlockSizeException eBlockSize) {
529            throw new InvalidKeyException("The wrapped key does not have " +
530                                          "the correct length");
531        }
532        return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm,
533                                          wrappedKeyType);
534    }
535}
536