src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java

Print this page

        

@@ -71,29 +71,48 @@
  *
  * @author David Brownell
  */
 public abstract class HandshakeMessage {
 
-    HandshakeMessage() { }
+    /* Class and subclass dynamic debugging support */
+    public static final Debug debug = Debug.getInstance("ssl");
 
     // enum HandshakeType:
-    static final byte   ht_hello_request = 0;
-    static final byte   ht_client_hello = 1;
-    static final byte   ht_server_hello = 2;
+    static final byte   ht_hello_request          = 0;      // RFC 5246
+    static final byte   ht_client_hello           = 1;      // RFC 5246
+    static final byte   ht_server_hello           = 2;      // RFC 5246
+    static final byte   ht_hello_verify_request   = 3;      // RFC 6347
+    static final byte   ht_new_session_ticket     = 4;      // RFC 4507
 
-    static final byte   ht_certificate = 11;
-    static final byte   ht_server_key_exchange = 12;
-    static final byte   ht_certificate_request = 13;
-    static final byte   ht_server_hello_done = 14;
-    static final byte   ht_certificate_verify = 15;
-    static final byte   ht_client_key_exchange = 16;
+    static final byte   ht_certificate            = 11;     // RFC 5246
+    static final byte   ht_server_key_exchange    = 12;     // RFC 5246
+    static final byte   ht_certificate_request    = 13;     // RFC 5246
+    static final byte   ht_server_hello_done      = 14;     // RFC 5246
+    static final byte   ht_certificate_verify     = 15;     // RFC 5246
+    static final byte   ht_client_key_exchange    = 16;     // RFC 5246
 
-    static final byte   ht_finished = 20;
+    static final byte   ht_finished               = 20;     // RFC 5246
+    static final byte   ht_certificate_url        = 21;     // RFC 6066
+    static final byte   ht_certificate_status     = 22;     // RFC 6066
+    static final byte   ht_supplemental_data      = 23;     // RFC 4680
 
-    /* Class and subclass dynamic debugging support */
-    public static final Debug debug = Debug.getInstance("ssl");
+    static final byte   ht_not_applicable         = -1;     // N/A
 
+    /*
+     * SSL 3.0 MAC padding constants.
+     * Also used by CertificateVerify and Finished during the handshake.
+     */
+    static final byte[] MD5_pad1 = genPad(0x36, 48);
+    static final byte[] MD5_pad2 = genPad(0x5c, 48);
+
+    static final byte[] SHA_pad1 = genPad(0x36, 40);
+    static final byte[] SHA_pad2 = genPad(0x5c, 40);
+
+    // default constructor
+    HandshakeMessage() {
+    }
+
     /**
      * Utility method to convert a BigInteger to a byte array in unsigned
      * format as needed in the handshake messages. BigInteger uses
      * 2's complement format, i.e. it prepends an extra zero if the MSB
      * is set. We remove that.

@@ -107,20 +126,10 @@
             b = newarray;
         }
         return b;
     }
 
-    /*
-     * SSL 3.0 MAC padding constants.
-     * Also used by CertificateVerify and Finished during the handshake.
-     */
-    static final byte[] MD5_pad1 = genPad(0x36, 48);
-    static final byte[] MD5_pad2 = genPad(0x5c, 48);
-
-    static final byte[] SHA_pad1 = genPad(0x36, 40);
-    static final byte[] SHA_pad2 = genPad(0x5c, 40);
-
     private static byte[] genPad(int b, int count) {
         byte[] padding = new byte[count];
         Arrays.fill(padding, (byte)b);
         return padding;
     }

@@ -139,10 +148,11 @@
                 + ", type = " + messageType() + ", len = " + len);
         }
         s.write(messageType());
         s.putInt24(len);
         send(s);
