1 /* 2 * Copyright (c) 2018, 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 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 */ 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 */ 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 */ 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 */ 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 hmacObj.init(pseudoRandKey); 145 if (info == null) { 146 info = new byte[0]; 147 } 148 int rounds = (outLen + hmacLen - 1) / hmacLen; 149 kdfOutput = new byte[rounds * hmacLen]; 150 int offset = 0; 151 int tLength = 0; 152 153 for (int i = 0; i < rounds ; i++) { 154 155 // Calculate this round 156 try { 157 // Add T(i). This will be an empty string on the first 158 // iteration since tLength starts at zero. After the first 159 // iteration, tLength is changed to the HMAC length for the 160 // rest of the loop. 161 hmacObj.update(kdfOutput, 162 Math.max(0, offset - hmacLen), tLength); 163 hmacObj.update(info); // Add info 164 hmacObj.update((byte)(i + 1)); // Add round number 165 hmacObj.doFinal(kdfOutput, offset); 166 167 tLength = hmacLen; 168 offset += hmacLen; // For next iteration 169 } catch (ShortBufferException sbe) { 170 // This really shouldn't happen given that we've 171 // sized the buffers to their largest possible size up-front, 172 // but just in case... 173 throw new RuntimeException(sbe); 174 } 175 } 176 177 return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg); 178 } 179 180 /** 181 * Perform the HKDF Extract-then-Expand operation. 182 * 183 * @param inputKey the input keying material provided as a 184 * {@code SecretKey}. 185 * @param salt a salt value, implemented as a {@code SecretKey}. A 186 * {@code null} value is allowed, which will internally use an array of 187 * zero bytes the same size as the underlying hash output length. 188 * @param info optional context-specific info. A {@code null} value is 189 * allowed in which case a zero-length byte array will be used. 190 * @param outLen the length of the resulting {@code SecretKey} 191 * @param keyAlg the algorithm name applied to the resulting 192 * {@code SecretKey} 193 * 194 * @return the resulting derivation stored in a {@code SecretKey} object. 195 * 196 * @throws InvalidKeyException if initialization of the underlying HMAC 197 * process fails with the salt during the extract phase, or with the 198 * resulting PRK during the expand phase. 199 */ 200 SecretKey extractExpand(SecretKey inputKey, SecretKey salt, byte[] info, 201 int outLen, String keyAlg) throws InvalidKeyException { 202 SecretKey prk = extract(salt, inputKey, "HKDF-PRK"); 203 return expand(prk, info, outLen, keyAlg); 204 } 205 206 /** 207 * Perform the HKDF Extract-then-Expand operation. 208 * 209 * @param inputKey the input keying material provided as a 210 * {@code SecretKey}. 211 * @param salt a salt value as cleartext bytes. A {@code null} value is 212 * allowed, which will internally use an array of zero bytes the same 213 * size as the underlying hash output length. 214 * @param info optional context-specific info. A {@code null} value is 215 * allowed in which case a zero-length byte array will be used. 216 * @param outLen the length of the resulting {@code SecretKey} 217 * @param keyAlg the algorithm name applied to the resulting 218 * {@code SecretKey} 219 * 220 * @return the resulting derivation stored in a {@code SecretKey} object. 221 * 222 * @throws InvalidKeyException if initialization of the underlying HMAC 223 * process fails with the salt during the extract phase, or with the 224 * resulting PRK during the expand phase. 225 */ 226 SecretKey extractExpand(SecretKey inputKey, byte[] salt, byte[] info, 227 int outLen, String keyAlg) throws InvalidKeyException { 228 byte[] saltBytes = (salt != null) ? salt : new byte[hmacLen]; 229 return extractExpand(inputKey, 230 new SecretKeySpec(saltBytes, "HKDF-PRK"), info, outLen, keyAlg); 231 } 232 }