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