--- /dev/null 2018-05-11 10:42:23.849000000 -0700 +++ new/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java 2018-05-11 15:10:23.250696600 -0700 @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.ProviderException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.SSLHandshakeException; +import sun.security.internal.spec.TlsKeyMaterialParameterSpec; +import sun.security.internal.spec.TlsKeyMaterialSpec; +import sun.security.ssl.CipherSuite.HashAlg; +import static sun.security.ssl.CipherSuite.HashAlg.H_NONE; + +enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator { + SSL30 ("kdf_ssl30", new S30TrafficKeyDerivationGenerator()), + TLS10 ("kdf_tls10", new T10TrafficKeyDerivationGenerator()), + TLS12 ("kdf_tls12", new T12TrafficKeyDerivationGenerator()), + TLS13 ("kdf_tls13", new T13TrafficKeyDerivationGenerator()); + + final String name; + final SSLKeyDerivationGenerator keyDerivationGenerator; + + SSLTrafficKeyDerivation(String name, + SSLKeyDerivationGenerator keyDerivationGenerator) { + this.name = name; + this.keyDerivationGenerator = keyDerivationGenerator; + } + + static SSLTrafficKeyDerivation valueOf(ProtocolVersion protocolVersion) { + switch (protocolVersion) { + case SSL30: + return SSLTrafficKeyDerivation.SSL30; + case TLS10: + case TLS11: + case DTLS10: + return SSLTrafficKeyDerivation.TLS10; + case TLS12: + case DTLS12: + return SSLTrafficKeyDerivation.TLS12; + case TLS13: + case DTLS13: + return SSLTrafficKeyDerivation.TLS13; + } + + return null; + } + + @Override + public SSLKeyDerivation createKeyDerivation(HandshakeContext context, + SecretKey secretKey) throws IOException { + return keyDerivationGenerator.createKeyDerivation(context, secretKey); + } + + private static final class S30TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private S30TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, SecretKey secretKey) throws IOException { + return new LegacyTrafficKeyDerivation(context, secretKey); + } + } + + private static final class T10TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private T10TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, SecretKey secretKey) throws IOException { + return new LegacyTrafficKeyDerivation(context, secretKey); + } + } + + private static final class T12TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private T12TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, SecretKey secretKey) throws IOException { + return new LegacyTrafficKeyDerivation(context, secretKey); + } + } + + private static final class T13TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private T13TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, + SecretKey secretKey) throws IOException { + return new T13TrafficKeyDerivation(context, secretKey); + } + } + + static final class T13TrafficKeyDerivation implements SSLKeyDerivation { + private final CipherSuite cs; + private final SecretKey secret; + + T13TrafficKeyDerivation( + HandshakeContext context, SecretKey secret) { + this.secret = secret; + this.cs = context.negotiatedCipherSuite; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + KeySchedule ks = KeySchedule.valueOf(algorithm); + try { + HKDF hkdf = new HKDF(cs.hashAlg.name); + byte[] hkdfInfo = + createHkdfInfo(ks.label, ks.getKeyLength(cs)); + return hkdf.expand(secret, hkdfInfo, + ks.getKeyLength(cs), + ks.getAlgorithm(cs, algorithm)); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException)(new SSLHandshakeException( + "Could not generate secret").initCause(gse)); + } + } + + private static byte[] createHkdfInfo( + byte[] label, int length) throws IOException { + byte[] info = new byte[4 + label.length]; + ByteBuffer m = ByteBuffer.wrap(info); + try { + Record.putInt16(m, length); + Record.putBytes8(m, label); + Record.putInt8(m, 0x00); // zero-length context + } catch (IOException ioe) { + // unlikely + } + + return info; + } + } + + private enum KeySchedule { + // Note that we use enum name as the key/ name. + TlsKey ("key", false), + TlsIv ("iv", true), + TlsUpdateNplus1 ("traffic upd", false); + + private final byte[] label; + private final boolean isIv; + + private KeySchedule(String label, boolean isIv) { + this.label = ("tls13 " + label).getBytes(); + this.isIv = isIv; + } + + int getKeyLength(CipherSuite cs) { + if (this == KeySchedule.TlsUpdateNplus1) + return cs.hashAlg.hashLength; + return isIv ? cs.bulkCipher.ivSize : cs.bulkCipher.keySize; + } + + String getAlgorithm(CipherSuite cs, String algorithm) { + return isIv ? algorithm : cs.bulkCipher.algorithm; + } + } + + @SuppressWarnings("deprecation") + static final class LegacyTrafficKeyDerivation implements SSLKeyDerivation { + private final HandshakeContext context; + private final SecretKey masterSecret; + private final TlsKeyMaterialSpec keyMaterialSpec; + + LegacyTrafficKeyDerivation( + HandshakeContext context, SecretKey masterSecret) { + this.context = context; + this.masterSecret = masterSecret; + + CipherSuite cipherSuite = context.negotiatedCipherSuite; + ProtocolVersion protocolVersion = context.negotiatedProtocol; + + /* + * For both the read and write sides of the protocol, we use the + * master to generate MAC secrets and cipher keying material. Block + * ciphers need initialization vectors, which we also generate. + * + * First we figure out how much keying material is needed. + */ + int hashSize = cipherSuite.macAlg.size; + boolean is_exportable = cipherSuite.exportable; + SSLCipher cipher = cipherSuite.bulkCipher; + int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; + + // Which algs/params do we need to use? + String keyMaterialAlg; + HashAlg hashAlg; + + byte majorVersion = protocolVersion.major; + byte minorVersion = protocolVersion.minor; + if (protocolVersion.isDTLS) { + // Use TLS version number for DTLS key calculation + if (protocolVersion.id == ProtocolVersion.DTLS10.id) { + majorVersion = ProtocolVersion.TLS11.major; + minorVersion = ProtocolVersion.TLS11.minor; + + keyMaterialAlg = "SunTlsKeyMaterial"; + hashAlg = H_NONE; + } else { // DTLS 1.2+ + majorVersion = ProtocolVersion.TLS12.major; + minorVersion = ProtocolVersion.TLS12.minor; + + keyMaterialAlg = "SunTls12KeyMaterial"; + hashAlg = cipherSuite.hashAlg; + } + } else { + if (protocolVersion.id >= ProtocolVersion.TLS12.id) { + keyMaterialAlg = "SunTls12KeyMaterial"; + hashAlg = cipherSuite.hashAlg; + } else { + keyMaterialAlg = "SunTlsKeyMaterial"; + hashAlg = H_NONE; + } + } + + // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to + // protect against the CBC attacks. AEAD/GCM cipher suites in + // TLS v1.2 or later use a fixed IV as the implicit part of the + // partially implicit nonce technique described in RFC 5116. + int ivSize = cipher.ivSize; + if (cipher.cipherType == CipherType.AEAD_CIPHER) { + ivSize = cipher.fixedIvSize; + } else if ( + cipher.cipherType == CipherType.BLOCK_CIPHER && + protocolVersion.useTLS11PlusSpec()) { + ivSize = 0; + } + + TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( + masterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF), + context.clientHelloRandom.randomBytes, + context.serverHelloRandom.randomBytes, + cipher.algorithm, cipher.keySize, expandedKeySize, + ivSize, hashSize, + hashAlg.name, hashAlg.hashLength, hashAlg.blockSize); + + try { + KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); + kg.init(spec); + + this.keyMaterialSpec = (TlsKeyMaterialSpec)kg.generateKey(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + + SecretKey getTrafficKey(String algorithm) { + switch (algorithm) { + case "clientMacKey": + return keyMaterialSpec.getClientMacKey(); + case "serverMacKey": + return keyMaterialSpec.getServerMacKey(); + case "clientWriteKey": + return keyMaterialSpec.getClientCipherKey(); + case "serverWriteKey": + return keyMaterialSpec.getServerCipherKey(); + case "clientWriteIv": + IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv(); + return (cliIvSpec == null) ? null : + new SecretKeySpec(cliIvSpec.getIV(), "TlsIv"); + case "serverWriteIv": + IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv(); + return (srvIvSpec == null) ? null : + new SecretKeySpec(srvIvSpec.getIV(), "TlsIv"); + } + + return null; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + return getTrafficKey(algorithm); + } + } +} +