1/*
2 * Copyright (c) 2004, 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 implements the CMS DESede KeyWrap algorithm as defined
35 * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap></a>
36 * "XML Encryption Syntax and Processing" section 5.6.2
37 * "CMS Triple DES Key Wrap".
38 * Note: only <code>CBC</code> mode and <code>NoPadding</code> padding
39 * scheme can be used for this algorithm.
40 *
41 * @author Valerie Peng
42 *
43 *
44 * @see DESedeCipher
45 */
46public final class DESedeWrapCipher extends CipherSpi {
47
48    private static final byte[] IV2 = {
49        (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c,
50        (byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x05
51    };
52
53    private static final int CHECKSUM_LEN = 8;
54    private static final int IV_LEN = 8;
55
56    /*
57     * internal cipher object which does the real work.
58     */
59    private FeedbackCipher cipher;
60
61    /*
62     * iv for (re-)initializing the internal cipher object.
63     */
64    private byte[] iv = null;
65
66    /*
67     * key for re-initializing the internal cipher object.
68     */
69    private Key cipherKey = null;
70
71    /*
72     * are we encrypting or decrypting?
73     */
74    private boolean decrypting = false;
75
76    /**
77     * Creates an instance of CMS DESede KeyWrap cipher with default
78     * mode, i.e. "CBC" and padding scheme, i.e. "NoPadding".
79     */
80    public DESedeWrapCipher() {
81        cipher = new CipherBlockChaining(new DESedeCrypt());
82    }
83
84    /**
85     * Sets the mode of this cipher. Only "CBC" mode is accepted for this
86     * cipher.
87     *
88     * @param mode the cipher mode.
89     *
90     * @exception NoSuchAlgorithmException if the requested cipher mode
91     * is not "CBC".
92     */
93    protected void engineSetMode(String mode)
94        throws NoSuchAlgorithmException {
95        if (!mode.equalsIgnoreCase("CBC")) {
96            throw new NoSuchAlgorithmException(mode + " cannot be used");
97        }
98    }
99
100    /**
101     * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
102     * is accepted for this cipher.
103     *
104     * @param padding the padding mechanism.
105     *
106     * @exception NoSuchPaddingException if the requested padding mechanism
107     * is not "NoPadding".
108     */
109    protected void engineSetPadding(String padding)
110        throws NoSuchPaddingException {
111        if (!padding.equalsIgnoreCase("NoPadding")) {
112            throw new NoSuchPaddingException(padding + " cannot be used");
113        }
114    }
115
116    /**
117     * Returns the block size (in bytes), i.e. 8 bytes.
118     *
119     * @return the block size (in bytes), i.e. 8 bytes.
120     */
121    protected int engineGetBlockSize() {
122        return DESConstants.DES_BLOCK_SIZE;
123    }
124
125    /**
126     * Returns the length in bytes that an output buffer would need to be
127     * given the input length <code>inputLen</code> (in bytes).
128     *
129     * <p>The actual output length of the next <code>update</code> or
130     * <code>doFinal</code> call may be smaller than the length returned
131     * by this method.
132     *
133     * @param inputLen the input length (in bytes).
134     *
135     * @return the required output buffer size (in bytes).
136     */
137    protected int engineGetOutputSize(int inputLen) {
138        // can only return an upper-limit if not initialized yet.
139        int result = 0;
140        if (decrypting) {
141            result = inputLen - 16; // CHECKSUM_LEN + IV_LEN;
142        } else {
143            result = inputLen + 16;
144        }
145        return (result < 0? 0:result);
146    }
147
148    /**
149     * Returns the initialization vector (IV) in a new buffer.
150     *
151     * @return the initialization vector, or null if the underlying
152     * algorithm does not use an IV, or if the IV has not yet
153     * been set.
154     */
155    protected byte[] engineGetIV() {
156        return (iv == null) ? null : iv.clone();
157    }
158
159    /**
160     * Initializes this cipher with a key and a source of randomness.
161     *
162     * <p>The cipher only supports the following two operation modes:
163     * {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.
164     * <p>For modes other than the above two, UnsupportedOperationException
165     * will be thrown.
166     * <p>If this cipher requires an initialization vector (IV), it will get
167     * it from <code>random</code>.
168     *
169     * @param opmode the operation mode of this cipher. Only
170     * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
171     * @param key the secret key.
172     * @param random the source of randomness.
173     *
174     * @exception InvalidKeyException if the given key is inappropriate
175     * or if parameters are required but not supplied.
176     */
177    protected void engineInit(int opmode, Key key, SecureRandom random)
178        throws InvalidKeyException {
179        try {
180            engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
181        } catch (InvalidAlgorithmParameterException iape) {
182            // should never happen
183            InvalidKeyException ike =
184                new InvalidKeyException("Parameters required");
185            ike.initCause(iape);
186            throw ike;
187        }
188    }
189
190    /**
191     * Initializes this cipher with a key, a set of algorithm parameters,
192     * and a source of randomness.
193     *
194     * <p>The cipher only supports the following two operation modes:
195     * {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.
196     * <p>For modes other than the above two, UnsupportedOperationException
197     * will be thrown.
198     * <p>If this cipher requires an initialization vector (IV), it will get
199     * it from <code>random</code>.
200     *
201     * @param opmode the operation mode of this cipher. Only
202     * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
203     * @param key the secret key.
204     * @param params the algorithm parameters.
205     * @param random the source of randomness.
206     *
207     * @exception InvalidKeyException if the given key is inappropriate.
208     * @exception InvalidAlgorithmParameterException if the given algorithm
209     * parameters are inappropriate for this cipher.
210     */
211    protected void engineInit(int opmode, Key key,
212                              AlgorithmParameterSpec params,
213                              SecureRandom random)
214        throws InvalidKeyException, InvalidAlgorithmParameterException {
215        byte[] currIv = null;
216        if (opmode == Cipher.WRAP_MODE) {
217            decrypting = false;
218            if (params == null) {
219                iv = new byte[IV_LEN];
220                if (random == null) {
221                    random = SunJCE.getRandom();
222                }
223                random.nextBytes(iv);
224            }
225            else if (params instanceof IvParameterSpec) {
226                iv = ((IvParameterSpec) params).getIV();
227            } else {
228                throw new InvalidAlgorithmParameterException
229                    ("Wrong parameter type: IV expected");
230            }
231            currIv = iv;
232        } else if (opmode == Cipher.UNWRAP_MODE) {
233            if (params != null) {
234                throw new InvalidAlgorithmParameterException
235                    ("No parameter accepted for unwrapping keys");
236            }
237            iv = null;
238            decrypting = true;
239            currIv = IV2;
240        } else {
241            throw new UnsupportedOperationException("This cipher can " +
242                "only be used for key wrapping and unwrapping");
243        }
244        cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(),
245                    currIv);
246        cipherKey = key;
247    }
248
249    /**
250     * Initializes this cipher with a key, a set of algorithm parameters,
251     * and a source of randomness.
252     *
253     * <p>The cipher only supports the following two operation modes:
254     * {@code Cipher.WRAP_MODE}, and {@code Cipher.UNWRAP_MODE}.
255     * <p>For modes other than the above two, UnsupportedOperationException
256     * will be thrown.
257     * <p>If this cipher requires an initialization vector (IV), it will get
258     * it from <code>random</code>.
259     *
260     * @param opmode the operation mode of this cipher. Only
261     * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
262     * @param key the secret key.
263     * @param params the algorithm parameters.
264     * @param random the source of randomness.
265     *
266     * @exception InvalidKeyException if the given key is inappropriate.
267     * @exception InvalidAlgorithmParameterException if the given algorithm
268     * parameters are inappropriate for this cipher.
269     */
270    protected void engineInit(int opmode, Key key,
271                              AlgorithmParameters params,
272                              SecureRandom random)
273        throws InvalidKeyException, InvalidAlgorithmParameterException {
274        IvParameterSpec ivSpec = null;
275        if (params != null) {
276            try {
277                DESedeParameters paramsEng = new DESedeParameters();
278                paramsEng.engineInit(params.getEncoded());
279                ivSpec = paramsEng.engineGetParameterSpec(IvParameterSpec.class);
280            } catch (Exception ex) {
281                InvalidAlgorithmParameterException iape =
282                    new InvalidAlgorithmParameterException
283                        ("Wrong parameter type: IV expected");
284                iape.initCause(ex);
285                throw iape;
286            }
287        }
288        engineInit(opmode, key, ivSpec, random);
289    }
290
291    /**
292     * This operation is not supported by this cipher.
293     * Since it's impossible to initialize this cipher given the
294     * current Cipher.engineInit(...) implementation,
295     * IllegalStateException will always be thrown upon invocation.
296     *
297     * @param in the input buffer.
298     * @param inOffset the offset in <code>in</code> where the input
299     * starts.
300     * @param inLen the input length.
301     *
302     * @return n/a.
303     *
304     * @exception IllegalStateException upon invocation of this method.
305     */
306    protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
307        throw new IllegalStateException("Cipher has not been initialized");
308    }
309
310    /**
311     * This operation is not supported by this cipher.
312     * Since it's impossible to initialize this cipher given the
313     * current Cipher.engineInit(...) implementation,
314     * IllegalStateException will always be thrown upon invocation.
315     *
316     * @param in the input buffer.
317     * @param inOffset the offset in <code>in</code> where the input
318     * starts.
319     * @param inLen the input length.
320     * @param out the buffer for the result.
321     * @param outOffset the offset in <code>out</code> where the result
322     * is stored.
323     *
324     * @return n/a.
325     *
326     * @exception IllegalStateException upon invocation of this method.
327     */
328    protected int engineUpdate(byte[] in, int inOffset, int inLen,
329                               byte[] out, int outOffset)
330        throws ShortBufferException {
331        throw new IllegalStateException("Cipher has not been initialized");
332    }
333
334    /**
335     * This operation is not supported by this cipher.
336     * Since it's impossible to initialize this cipher given the
337     * current Cipher.engineInit(...) implementation,
338     * IllegalStateException will always be thrown upon invocation.
339     *
340     * @param in the input buffer.
341     * @param inOffset the offset in <code>in</code> where the input
342     * starts.
343     * @param inLen the input length.
344     *
345     * @return the new buffer with the result.
346     *
347     * @exception IllegalStateException upon invocation of this method.
348     */
349    protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen)
350        throws IllegalBlockSizeException, BadPaddingException {
351        throw new IllegalStateException("Cipher has not been initialized");
352    }
353
354    /**
355     * This operation is not supported by this cipher.
356     * Since it's impossible to initialize this cipher given the
357     * current Cipher.engineInit(...) implementation,
358     * IllegalStateException will always be thrown upon invocation.
359     *
360     * @param input the input buffer.
361     * @param inputOffset the offset in {@code input} where the input
362     * starts.
363     * @param inputLen the input length.
364     * @param output the buffer for the result.
365     * @param outputOffset the ofset in {@code output} where the result
366     * is stored.
367     *
368     * @return the number of bytes stored in {@code out}.
369     *
370     * @exception IllegalStateException upon invocation of this method.
371     */
372    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
373                                byte[] output, int outputOffset)
374        throws IllegalBlockSizeException, ShortBufferException,
375               BadPaddingException {
376        throw new IllegalStateException("Cipher has not been initialized");
377    }
378
379    /**
380     * Returns the parameters used with this cipher.
381     * Note that null maybe returned if this cipher does not use any
382     * parameters or when it has not be set, e.g. initialized with
383     * UNWRAP_MODE but wrapped key data has not been given.
384     *
385     * @return the parameters used with this cipher; can be null.
386     */
387    protected AlgorithmParameters engineGetParameters() {
388        AlgorithmParameters params = null;
389        if (iv != null) {
390            String algo = cipherKey.getAlgorithm();
391            try {
392                params = AlgorithmParameters.getInstance(algo,
393                    SunJCE.getInstance());
394                params.init(new IvParameterSpec(iv));
395            } catch (NoSuchAlgorithmException nsae) {
396                // should never happen
397                throw new RuntimeException("Cannot find " + algo +
398                    " AlgorithmParameters implementation in SunJCE provider");
399            } catch (InvalidParameterSpecException ipse) {
400                // should never happen
401                throw new RuntimeException("IvParameterSpec not supported");
402            }
403        }
404        return params;
405    }
406
407    /**
408     * Returns the key size of the given key object in number of bits.
409     * This cipher always return the same key size as the DESede ciphers.
410     *
411     * @param key the key object.
412     *
413     * @return the "effective" key size of the given key object.
414     *
415     * @exception InvalidKeyException if <code>key</code> is invalid.
416     */
417    protected int engineGetKeySize(Key key) throws InvalidKeyException {
418        byte[] encoded = key.getEncoded();
419        if (encoded.length != 24) {
420            throw new InvalidKeyException("Invalid key length: " +
421                encoded.length + " bytes");
422        }
423        // Return the effective key length
424        return 112;
425    }
426
427    /**
428     * Wrap a key.
429     *
430     * @param key the key to be wrapped.
431     *
432     * @return the wrapped key.
433     *
434     * @exception IllegalBlockSizeException if this cipher is a block
435     * cipher, no padding has been requested, and the length of the
436     * encoding of the key to be wrapped is not a
437     * multiple of the block size.
438     *
439     * @exception InvalidKeyException if it is impossible or unsafe to
440     * wrap the key with this cipher (e.g., a hardware protected key is
441     * being passed to a software only cipher).
442     */
443    protected byte[] engineWrap(Key key)
444        throws IllegalBlockSizeException, InvalidKeyException {
445        byte[] keyVal = key.getEncoded();
446        if ((keyVal == null) || (keyVal.length == 0)) {
447            throw new InvalidKeyException("Cannot get an encoding of " +
448                                          "the key to be wrapped");
449        }
450
451        byte[] cks = getChecksum(keyVal);
452        byte[] in = new byte[keyVal.length + CHECKSUM_LEN];
453        System.arraycopy(keyVal, 0, in, 0, keyVal.length);
454        System.arraycopy(cks, 0, in, keyVal.length, CHECKSUM_LEN);
455
456        byte[] out = new byte[iv.length + in.length];
457        System.arraycopy(iv, 0, out, 0, iv.length);
458
459        cipher.encrypt(in, 0, in.length, out, iv.length);
460
461        // reverse the array content
462        for (int i = 0; i < out.length/2; i++) {
463            byte temp = out[i];
464            out[i] = out[out.length-1-i];
465            out[out.length-1-i] = temp;
466        }
467        try {
468            cipher.init(false, cipherKey.getAlgorithm(),
469                        cipherKey.getEncoded(), IV2);
470        } catch (InvalidKeyException ike) {
471            // should never happen
472            throw new RuntimeException("Internal cipher key is corrupted");
473        }
474        byte[] out2 = new byte[out.length];
475        cipher.encrypt(out, 0, out.length, out2, 0);
476
477        // restore cipher state to prior to this call
478        try {
479            cipher.init(decrypting, cipherKey.getAlgorithm(),
480                        cipherKey.getEncoded(), iv);
481        } catch (InvalidKeyException ike) {
482            // should never happen
483            throw new RuntimeException("Internal cipher key is corrupted");
484        }
485        return out2;
486    }
487
488    /**
489     * Unwrap a previously wrapped key.
490     *
491     * @param wrappedKey the key to be unwrapped.
492     *
493     * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
494     *
495     * @param wrappedKeyType the type of the wrapped key.
496     * This is one of <code>Cipher.SECRET_KEY</code>,
497     * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
498     *
499     * @return the unwrapped key.
500     *
501     * @exception NoSuchAlgorithmException if no installed providers
502     * can create keys of type <code>wrappedKeyType</code> for the
503     * <code>wrappedKeyAlgorithm</code>.
504     *
505     * @exception InvalidKeyException if <code>wrappedKey</code> does not
506     * represent a wrapped key of type <code>wrappedKeyType</code> for
507     * the <code>wrappedKeyAlgorithm</code>.
508     */
509    protected Key engineUnwrap(byte[] wrappedKey,
510                               String wrappedKeyAlgorithm,
511                               int wrappedKeyType)
512        throws InvalidKeyException, NoSuchAlgorithmException {
513        if (wrappedKey.length == 0) {
514            throw new InvalidKeyException("The wrapped key is empty");
515        }
516        byte[] buffer = new byte[wrappedKey.length];
517        cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);
518
519        // reverse array content
520        for (int i = 0; i < buffer.length/2; i++) {
521            byte temp = buffer[i];
522            buffer[i] = buffer[buffer.length-1-i];
523            buffer[buffer.length-1-i] = temp;
524        }
525        iv = new byte[IV_LEN];
526        System.arraycopy(buffer, 0, iv, 0, iv.length);
527        cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
528                    iv);
529        byte[] buffer2 = new byte[buffer.length - iv.length];
530        cipher.decrypt(buffer, iv.length, buffer2.length,
531                       buffer2, 0);
532        int keyValLen = buffer2.length - CHECKSUM_LEN;
533        byte[] cks = getChecksum(buffer2, 0, keyValLen);
534        int offset = keyValLen;
535        for (int i = 0; i < CHECKSUM_LEN; i++) {
536            if (buffer2[offset + i] != cks[i]) {
537                throw new InvalidKeyException("Checksum comparison failed");
538            }
539        }
540        // restore cipher state to prior to this call
541        cipher.init(decrypting, cipherKey.getAlgorithm(),
542                    cipherKey.getEncoded(), IV2);
543        byte[] out = new byte[keyValLen];
544        System.arraycopy(buffer2, 0, out, 0, keyValLen);
545        return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
546                                          wrappedKeyType);
547    }
548
549    private static final byte[] getChecksum(byte[] in) {
550        return getChecksum(in, 0, in.length);
551    }
552    private static final byte[] getChecksum(byte[] in, int offset, int len) {
553        MessageDigest md = null;
554        try {
555            md = MessageDigest.getInstance("SHA1");
556        } catch (NoSuchAlgorithmException nsae) {
557            throw new RuntimeException("SHA1 message digest not available");
558        }
559        md.update(in, offset, len);
560        byte[] cks = new byte[CHECKSUM_LEN];
561        System.arraycopy(md.digest(), 0, cks, 0, cks.length);
562        return cks;
563    }
564}
565