+        s.complete();
     }
 
     /*
      * Subclasses implement these methods so those kinds of
      * messages can be emitted.  Base class delegates to subclass.

@@ -197,11 +207,74 @@
         out.println("*** HelloRequest (empty)");
     }
 
 }
 
+/*
+ * HelloVerifyRequest ... SERVER --> CLIENT  [DTLS only]
+ *
+ * The definition of HelloVerifyRequest is as follows:
+ *
+ *     struct {
+ *       ProtocolVersion server_version;
+ *       opaque cookie<0..2^8-1>;
+ *     } HelloVerifyRequest;
+ *
+ * For DTLS protocols, once the client has transmitted the ClientHello message,
+ * it expects to see a HelloVerifyRequest from the server.  However, if the
+ * server's message is lost, the client knows that either the ClientHello or
+ * the HelloVerifyRequest has been lost and retransmits. [RFC 6347]
+ */
+static final class HelloVerifyRequest extends HandshakeMessage {
+    ProtocolVersion     protocolVersion;
+    byte[]              cookie;         // 1 to 2^8 - 1 bytes
 
+    HelloVerifyRequest(HelloCookieManager helloCookieManager,
+            ClientHello clientHelloMsg) {
+
+        this.protocolVersion = clientHelloMsg.protocolVersion;
+        this.cookie = helloCookieManager.getCookie(clientHelloMsg);
+    }
+
+    HelloVerifyRequest(
+            HandshakeInStream input, int messageLength) throws IOException {
+
+        this.protocolVersion =
+                ProtocolVersion.valueOf(input.getInt8(), input.getInt8());
+        this.cookie = input.getBytes8();
+
+        // Is it a valid cookie?
+        HelloCookieManager.checkCookie(protocolVersion, cookie);
+    }
+
+    @Override
+    int messageType() {
+        return ht_hello_verify_request;
+    }
+
+    @Override
+    int messageLength() {
+        return 2 + cookie.length;       // 2: the length of protocolVersion
+    }
+
+    @Override
+    void send(HandshakeOutStream hos) throws IOException {
+        hos.putInt8(protocolVersion.major);
+        hos.putInt8(protocolVersion.minor);
+        hos.putBytes8(cookie);
+    }
+
+    @Override
+    void print(PrintStream out) throws IOException {
+        out.println("*** HelloVerifyRequest");
+        if (debug != null && Debug.isOn("verbose")) {
+            out.println("server_version: " + protocolVersion);
+            Debug.println(out, "cookie", cookie);
+        }
+    }
+}
+
 /*
  * ClientHello ... CLIENT --> SERVER
  *
  * Client initiates handshake by telling server what it wants, and what it
  * can support (prioritized by what's first in the ciphe suite list).

@@ -214,23 +287,32 @@
 static final class ClientHello extends HandshakeMessage {
 
     ProtocolVersion     protocolVersion;
     RandomCookie        clnt_random;
     SessionId           sessionId;
+    byte[]                      cookie;                     // DTLS only
     private CipherSuiteList    cipherSuites;
+    private final boolean       isDTLS;
     byte[]              compression_methods;
 
     HelloExtensions extensions = new HelloExtensions();
 
     private final static byte[]  NULL_COMPRESSION = new byte[] {0};
 
     ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
-            SessionId sessionId, CipherSuiteList cipherSuites) {
+            SessionId sessionId, CipherSuiteList cipherSuites,
+            boolean isDTLS) {
 
+        this.isDTLS = isDTLS;
         this.protocolVersion = protocolVersion;
         this.sessionId = sessionId;
         this.cipherSuites = cipherSuites;
+        if (isDTLS) {
+            this.cookie = new byte[0];
+        } else {
+            this.cookie = null;
+        }
 
         if (cipherSuites.containsEC()) {
             extensions.add(SupportedEllipticCurvesExtension.DEFAULT);
             extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT);
         }

@@ -237,15 +319,25 @@
 
         clnt_random = new RandomCookie(generator);
         compression_methods = NULL_COMPRESSION;
     }
 
-    ClientHello(HandshakeInStream s, int messageLength) throws IOException {
+    ClientHello(HandshakeInStream s,
+            int messageLength, boolean isDTLS) throws IOException {
+
+        this.isDTLS = isDTLS;
+
         protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());
         clnt_random = new RandomCookie(s);
         sessionId = new SessionId(s.getBytes8());
         sessionId.checkLength(protocolVersion);
+        if (isDTLS) {
+            cookie = s.getBytes8();
+        } else {
+            cookie = null;
+        }
+
         cipherSuites = new CipherSuiteList(s);
         compression_methods = s.getBytes8();
         if (messageLength() != messageLength) {
             extensions = new HelloExtensions(s);
         }

@@ -277,10 +369,32 @@
         HelloExtension signatureAlgorithm =
                 new SignatureAlgorithmsExtension(algorithms);
         extensions.add(signatureAlgorithm);
     }
 
+    void addMFLExtension(int maximumPacketSize) {
+        HelloExtension maxFragmentLength =
+                new MaxFragmentLengthExtension(maximumPacketSize);
+        extensions.add(maxFragmentLength);
+    }
+
+    void updateHelloCookie(MessageDigest cookieDigest) {
+        //
+        // Just use HandshakeOutStream to compute the hello verify cookie.
+        // Not actually used to output handshake message records.
+        //
+        HandshakeOutStream hos = new HandshakeOutStream(null);
+
+        try {
+            send(hos, false);    // Do not count hello verify cookie.
+        } catch (IOException ioe) {
+            // unlikely to happen
+        }
+
+        cookieDigest.update(hos.toByteArray());
+    }
+
     @Override
     int messageType() { return ht_client_hello; }
 
     @Override
     int messageLength() {

@@ -288,24 +402,19 @@
          * Add fixed size parts of each field...
          * version + random + session + cipher + compress
          */
         return (2 + 32 + 1 + 2 + 1
             + sessionId.length()                /* ... + variable parts */
+            + (isDTLS ? (1 + cookie.length) : 0)
             + (cipherSuites.size() * 2)
             + compression_methods.length)
             + extensions.length();
     }
 
     @Override
     void send(HandshakeOutStream s) throws IOException {
-        s.putInt8(protocolVersion.major);
-        s.putInt8(protocolVersion.minor);
-        clnt_random.send(s);
-        s.putBytes8(sessionId.getId());
-        cipherSuites.send(s);
-        s.putBytes8(compression_methods);
-        extensions.send(s);
+        send(s, true);  // Count hello verify cookie.
     }
 
     @Override
     void print(PrintStream s) throws IOException {
         s.println("*** ClientHello, " + protocolVersion);

@@ -315,17 +424,36 @@
             clnt_random.print(s);
 
             s.print("Session ID:  ");
             s.println(sessionId);
 
+            if (isDTLS) {
+                Debug.println(s, "cookie", cookie);
+            }
+
             s.println("Cipher Suites: " + cipherSuites);
 
             Debug.println(s, "Compression Methods", compression_methods);
             extensions.print(s);
             s.println("***");
         }
     }
