1 /*
2  * Copyright (c) 2004, 2019, 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 /*
27  */
28 
29 package sun.security.krb5.internal.crypto.dk;
30 
31 import javax.crypto.Cipher;
32 import javax.crypto.Mac;
33 import javax.crypto.SecretKeyFactory;
34 import javax.crypto.SecretKey;
35 import javax.crypto.spec.SecretKeySpec;
36 import javax.crypto.spec.DESedeKeySpec;
37 import javax.crypto.spec.IvParameterSpec;
38 import javax.crypto.spec.PBEKeySpec;
39 import java.security.spec.KeySpec;
40 import java.security.GeneralSecurityException;
41 import sun.security.krb5.KrbCryptoException;
42 import sun.security.krb5.Confounder;
43 import sun.security.krb5.internal.crypto.KeyUsage;
44 import java.util.Arrays;
45 
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 
48 /**
49  * This class provides the implementation of AES Encryption for Kerberos
50  * as defined RFC 3962.
51  * http://www.ietf.org/rfc/rfc3962.txt
52  *
53  * Algorithm profile described in [KCRYPTO]:
54  * +--------------------------------------------------------------------+
55  * |               protocol key format          128- or 256-bit string  |
56  * |                                                                    |
57  * |            string-to-key function          PBKDF2+DK with variable |
58  * |                                          iteration count (see      |
59  * |                                          above)                    |
60  * |                                                                    |
61  * |  default string-to-key parameters          00 00 10 00             |
62  * |                                                                    |
63  * |        key-generation seed length          key size                |
64  * |                                                                    |
65  * |            random-to-key function          identity function       |
66  * |                                                                    |
67  * |                    hash function, H                SHA-1           |
68  * |                                                                    |
69  * |               HMAC output size, h          12 octets (96 bits)     |
70  * |                                                                    |
71  * |             message block size, m          1 octet                 |
72  * |                                                                    |
73  * |  encryption/decryption functions,          AES in CBC-CTS mode     |
74  * |  E and D                                 (cipher block size 16     |
75  * |                                          octets), with next to     |
76  * |                                          last block as CBC-style   |
77  * |                                          ivec                      |
78  * +--------------------------------------------------------------------+
79  *
80  * Supports AES128 and AES256
81  *
82  * @author Seema Malkani
83  */
84 
85 public class AesDkCrypto extends DkCrypto {
86 
87     private static final boolean debug = false;
88 
89     private static final int BLOCK_SIZE = 16;
90     private static final int DEFAULT_ITERATION_COUNT = 4096;
91     private static final byte[] ZERO_IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0,
92                                                        0, 0, 0, 0, 0, 0, 0, 0 };
93     private static final int hashSize = 96/8;
94     private final int keyLength;
95 
AesDkCrypto(int length)96     public AesDkCrypto(int length) {
97         keyLength = length;
98     }
99 
getKeySeedLength()100     protected int getKeySeedLength() {
101         return keyLength;   // bits; AES key material
102     }
103 
stringToKey(char[] password, String salt, byte[] s2kparams)104     public byte[] stringToKey(char[] password, String salt, byte[] s2kparams)
105         throws GeneralSecurityException {
106 
107         byte[] saltUtf8 = null;
108         try {
109             saltUtf8 = salt.getBytes(UTF_8);
110             return stringToKey(password, saltUtf8, s2kparams);
111         } catch (Exception e) {
112             return null;
113         } finally {
114             if (saltUtf8 != null) {
115                 Arrays.fill(saltUtf8, (byte)0);
116             }
117         }
118     }
119 
stringToKey(char[] secret, byte[] salt, byte[] params)120     private byte[] stringToKey(char[] secret, byte[] salt, byte[] params)
121         throws GeneralSecurityException {
122 
123         int iter_count = DEFAULT_ITERATION_COUNT;
124         if (params != null) {
125             if (params.length != 4) {
126                 throw new RuntimeException("Invalid parameter to stringToKey");
127             }
128             iter_count = readBigEndian(params, 0, 4);
129         }
130 
131         byte[] tmpKey = randomToKey(PBKDF2(secret, salt, iter_count,
132                                         getKeySeedLength()));
133         byte[] result = dk(tmpKey, KERBEROS_CONSTANT);
134         return result;
135     }
136 
randomToKey(byte[] in)137     protected byte[] randomToKey(byte[] in) {
138         // simple identity operation
139         return in;
140     }
141 
getCipher(byte[] key, byte[] ivec, int mode)142     protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
143         throws GeneralSecurityException {
144 
145         // IV
146         if (ivec == null) {
147            ivec = ZERO_IV;
148         }
149         SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
150         Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
151         IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
152         cipher.init(mode, secretKey, encIv);
153         return cipher;
154     }
155 
156     // get an instance of the AES Cipher in CTS mode
getChecksumLength()157     public int getChecksumLength() {
158         return hashSize;  // bytes
159     }
160 
161     /**
162      * Get the truncated HMAC
163      */
getHmac(byte[] key, byte[] msg)164     protected byte[] getHmac(byte[] key, byte[] msg)
165         throws GeneralSecurityException {
166 
167         SecretKey keyKi = new SecretKeySpec(key, "HMAC");
168         Mac m = Mac.getInstance("HmacSHA1");
169         m.init(keyKi);
170 
171         // generate hash
172         byte[] hash = m.doFinal(msg);
173 
174         // truncate hash
175         byte[] output = new byte[hashSize];
176         System.arraycopy(hash, 0, output, 0, hashSize);
177         return output;
178     }
179 
180     /**
181      * Calculate the checksum
182      */
calculateChecksum(byte[] baseKey, int usage, byte[] input, int start, int len)183     public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input,
184         int start, int len) throws GeneralSecurityException {
185 
186         if (!KeyUsage.isValid(usage)) {
187             throw new GeneralSecurityException("Invalid key usage number: "
188                                                 + usage);
189         }
190 
191         // Derive keys
192         byte[] constant = new byte[5];
193         constant[0] = (byte) ((usage>>24)&0xff);
194         constant[1] = (byte) ((usage>>16)&0xff);
195         constant[2] = (byte) ((usage>>8)&0xff);
196         constant[3] = (byte) (usage&0xff);
197 
198         constant[4] = (byte) 0x99;
199 
200         byte[] Kc = dk(baseKey, constant);  // Checksum key
201         if (debug) {
202             System.err.println("usage: " + usage);
203             traceOutput("input", input, start, Math.min(len, 32));
204             traceOutput("constant", constant, 0, constant.length);
205             traceOutput("baseKey", baseKey, 0, baseKey.length);
206             traceOutput("Kc", Kc, 0, Kc.length);
207         }
208 
209         try {
210             // Generate checksum
211             // H1 = HMAC(Kc, input)
212             byte[] hmac = getHmac(Kc, input);
213             if (debug) {
214                 traceOutput("hmac", hmac, 0, hmac.length);
215             }
216             if (hmac.length == getChecksumLength()) {
217                 return hmac;
218             } else if (hmac.length > getChecksumLength()) {
219                 byte[] buf = new byte[getChecksumLength()];
220                 System.arraycopy(hmac, 0, buf, 0, buf.length);
221                 return buf;
222             } else {
223                 throw new GeneralSecurityException("checksum size too short: " +
224                         hmac.length + "; expecting : " + getChecksumLength());
225             }
226         } finally {
227             Arrays.fill(Kc, 0, Kc.length, (byte)0);
228         }
229     }
230 
231     /**
232      * Performs encryption using derived key; adds confounder.
233      */
encrypt(byte[] baseKey, int usage, byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)234     public byte[] encrypt(byte[] baseKey, int usage,
235         byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)
236         throws GeneralSecurityException, KrbCryptoException {
237 
238         if (!KeyUsage.isValid(usage)) {
239             throw new GeneralSecurityException("Invalid key usage number: "
240                                                  + usage);
241         }
242         byte[] output = encryptCTS(baseKey, usage, ivec, new_ivec, plaintext,
243                                         start, len, true);
244         return output;
245     }
246 
247     /**
248      * Performs encryption using derived key; does not add confounder.
249      */
encryptRaw(byte[] baseKey, int usage, byte[] ivec, byte[] plaintext, int start, int len)250     public byte[] encryptRaw(byte[] baseKey, int usage,
251         byte[] ivec, byte[] plaintext, int start, int len)
252         throws GeneralSecurityException, KrbCryptoException {
253 
254         if (!KeyUsage.isValid(usage)) {
255             throw new GeneralSecurityException("Invalid key usage number: "
256                                                 + usage);
257         }
258         byte[] output = encryptCTS(baseKey, usage, ivec, null, plaintext,
259                                         start, len, false);
260         return output;
261     }
262 
263     /**
264      * @param baseKey key from which keys are to be derived using usage
265      * @param ciphertext  E(Ke, conf | plaintext | padding, ivec) | H1[1..h]
266      */
decrypt(byte[] baseKey, int usage, byte[] ivec, byte[] ciphertext, int start, int len)267     public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,
268         byte[] ciphertext, int start, int len) throws GeneralSecurityException {
269 
270         if (!KeyUsage.isValid(usage)) {
271             throw new GeneralSecurityException("Invalid key usage number: "
272                                                 + usage);
273         }
274         byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext,
275                                         start, len, true);
276         return output;
277     }
278 
279     /**
280      * Decrypts data using specified key and initial vector.
281      * @param baseKey encryption key to use
282      * @param ciphertext  encrypted data to be decrypted
283      * @param usage ignored
284      */
decryptRaw(byte[] baseKey, int usage, byte[] ivec, byte[] ciphertext, int start, int len)285     public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,
286         byte[] ciphertext, int start, int len)
287         throws GeneralSecurityException {
288 
289         if (!KeyUsage.isValid(usage)) {
290             throw new GeneralSecurityException("Invalid key usage number: "
291                                                 + usage);
292         }
293         byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext,
294                                         start, len, false);
295         return output;
296     }
297 
298     /**
299      * Encrypt AES in CBC-CTS mode using derived keys.
300      */
encryptCTS(byte[] baseKey, int usage, byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len, boolean confounder_exists)301     private byte[] encryptCTS(byte[] baseKey, int usage, byte[] ivec,
302         byte[] new_ivec, byte[] plaintext, int start, int len,
303         boolean confounder_exists)
304         throws GeneralSecurityException, KrbCryptoException {
305 
306         byte[] Ke = null;
307         byte[] Ki = null;
308 
309         if (debug) {
310             System.err.println("usage: " + usage);
311             if (ivec != null) {
312                 traceOutput("old_state.ivec", ivec, 0, ivec.length);
313             }
314             traceOutput("plaintext", plaintext, start, Math.min(len, 32));
315             traceOutput("baseKey", baseKey, 0, baseKey.length);
316         }
317 
318         try {
319             // derive Encryption key
320             byte[] constant = new byte[5];
321             constant[0] = (byte) ((usage>>24)&0xff);
322             constant[1] = (byte) ((usage>>16)&0xff);
323             constant[2] = (byte) ((usage>>8)&0xff);
324             constant[3] = (byte) (usage&0xff);
325             constant[4] = (byte) 0xaa;
326             Ke = dk(baseKey, constant);  // Encryption key
327 
328             byte[] toBeEncrypted = null;
329             if (confounder_exists) {
330                 byte[] confounder = Confounder.bytes(BLOCK_SIZE);
331                 toBeEncrypted = new byte[confounder.length + len];
332                 System.arraycopy(confounder, 0, toBeEncrypted,
333                                         0, confounder.length);
334                 System.arraycopy(plaintext, start, toBeEncrypted,
335                                         confounder.length, len);
336             } else {
337                 toBeEncrypted = new byte[len];
338                 System.arraycopy(plaintext, start, toBeEncrypted, 0, len);
339             }
340 
341             // encryptedData + HMAC
342             byte[] output = new byte[toBeEncrypted.length + hashSize];
343 
344             // AES in JCE
345             Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding");
346             SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES");
347             IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
348             cipher.init(Cipher.ENCRYPT_MODE, secretKey, encIv);
349             cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length, output);
350 
351             // Derive integrity key
352             constant[4] = (byte) 0x55;
353             Ki = dk(baseKey, constant);
354             if (debug) {
355                 traceOutput("constant", constant, 0, constant.length);
356                 traceOutput("Ki", Ki, 0, Ke.length);
357             }
358 
359             // Generate checksum
360             // H1 = HMAC(Ki, conf | plaintext | pad)
361             byte[] hmac = getHmac(Ki, toBeEncrypted);
362 
363             // encryptedData + HMAC
364             System.arraycopy(hmac, 0, output, toBeEncrypted.length,
365                                 hmac.length);
366             return output;
367         } finally {
368             if (Ke != null) {
369                 Arrays.fill(Ke, 0, Ke.length, (byte) 0);
370             }
371             if (Ki != null) {
372                 Arrays.fill(Ki, 0, Ki.length, (byte) 0);
373             }
374         }
375     }
376 
377     /**
378      * Decrypt AES in CBC-CTS mode using derived keys.
379      */
decryptCTS(byte[] baseKey, int usage, byte[] ivec, byte[] ciphertext, int start, int len, boolean confounder_exists)380     private byte[] decryptCTS(byte[] baseKey, int usage, byte[] ivec,
381         byte[] ciphertext, int start, int len, boolean confounder_exists)
382         throws GeneralSecurityException {
383 
384         byte[] Ke = null;
385         byte[] Ki = null;
386 
387         try {
388             // Derive encryption key
389             byte[] constant = new byte[5];
390             constant[0] = (byte) ((usage>>24)&0xff);
391             constant[1] = (byte) ((usage>>16)&0xff);
392             constant[2] = (byte) ((usage>>8)&0xff);
393             constant[3] = (byte) (usage&0xff);
394 
395             constant[4] = (byte) 0xaa;
396             Ke = dk(baseKey, constant);  // Encryption key
397 
398             if (debug) {
399                 System.err.println("usage: " + usage);
400                 if (ivec != null) {
401                     traceOutput("old_state.ivec", ivec, 0, ivec.length);
402                 }
403                 traceOutput("ciphertext", ciphertext, start, Math.min(len, 32));
404                 traceOutput("constant", constant, 0, constant.length);
405                 traceOutput("baseKey", baseKey, 0, baseKey.length);
406                 traceOutput("Ke", Ke, 0, Ke.length);
407             }
408 
409             // Decrypt [confounder | plaintext ] (without checksum)
410 
411             // AES in JCE
412             Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding");
413             SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES");
414             IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
415             cipher.init(Cipher.DECRYPT_MODE, secretKey, encIv);
416             byte[] plaintext = cipher.doFinal(ciphertext, start, len-hashSize);
417 
418             if (debug) {
419                 traceOutput("AES PlainText", plaintext, 0,
420                                 Math.min(plaintext.length, 32));
421             }
422 
423             // Derive integrity key
424             constant[4] = (byte) 0x55;
425             Ki = dk(baseKey, constant);  // Integrity key
426             if (debug) {
427                 traceOutput("constant", constant, 0, constant.length);
428                 traceOutput("Ki", Ki, 0, Ke.length);
429             }
430 
431             // Verify checksum
432             // H1 = HMAC(Ki, conf | plaintext | pad)
433             byte[] calculatedHmac = getHmac(Ki, plaintext);
434             int hmacOffset = start + len - hashSize;
435             if (debug) {
436                 traceOutput("calculated Hmac", calculatedHmac,
437                                 0, calculatedHmac.length);
438                 traceOutput("message Hmac", ciphertext, hmacOffset, hashSize);
439             }
440             boolean cksumFailed = false;
441             if (calculatedHmac.length >= hashSize) {
442                 for (int i = 0; i < hashSize; i++) {
443                     if (calculatedHmac[i] != ciphertext[hmacOffset+i]) {
444                         cksumFailed = true;
445                         if (debug) {
446                             System.err.println("Checksum failed !");
447                         }
448                         break;
449                     }
450                 }
451             }
452             if (cksumFailed) {
453                 throw new GeneralSecurityException("Checksum failed");
454             }
455 
456             if (confounder_exists) {
457                 // Get rid of confounder
458                 // [ confounder | plaintext ]
459                 byte[] output = new byte[plaintext.length - BLOCK_SIZE];
460                 System.arraycopy(plaintext, BLOCK_SIZE, output,
461                                         0, output.length);
462                 return output;
463             } else {
464                 return plaintext;
465             }
466         } finally {
467             if (Ke != null) {
468                 Arrays.fill(Ke, 0, Ke.length, (byte) 0);
469             }
470             if (Ki != null) {
471                 Arrays.fill(Ki, 0, Ki.length, (byte) 0);
472             }
473         }
474     }
475 
476     /*
477      * Invoke the PKCS#5 PBKDF2 algorithm
478      */
PBKDF2(char[] secret, byte[] salt, int count, int keyLength)479     private static byte[] PBKDF2(char[] secret, byte[] salt,
480         int count, int keyLength) throws GeneralSecurityException {
481 
482         PBEKeySpec keySpec = new PBEKeySpec(secret, salt, count, keyLength);
483         SecretKeyFactory skf =
484                 SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
485         SecretKey key = skf.generateSecret(keySpec);
486         byte[] result = key.getEncoded();
487 
488         return result;
489     }
490 
readBigEndian(byte[] data, int pos, int size)491     public static final int readBigEndian(byte[] data, int pos, int size) {
492         int retVal = 0;
493         int shifter = (size-1)*8;
494         while (size > 0) {
495             retVal += (data[pos] & 0xff) << shifter;
496             shifter -= 8;
497             pos++;
498             size--;
499         }
500         return retVal;
501     }
502 
503 }
504