1 /*
2  * Copyright (c) 2004, 2017, 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 
26 package com.sun.crypto.provider;
27 
28 import java.security.*;
29 import java.security.spec.*;
30 import javax.crypto.*;
31 import 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  */
46 public 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      */
DESedeWrapCipher()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      */
engineSetMode(String mode)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      */
engineSetPadding(String padding)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      */
engineGetBlockSize()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      */
engineGetOutputSize(int inputLen)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 = Math.addExact(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      */
engineGetIV()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      */
engineInit(int opmode, Key key, SecureRandom random)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      */
engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)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      */
engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)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      */
engineUpdate(byte[] in, int inOffset, int inLen)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      */
engineUpdate(byte[] in, int inOffset, int inLen, byte[] out, int outOffset)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      */
engineDoFinal(byte[] in, int inOffset, int inLen)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      */
engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)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      */
engineGetParameters()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      */
engineGetKeySize(Key key)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      */
engineWrap(Key key)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[Math.addExact(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[Math.addExact(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         } catch (InvalidAlgorithmParameterException iape) {
474             // should never happen
475             throw new RuntimeException("Internal cipher IV is invalid");
476         }
477         byte[] out2 = new byte[out.length];
478         cipher.encrypt(out, 0, out.length, out2, 0);
479 
480         // restore cipher state to prior to this call
481         try {
482             cipher.init(decrypting, cipherKey.getAlgorithm(),
483                         cipherKey.getEncoded(), iv);
484         } catch (InvalidKeyException ike) {
485             // should never happen
486             throw new RuntimeException("Internal cipher key is corrupted");
487         } catch (InvalidAlgorithmParameterException iape) {
488             // should never happen
489             throw new RuntimeException("Internal cipher IV is invalid");
490         }
491         return out2;
492     }
493 
494     /**
495      * Unwrap a previously wrapped key.
496      *
497      * @param wrappedKey the key to be unwrapped.
498      *
499      * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
500      *
501      * @param wrappedKeyType the type of the wrapped key.
502      * This is one of <code>Cipher.SECRET_KEY</code>,
503      * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
504      *
505      * @return the unwrapped key.
506      *
507      * @exception NoSuchAlgorithmException if no installed providers
508      * can create keys of type <code>wrappedKeyType</code> for the
509      * <code>wrappedKeyAlgorithm</code>.
510      *
511      * @exception InvalidKeyException if <code>wrappedKey</code> does not
512      * represent a wrapped key of type <code>wrappedKeyType</code> for
513      * the <code>wrappedKeyAlgorithm</code>.
514      */
engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)515     protected Key engineUnwrap(byte[] wrappedKey,
516                                String wrappedKeyAlgorithm,
517                                int wrappedKeyType)
518         throws InvalidKeyException, NoSuchAlgorithmException {
519         if (wrappedKey.length == 0) {
520             throw new InvalidKeyException("The wrapped key is empty");
521         }
522         byte[] buffer = new byte[wrappedKey.length];
523         cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);
524 
525         // reverse array content
526         for (int i = 0; i < buffer.length/2; i++) {
527             byte temp = buffer[i];
528             buffer[i] = buffer[buffer.length-1-i];
529             buffer[buffer.length-1-i] = temp;
530         }
531         iv = new byte[IV_LEN];
532         System.arraycopy(buffer, 0, iv, 0, iv.length);
533         try {
534             cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
535                     iv);
536         } catch (InvalidAlgorithmParameterException iape) {
537             throw new InvalidKeyException("IV in wrapped key is invalid");
538         }
539         byte[] buffer2 = new byte[buffer.length - iv.length];
540         cipher.decrypt(buffer, iv.length, buffer2.length,
541                        buffer2, 0);
542         int keyValLen = buffer2.length - CHECKSUM_LEN;
543         byte[] cks = getChecksum(buffer2, 0, keyValLen);
544         int offset = keyValLen;
545         for (int i = 0; i < CHECKSUM_LEN; i++) {
546             if (buffer2[offset + i] != cks[i]) {
547                 throw new InvalidKeyException("Checksum comparison failed");
548             }
549         }
550         // restore cipher state to prior to this call
551         try {
552           cipher.init(decrypting, cipherKey.getAlgorithm(),
553                     cipherKey.getEncoded(), IV2);
554         } catch (InvalidAlgorithmParameterException iape) {
555             throw new InvalidKeyException("IV in wrapped key is invalid");
556         }
557         byte[] out = new byte[keyValLen];
558         System.arraycopy(buffer2, 0, out, 0, keyValLen);
559         return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
560                                           wrappedKeyType);
561     }
562 
getChecksum(byte[] in)563     private static final byte[] getChecksum(byte[] in) {
564         return getChecksum(in, 0, in.length);
565     }
getChecksum(byte[] in, int offset, int len)566     private static final byte[] getChecksum(byte[] in, int offset, int len) {
567         MessageDigest md = null;
568         try {
569             md = MessageDigest.getInstance("SHA1");
570         } catch (NoSuchAlgorithmException nsae) {
571             throw new RuntimeException("SHA1 message digest not available");
572         }
573         md.update(in, offset, len);
574         byte[] cks = new byte[CHECKSUM_LEN];
575         System.arraycopy(md.digest(), 0, cks, 0, cks.length);
576         return cks;
577     }
578 }
579