src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java
changeset 50768 68fa3d4026ea
child 53734 cb1642ccc732
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java	Mon Jun 25 13:41:39 2018 -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:
+                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);
+        }
+    }
+}
+