1 /* 2 * Copyright (c) 2018, 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 package sun.security.ssl; 27 28 import java.security.NoSuchAlgorithmException; 29 import java.security.InvalidKeyException; 30 import javax.crypto.Mac; 31 import javax.crypto.SecretKey; 32 import javax.crypto.ShortBufferException; 33 import javax.crypto.spec.SecretKeySpec; 34 import java.util.Objects; 35 36 /** 37 * An implementation of the HKDF key derivation algorithm outlined in RFC 5869, 38 * specific to the needs of TLS 1.3 key derivation in JSSE. This is not a 39 * general purpose HKDF implementation and is suited only to single-key output 40 * derivations. 41 * 42 * HKDF objects are created by specifying a message digest algorithm. That 43 * digest algorithm will be used by the HMAC function as part of the HKDF 44 * derivation process. 45 */ 46 final class HKDF { 47 private final String hmacAlg; 48 private final Mac hmacObj; 49 private final int hmacLen; 50 51 /** 52 * Create an HDKF object, specifying the underlying message digest 53 * algorithm. 54 * 55 * @param hashAlg a standard name corresponding to a supported message 56 * digest algorithm. 57 * 58 * @throws NoSuchAlgorithmException if that message digest algorithm does 59 * not have an HMAC variant supported on any available provider. 60 */ HKDF(String hashAlg)61 HKDF(String hashAlg) throws NoSuchAlgorithmException { 62 Objects.requireNonNull(hashAlg, 63 "Must provide underlying HKDF Digest algorithm."); 64 hmacAlg = "Hmac" + hashAlg.replace("-", ""); 65 hmacObj = Mac.getInstance(hmacAlg); 66 hmacLen = hmacObj.getMacLength(); 67 } 68 69 /** 70 * Perform the HMAC-Extract derivation. 71 * 72 * @param salt a salt value, implemented as a {@code SecretKey}. A 73 * {@code null} value is allowed, which will internally use an array of 74 * zero bytes the same size as the underlying hash output length. 75 * @param inputKey the input keying material provided as a 76 * {@code SecretKey}. 77 * @param keyAlg the algorithm name assigned to the resulting 78 * {@code SecretKey} object. 79 * 80 * @return a {@code SecretKey} that is the result of the HKDF extract 81 * operation. 82 * 83 * @throws InvalidKeyException if the {@code salt} parameter cannot be 84 * used to initialize the underlying HMAC. 85 */ extract(SecretKey salt, SecretKey inputKey, String keyAlg)86 SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg) 87 throws InvalidKeyException { 88 if (salt == null) { 89 salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt"); 90 } 91 hmacObj.init(salt); 92 93 return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()), 94 keyAlg); 95 } 96 97 /** 98 * Perform the HMAC-Extract derivation. 99 * 100 * @param salt a salt value as cleartext bytes. A {@code null} value is 101 * allowed, which will internally use an array of zero bytes the same 102 * size as the underlying hash output length. 103 * @param inputKey the input keying material provided as a 104 * {@code SecretKey}. 105 * @param keyAlg the algorithm name assigned to the resulting 106 * {@code SecretKey} object. 107 * 108 * @return a {@code SecretKey} that is the result of the HKDF extract 109 * operation. 110 * 111 * @throws InvalidKeyException if the {@code salt} parameter cannot be 112 * used to initialize the underlying HMAC. 113 */ extract(byte[] salt, SecretKey inputKey, String keyAlg)114 SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg) 115 throws InvalidKeyException { 116 if (salt == null) { 117 salt = new byte[hmacLen]; 118 } 119 return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg); 120 } 121 122 /** 123 * Perform the HKDF-Expand derivation for a single-key output. 124 * 125 * @param pseudoRandKey the pseudo random key (PRK). 126 * @param info optional context-specific info. A {@code null} value is 127 * allowed in which case a zero-length byte array will be used. 128 * @param outLen the length of the resulting {@code SecretKey} 129 * @param keyAlg the algorithm name applied to the resulting 130 * {@code SecretKey} 131 * 132 * @return the resulting key derivation as a {@code SecretKey} object 133 * 134 * @throws InvalidKeyException if the underlying HMAC operation cannot 135 * be initialized using the provided {@code pseudoRandKey} object. 136 */ expand(SecretKey pseudoRandKey, byte[] info, int outLen, String keyAlg)137 SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen, 138 String keyAlg) throws InvalidKeyException { 139 byte[] kdfOutput; 140 141 // Calculate the number of rounds of HMAC that are needed to 142 // meet the requested data. Then set up the buffers we will need. 143 Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed."); 144 145 // Output from the expand operation must be <= 255 * hmac length 146 if (outLen > 255 * hmacLen) { 147 throw new IllegalArgumentException("Requested output length " + 148 "exceeds maximum length allowed for HKDF expansion"); 149 } 150 hmacObj.init(pseudoRandKey); 151 if (info == null) { 152 info = new byte[0]; 153 } 154 int rounds = (outLen + hmacLen - 1) / hmacLen; 155 kdfOutput = new byte[rounds * hmacLen]; 156 int offset = 0; 157 int tLength = 0; 158 159 for (int i = 0; i < rounds ; i++) { 160 161 // Calculate this round 162 try { 163 // Add T(i). This will be an empty string on the first 164 // iteration since tLength starts at zero. After the first 165 // iteration, tLength is changed to the HMAC length for the 166 // rest of the loop. 167 hmacObj.update(kdfOutput, 168 Math.max(0, offset - hmacLen), tLength); 169 hmacObj.update(info); // Add info 170 hmacObj.update((byte)(i + 1)); // Add round number 171 hmacObj.doFinal(kdfOutput, offset); 172 173 tLength = hmacLen; 174 offset += hmacLen; // For next iteration 175 } catch (ShortBufferException sbe) { 176 // This really shouldn't happen given that we've 177 // sized the buffers to their largest possible size up-front, 178 // but just in case... 179 throw new RuntimeException(sbe); 180 } 181 } 182 183 return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg); 184 } 185 } 186 187