ProtocolVersion.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 2002, 2015, 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
26package sun.security.ssl;
27
28import java.util.*;
29import java.security.CryptoPrimitive;
30import sun.security.ssl.CipherSuite.*;
31
32/**
33 * Type safe enum for an SSL/TLS protocol version. Instances are obtained
34 * using the static factory methods or by referencing the static members
35 * in this class. Member variables are final and can be accessed without
36 * accessor methods.
37 *
38 * There is only ever one instance per supported protocol version, this
39 * means == can be used for comparision instead of equals() if desired.
40 *
41 * Checks for a particular version number should generally take this form:
42 *
43 * <pre>{@code
44 * if (protocolVersion.v >= ProtocolVersion.TLS10) {
45 *   // TLS 1.0 code goes here
46 * } else {
47 *   // SSL 3.0 code here
48 * }
49 * }</pre>
50 *
51 * @author  Andreas Sterbenz
52 * @since   1.4.1
53 */
54public final class ProtocolVersion implements Comparable<ProtocolVersion> {
55
56    // The limit of maximum protocol version
57    static final int LIMIT_MAX_VALUE = 0xFFFF;
58
59    // The limit of minimum protocol version
60    static final int LIMIT_MIN_VALUE = 0x0000;
61
62    // Dummy protocol version value for invalid SSLSession
63    static final ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
64
65    // If enabled, send/accept SSLv2 hello messages
66    static final ProtocolVersion SSL20Hello =
67                                new ProtocolVersion(0x0002, "SSLv2Hello");
68
69    // SSL 3.0
70    static final ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
71
72    // TLS 1.0
73    static final ProtocolVersion TLS10 = new ProtocolVersion(0x0301, "TLSv1");
74
75    // TLS 1.1
76    static final ProtocolVersion TLS11 = new ProtocolVersion(0x0302, "TLSv1.1");
77
78    // TLS 1.2
79    static final ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
80
81    // DTLS 1.0
82    // {254, 255}, the version value of DTLS 1.0.
83    static final ProtocolVersion DTLS10 =
84                                new ProtocolVersion(0xFEFF, "DTLSv1.0");
85
86    // No DTLS 1.1, that version number was skipped in order to harmonize
87    // version numbers with TLS.
88
89    // DTLS 1.2
90    // {254, 253}, the version value of DTLS 1.2.
91    static final ProtocolVersion DTLS12 =
92                                new ProtocolVersion(0xFEFD, "DTLSv1.2");
93
94    private static final boolean FIPS = SunJSSE.isFIPS();
95
96    // minimum version we implement (SSL 3.0)
97    static final ProtocolVersion MIN = FIPS ? TLS10 : SSL30;
98
99    // maximum version we implement (TLS 1.2)
100    static final ProtocolVersion MAX = TLS12;
101
102    // SSL/TLS ProtocolVersion to use by default (TLS 1.2)
103    static final ProtocolVersion DEFAULT_TLS = TLS12;
104
105    // DTLS ProtocolVersion to use by default (TLS 1.2)
106    static final ProtocolVersion DEFAULT_DTLS = DTLS12;
107
108    // Default version for hello messages (SSLv2Hello)
109    static final ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
110
111    // Available protocols
112    //
113    // Including all supported protocols except the disabled ones.
114    static final Set<ProtocolVersion> availableProtocols;
115
116    // version in 16 bit MSB format as it appears in records and
117    // messages, i.e. 0x0301 for TLS 1.0
118    public final int v;
119
120    // major and minor version
121    public final byte major, minor;
122
123    // name used in JSSE (e.g. TLSv1 for TLS 1.0)
124    final String name;
125
126    // Initialize the available protocols.
127    static {
128        Set<ProtocolVersion> protocols = new HashSet<>(7);
129
130        ProtocolVersion[] pvs = new ProtocolVersion[] {
131                SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
132        EnumSet<CryptoPrimitive> cryptoPrimitives =
133            EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
134        for (ProtocolVersion p : pvs) {
135            if (SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits(
136                    cryptoPrimitives, p.name, null)) {
137                protocols.add(p);
138            }
139        }
140
141        availableProtocols =
142                Collections.<ProtocolVersion>unmodifiableSet(protocols);
143    }
144
145    // private
146    private ProtocolVersion(int v, String name) {
147        this.v = v;
148        this.name = name;
149        major = (byte)(v >>> 8);
150        minor = (byte)(v & 0xFF);
151    }
152
153    // private
154    private static ProtocolVersion valueOf(int v) {
155        if (v == SSL30.v) {
156            return SSL30;
157        } else if (v == TLS10.v) {
158            return TLS10;
159        } else if (v == TLS11.v) {
160            return TLS11;
161        } else if (v == TLS12.v) {
162            return TLS12;
163        } else if (v == SSL20Hello.v) {
164            return SSL20Hello;
165        } else if (v == DTLS10.v) {
166            return DTLS10;
167        } else if (v == DTLS12.v) {
168            return DTLS12;
169        } else {
170            int major = (v >>> 8) & 0xFF;
171            int minor = v & 0xFF;
172            return new ProtocolVersion(v, "Unknown-" + major + "." + minor);
173        }
174    }
175
176    /**
177     * Return a ProtocolVersion with the specified major and minor version
178     * numbers. Never throws exceptions.
179     */
180    public static ProtocolVersion valueOf(int major, int minor) {
181        return valueOf(((major & 0xFF) << 8) | (minor & 0xFF));
182    }
183
184    /**
185     * Return a ProtocolVersion for the given name.
186     *
187     * @exception IllegalArgumentException if name is null or does not
188     * identify a supported protocol
189     */
190    static ProtocolVersion valueOf(String name) {
191        if (name == null) {
192            throw new IllegalArgumentException("Protocol cannot be null");
193        }
194
195        if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
196            throw new IllegalArgumentException(
197                    "Only TLS 1.0 or later allowed in FIPS mode");
198        }
199
200        if (name.equals(SSL30.name)) {
201            return SSL30;
202        } else if (name.equals(TLS10.name)) {
203            return TLS10;
204        } else if (name.equals(TLS11.name)) {
205            return TLS11;
206        } else if (name.equals(TLS12.name)) {
207            return TLS12;
208        } else if (name.equals(SSL20Hello.name)) {
209            return SSL20Hello;
210        } else if (name.equals(DTLS10.name)) {
211            return DTLS10;
212        } else if (name.equals(DTLS12.name)) {
213            return DTLS12;
214        } else {
215            throw new IllegalArgumentException(name);
216        }
217    }
218
219    @Override
220    public String toString() {
221        return name;
222    }
223
224    /**
225     * Compares this object with the specified object for order.
226     */
227    @Override
228    public int compareTo(ProtocolVersion protocolVersion) {
229        if (maybeDTLSProtocol()) {
230            if (!protocolVersion.maybeDTLSProtocol()) {
231                throw new IllegalArgumentException("Not DTLS protocol");
232            }
233
234            return protocolVersion.v - this.v;
235        } else {
236            if (protocolVersion.maybeDTLSProtocol()) {
237                throw new IllegalArgumentException("Not TLS protocol");
238            }
239
240            return this.v - protocolVersion.v;
241        }
242    }
243
244    /**
245     * Returns true if a ProtocolVersion represents a DTLS protocol.
246     */
247    boolean isDTLSProtocol() {
248        return this.v == DTLS12.v || this.v == DTLS10.v;
249    }
250
251    /**
252     * Returns true if a ProtocolVersion may represent a DTLS protocol.
253     */
254    boolean maybeDTLSProtocol() {
255        return (this.major & 0x80) != 0;
256    }
257
258    boolean useTLS12PlusSpec() {
259        return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
260    }
261
262    boolean useTLS11PlusSpec() {
263        return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
264    }
265
266    boolean useTLS10PlusSpec() {
267        return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
268    }
269
270    boolean obsoletes(CipherSuite suite) {
271        ProtocolVersion proto = this;
272        if (proto.isDTLSProtocol()) {
273            // DTLS bans stream ciphers.
274            if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
275                return true;
276            }
277
278            proto = mapToTLSProtocol(this);
279        }
280
281        return (proto.v >= suite.obsoleted);
282    }
283
284    boolean supports(CipherSuite suite) {
285        ProtocolVersion proto = this;
286        if (proto.isDTLSProtocol()) {
287            // DTLS bans stream ciphers.
288            if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
289                return false;
290            }
291
292            proto = mapToTLSProtocol(this);
293        }
294
295        return (proto.v >= suite.supported);
296    }
297
298    // Map a specified protocol to the corresponding TLS version.
299    //
300    // DTLS 1.2 -> TLS 1.2
301    // DTLS 1.0 -> TLS 1.1
302    private static ProtocolVersion mapToTLSProtocol(
303            ProtocolVersion protocolVersion) {
304
305        if (protocolVersion.isDTLSProtocol()) {
306            if (protocolVersion.v == DTLS10.v) {
307                protocolVersion = TLS11;
308            } else {    // DTLS12
309                protocolVersion = TLS12;
310            }
311        }
312
313        return protocolVersion;
314    }
315}
316