+
+    private void send(HandshakeOutStream s, 
+            boolean computeCookie) throws IOException {
+        s.putInt8(protocolVersion.major);
+        s.putInt8(protocolVersion.minor);
+        clnt_random.send(s);
+        s.putBytes8(sessionId.getId());
+        if (isDTLS && computeCookie) {
+            s.putBytes8(cookie);
+        }
+        cipherSuites.send(s);
+        s.putBytes8(compression_methods);
+        extensions.send(s);
+    }
+
 }
 
 /*
  * ServerHello ... SERVER --> CLIENT
  *

@@ -738,11 +866,11 @@
 
         // The DH key has been validated in the constructor of DHCrypt.
         setValues(obj);
 
         Signature sig;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             this.preferableSignatureAlgorithm = signAlgorithm;
             sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
         } else {
             this.preferableSignatureAlgorithm = null;
             if (key.getAlgorithm().equals("DSA")) {

@@ -799,11 +927,11 @@
         KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys),
                                              new BigInteger(1, dh_p),
                                              new BigInteger(1, dh_g)));
 
         // read the signature and hash algorithm
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             int hash = input.getInt8();         // hash algorithm
             int signature = input.getInt8();    // signature algorithm
 
             preferableSignatureAlgorithm =
                 SignatureAndHashAlgorithm.valueOf(hash, signature, 0);

@@ -832,11 +960,11 @@
             input.read(signature);
         }
 
         Signature sig;
         String algorithm = publicKey.getAlgorithm();
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             sig = JsseJce.getSignature(
                         preferableSignatureAlgorithm.getAlgorithmName());
         } else {
                 switch (algorithm) {
                     case "DSA":

@@ -912,11 +1040,11 @@
         temp += dh_p.length;
         temp += dh_g.length;
         temp += dh_Ys.length;
 
         if (signature != null) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 temp += SignatureAndHashAlgorithm.sizeInRecord();
             }
 
             temp += signature.length;
             if (dhKeyExchangeFix) {

@@ -932,11 +1060,11 @@
         s.putBytes16(dh_p);
         s.putBytes16(dh_g);
         s.putBytes16(dh_Ys);
 
         if (signature != null) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 s.putInt8(preferableSignatureAlgorithm.getHashValue());
                 s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
             }
 
             if (dhKeyExchangeFix) {

@@ -957,11 +1085,11 @@
             Debug.println(s, "Server DH Public Key", dh_Ys);
 
             if (signature == null) {
                 s.println("Anonymous");
             } else {
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     s.println("Signature Algorithm " +
                         preferableSignatureAlgorithm.getAlgorithmName());
                 }
 
                 s.println("Signed with a DSA or RSA public key");

@@ -1019,11 +1147,11 @@
             // ECDH_anon
             return;
         }
 
         Signature sig;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             this.preferableSignatureAlgorithm = signAlgorithm;
             sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
         } else {
             sig = getSignature(privateKey.getAlgorithm());
         }

@@ -1082,11 +1210,11 @@
             // ECDH_anon
             return;
         }
 
         // read the signature and hash algorithm
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             int hash = input.getInt8();         // hash algorithm
             int signature = input.getInt8();    // signature algorithm
 
             preferableSignatureAlgorithm =
                 SignatureAndHashAlgorithm.valueOf(hash, signature, 0);

@@ -1103,11 +1231,11 @@
         // read the signature
         signatureBytes = input.getBytes16();
 
         // verify the signature
         Signature sig;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             sig = JsseJce.getSignature(
                         preferableSignatureAlgorithm.getAlgorithmName());
         } else {
             sig = getSignature(signingKey.getAlgorithm());
         }

@@ -1155,11 +1283,11 @@
     @Override
     int messageLength() {
         int sigLen = 0;
         if (signatureBytes != null) {
             sigLen = 2 + signatureBytes.length;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 sigLen += SignatureAndHashAlgorithm.sizeInRecord();
             }
         }
 
         return 4 + pointBytes.length + sigLen;

@@ -1170,11 +1298,11 @@
         s.putInt8(CURVE_NAMED_CURVE);
         s.putInt16(curveId);
         s.putBytes8(pointBytes);
 
         if (signatureBytes != null) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 s.putInt8(preferableSignatureAlgorithm.getHashValue());
                 s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
             }
 
             s.putBytes16(signatureBytes);

@@ -1187,11 +1315,11 @@
 
         if (debug != null && Debug.isOn("verbose")) {
             if (signatureBytes == null) {
                 s.println("Anonymous");
             } else {
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+                if (protocolVersion.useTLS12PlusSpec()) {
                     s.println("Signature Algorithm " +
                             preferableSignatureAlgorithm.getAlgorithmName());
                 }
             }
 

@@ -1313,11 +1441,11 @@
         // needs to be adapted to take keyExchange into account.
         // We only request ECDSA client auth if we have ECC crypto available.
         this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
 
         // Use supported_signature_algorithms for TLS 1.2 or later.
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             if (signAlgs == null || signAlgs.isEmpty()) {
                 throw new SSLProtocolException(
                         "No supported signature algorithms");
             }
 

@@ -1337,11 +1465,11 @@
 
         // Read the certificate_types.
         types = input.getBytes8();
 
         // Read the supported_signature_algorithms for TLS 1.2 or later.
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             algorithmsLen = input.getInt16();
             if (algorithmsLen < 2) {
                 throw new SSLProtocolException(
                         "Invalid supported_signature_algorithms field");
             }

@@ -1404,11 +1532,11 @@
 
     @Override
     int messageLength() {
         int len = 1 + types.length + 2;
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             len += algorithmsLen + 2;
         }
 
         for (int i = 0; i < authorities.length; i++) {
             len += authorities[i].length();

@@ -1421,11 +1549,11 @@
     void send(HandshakeOutStream output) throws IOException {
         // put certificate_types
         output.putBytes8(types);
 
         // put supported_signature_algorithms
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             output.putInt16(algorithmsLen);
             for (SignatureAndHashAlgorithm algorithm : algorithms) {
                 output.putInt8(algorithm.getHashValue());      // hash
                 output.putInt8(algorithm.getSignatureValue()); // signature
             }

@@ -1476,11 +1604,11 @@
                     s.print(", ");
                 }
             }
             s.println();
 
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 StringBuilder sb = new StringBuilder();
                 boolean opened = false;
                 for (SignatureAndHashAlgorithm signAlg : algorithms) {
                     if (opened) {
                         sb.append(", ").append(signAlg.getAlgorithmName());

@@ -1574,11 +1702,11 @@
 
         this.protocolVersion = protocolVersion;
 
         String algorithm = privateKey.getAlgorithm();
         Signature sig = null;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             this.preferableSignatureAlgorithm = signAlgorithm;
             sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
         } else {
             sig = getSignature(protocolVersion, algorithm);
         }

@@ -1596,11 +1724,11 @@
             ProtocolVersion protocolVersion) throws IOException  {
 
         this.protocolVersion = protocolVersion;
 
         // read the signature and hash algorithm
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             int hashAlg = input.getInt8();         // hash algorithm
             int signAlg = input.getInt8();         // signature algorithm
 
             preferableSignatureAlgorithm =
                 SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0);

@@ -1632,11 +1760,11 @@
     boolean verify(ProtocolVersion protocolVersion,
             HandshakeHash handshakeHash, PublicKey publicKey,
             SecretKey masterSecret) throws GeneralSecurityException {
         String algorithm = publicKey.getAlgorithm();
         Signature sig = null;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             sig = JsseJce.getSignature(
                         preferableSignatureAlgorithm.getAlgorithmName());
         } else {
             sig = getSignature(protocolVersion, algorithm);
         }

@@ -1674,15 +1802,15 @@
             ProtocolVersion protocolVersion,
             HandshakeHash handshakeHash, String algorithm, SecretKey masterKey)
             throws SignatureException {
 
         if (algorithm.equals("RSA")) {
-            if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+            if (!protocolVersion.useTLS12PlusSpec()) {  // TLS1.1-
                 MessageDigest md5Clone = handshakeHash.getMD5Clone();
                 MessageDigest shaClone = handshakeHash.getSHAClone();
 
-                if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+                if (!protocolVersion.useTLS10PlusSpec()) {  // SSLv3
                     updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
                     updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
                 }
 
                 // The signature must be an instance of RSASignature, need

@@ -1690,14 +1818,14 @@
                 RSASignature.setHashes(sig, md5Clone, shaClone);
             } else {  // TLS1.2+
                 sig.update(handshakeHash.getAllHandshakeMessages());
             }
         } else { // DSA, ECDSA
-            if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1-
+            if (!protocolVersion.useTLS12PlusSpec()) {  // TLS1.1-
                 MessageDigest shaClone = handshakeHash.getSHAClone();
 
-                if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3
+                if (!protocolVersion.useTLS10PlusSpec()) {  // SSLv3
                     updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
                 }
 
                 sig.update(shaClone.digest());
             } else {  // TLS1.2+

@@ -1809,20 +1937,20 @@
 
     @Override
     int messageLength() {
         int temp = 2;
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             temp += SignatureAndHashAlgorithm.sizeInRecord();
         }
 
         return temp + signature.length;
     }
 
     @Override
     void send(HandshakeOutStream s) throws IOException {
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             s.putInt8(preferableSignatureAlgorithm.getHashValue());
             s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
         }
 
         s.putBytes16(signature);

@@ -1831,11 +1959,11 @@
     @Override
     void print(PrintStream s) throws IOException {
         s.println("*** CertificateVerify");
 
         if (debug != null && Debug.isOn("verbose")) {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 s.println("Signature Algorithm " +
                         preferableSignatureAlgorithm.getAlgorithmName());
             }
         }
     }

@@ -1897,11 +2025,11 @@
      */
     Finished(ProtocolVersion protocolVersion, HandshakeInStream input,
             CipherSuite cipherSuite) throws IOException {
         this.protocolVersion = protocolVersion;
         this.cipherSuite = cipherSuite;
-        int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36;
+        int msgLen = protocolVersion.useTLS10PlusSpec() ?  12 : 36;
         verifyData = new byte[msgLen];
         input.read(verifyData);
     }
 
     /*

@@ -1930,26 +2058,26 @@
             tlsLabel = "server finished";
         } else {
             throw new RuntimeException("Invalid sender: " + sender);
         }
 
-        if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+        if (protocolVersion.useTLS10PlusSpec()) {
             // TLS 1.0+
             try {
                 byte [] seed;
                 String prfAlg;
                 PRF prf;
 
                 // Get the KeyGenerator alg and calculate the seed.
-                if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
-                    // TLS 1.2
+                if (protocolVersion.useTLS12PlusSpec()) {
+                    // TLS 1.2+ or DTLS 1.2+
                     seed = handshakeHash.getFinishedHash();
 
                     prfAlg = "SunTls12Prf";
                     prf = cipherSuite.prfAlg;
                 } else {
-                    // TLS 1.0/1.1
+                    // TLS 1.0/1.1, DTLS 1.0
                     MessageDigest md5Clone = handshakeHash.getMD5Clone();
                     MessageDigest shaClone = handshakeHash.getSHAClone();
                     seed = new byte[36];
                     md5Clone.digest(seed, 0, 16);
                     shaClone.digest(seed, 16, 20);