1 /*
2  * Copyright (c) 2004, 2009, 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 sun.security.krb5.internal.crypto.dk;
27 
28 import javax.crypto.Cipher;
29 import javax.crypto.Mac;
30 import javax.crypto.SecretKeyFactory;
31 import javax.crypto.SecretKey;
32 import javax.crypto.spec.SecretKeySpec;
33 import javax.crypto.spec.DESKeySpec;
34 import javax.crypto.spec.DESedeKeySpec;
35 import javax.crypto.spec.IvParameterSpec;
36 import java.security.spec.KeySpec;
37 import java.security.GeneralSecurityException;
38 import java.security.InvalidKeyException;
39 import java.util.Arrays;
40 
41 public class Des3DkCrypto extends DkCrypto {
42 
43     private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0};
44 
Des3DkCrypto()45     public Des3DkCrypto() {
46     }
47 
getKeySeedLength()48     protected int getKeySeedLength() {
49         return 168;   // bits; 3DES key material has 21 bytes
50     }
51 
stringToKey(char[] salt)52     public byte[] stringToKey(char[] salt) throws GeneralSecurityException {
53         byte[] saltUtf8 = null;
54         try {
55             saltUtf8 = charToUtf8(salt);
56             return stringToKey(saltUtf8, null);
57         } finally {
58             if (saltUtf8 != null) {
59                 Arrays.fill(saltUtf8, (byte)0);
60             }
61             // Caller responsible for clearing its own salt
62         }
63     }
64 
stringToKey(byte[] secretAndSalt, byte[] opaque)65     private byte[] stringToKey(byte[] secretAndSalt, byte[] opaque)
66         throws GeneralSecurityException {
67 
68         if (opaque != null && opaque.length > 0) {
69             throw new RuntimeException("Invalid parameter to stringToKey");
70         }
71 
72         byte[] tmpKey = randomToKey(nfold(secretAndSalt, getKeySeedLength()));
73         return dk(tmpKey, KERBEROS_CONSTANT);
74     }
75 
parityFix(byte[] value)76     public byte[] parityFix(byte[] value)
77         throws GeneralSecurityException {
78         // fix key parity
79         setParityBit(value);
80         return value;
81     }
82 
83     /*
84      * From RFC 3961.
85      *
86      * The 168 bits of random key data are converted to a protocol key value
87      * as follows.  First, the 168 bits are divided into three groups of 56
88      * bits, which are expanded individually into 64 bits as in des3Expand().
89      * Result is a 24 byte (192-bit) key.
90      */
randomToKey(byte[] in)91     protected byte[] randomToKey(byte[] in) {
92         if (in.length != 21) {
93             throw new IllegalArgumentException("input must be 168 bits");
94         }
95 
96         byte[] one = keyCorrection(des3Expand(in, 0, 7));
97         byte[] two = keyCorrection(des3Expand(in, 7, 14));
98         byte[] three = keyCorrection(des3Expand(in, 14, 21));
99 
100         byte[] key = new byte[24];
101         System.arraycopy(one, 0, key, 0, 8);
102         System.arraycopy(two, 0, key, 8, 8);
103         System.arraycopy(three, 0, key, 16, 8);
104 
105         return key;
106     }
107 
keyCorrection(byte[] key)108     private static byte[] keyCorrection(byte[] key) {
109         // check for weak key
110         try {
111             if (DESKeySpec.isWeak(key, 0)) {
112                 key[7] = (byte)(key[7] ^ 0xF0);
113             }
114         } catch (InvalidKeyException ex) {
115             // swallow, since it should never happen
116         }
117         return key;
118     }
119 
120     /**
121      * From RFC 3961.
122      *
123      * Expands a 7-byte array into an 8-byte array that contains parity bits.
124      * The 56 bits are expanded into 64 bits as follows:
125      *   1  2  3  4  5  6  7  p
126      *   9 10 11 12 13 14 15  p
127      *   17 18 19 20 21 22 23  p
128      *   25 26 27 28 29 30 31  p
129      *   33 34 35 36 37 38 39  p
130      *   41 42 43 44 45 46 47  p
131      *   49 50 51 52 53 54 55  p
132      *   56 48 40 32 24 16  8  p
133      *
134      * (PI,P2,...,P8) are reserved for parity bits computed on the preceding
135      * seven independent bits and set so that the parity of the octet is odd,
136      * i.e., there is an odd number of "1" bits in the octet.
137      *
138      * @param start index of starting byte (inclusive)
139      * @param end index of ending byte (exclusive)
140      */
des3Expand(byte[] input, int start, int end)141     private static byte[] des3Expand(byte[] input, int start, int end) {
142         if ((end - start) != 7)
143             throw new IllegalArgumentException(
144                 "Invalid length of DES Key Value:" + start + "," + end);
145 
146         byte[] result = new byte[8];
147         byte last = 0;
148         System.arraycopy(input, start, result, 0, 7);
149         byte posn = 0;
150 
151         // Fill in last row
152         for (int i = start; i < end; i++) {
153             byte bit = (byte) (input[i]&0x01);
154             if (debug) {
155                 System.out.println(i + ": " + Integer.toHexString(input[i]) +
156                     " bit= " + Integer.toHexString(bit));
157             }
158             ++posn;
159             if (bit != 0) {
160                 last |= (bit<<posn);
161             }
162         }
163 
164         if (debug) {
165             System.out.println("last: " + Integer.toHexString(last));
166         }
167         result[7] = last;
168         setParityBit(result);
169         return result;
170     }
171 
172     /**
173      * Sets the parity bit (0th bit) in each byte so that each byte
174      * contains an odd number of 1's.
175      */
setParityBit(byte[] key)176     private static void setParityBit(byte[] key) {
177         for (int i = 0; i < key.length; i++) {
178             int b = key[i] & 0xfe;
179             b |= (Integer.bitCount(b) & 1) ^ 1;
180             key[i] = (byte) b;
181         }
182     }
183 
getCipher(byte[] key, byte[] ivec, int mode)184     protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
185         throws GeneralSecurityException {
186         // NoSuchAlgorithException
187         SecretKeyFactory factory = SecretKeyFactory.getInstance("desede");
188 
189         // InvalidKeyException
190         KeySpec spec = new DESedeKeySpec(key, 0);
191 
192         // InvalidKeySpecException
193         SecretKey secretKey = factory.generateSecret(spec);
194 
195         // IV
196         if (ivec == null) {
197             ivec = ZERO_IV;
198         }
199 
200         // NoSuchAlgorithmException, NoSuchPaddingException
201         // NoSuchProviderException
202         Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
203         IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
204 
205         // InvalidKeyException, InvalidAlgorithParameterException
206         cipher.init(mode, secretKey, encIv);
207 
208         return cipher;
209     }
210 
getChecksumLength()211     public int getChecksumLength() {
212         return 20;  // bytes
213     }
214 
getHmac(byte[] key, byte[] msg)215     protected byte[] getHmac(byte[] key, byte[] msg)
216         throws GeneralSecurityException {
217 
218         SecretKey keyKi = new SecretKeySpec(key, "HmacSHA1");
219         Mac m = Mac.getInstance("HmacSHA1");
220         m.init(keyKi);
221         return m.doFinal(msg);
222     }
223 }
224