| /* |
| * 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: |
| 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 |
| throw new RuntimeException("Unexpected exception", ioe); |
| } |
| |
| 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); |
| } |
| } |
| } |
| |