1 package org.bouncycastle.tls;
2 
3 import java.util.Vector;
4 
5 import org.bouncycastle.util.Strings;
6 
7 public final class ProtocolVersion
8 {
9     public static final ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0");
10     public static final ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0");
11     public static final ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1");
12     public static final ProtocolVersion TLSv12 = new ProtocolVersion(0x0303, "TLS 1.2");
13     public static final ProtocolVersion TLSv13 = new ProtocolVersion(0x0304, "TLS 1.3");
14     public static final ProtocolVersion DTLSv10 = new ProtocolVersion(0xFEFF, "DTLS 1.0");
15     public static final ProtocolVersion DTLSv12 = new ProtocolVersion(0xFEFD, "DTLS 1.2");
16 
17     static final ProtocolVersion CLIENT_EARLIEST_SUPPORTED_DTLS = DTLSv10;
18     static final ProtocolVersion CLIENT_EARLIEST_SUPPORTED_TLS = SSLv3;
19     static final ProtocolVersion CLIENT_LATEST_SUPPORTED_DTLS = DTLSv12;
20     static final ProtocolVersion CLIENT_LATEST_SUPPORTED_TLS = TLSv13;
21 
22     static final ProtocolVersion SERVER_EARLIEST_SUPPORTED_DTLS = DTLSv10;
23     static final ProtocolVersion SERVER_EARLIEST_SUPPORTED_TLS = SSLv3;
24     static final ProtocolVersion SERVER_LATEST_SUPPORTED_DTLS = DTLSv12;
25     static final ProtocolVersion SERVER_LATEST_SUPPORTED_TLS = TLSv13;
26 
contains(ProtocolVersion[] versions, ProtocolVersion version)27     public static boolean contains(ProtocolVersion[] versions, ProtocolVersion version)
28     {
29         if (versions != null && version != null)
30         {
31             for (int i = 0; i < versions.length; ++i)
32             {
33                 if (version.equals(versions[i]))
34                 {
35                     return true;
36                 }
37             }
38         }
39         return false;
40     }
41 
getEarliestDTLS(ProtocolVersion[] versions)42     public static ProtocolVersion getEarliestDTLS(ProtocolVersion[] versions)
43     {
44         ProtocolVersion earliest = null;
45         if (null != versions)
46         {
47             for (int i = 0; i < versions.length; ++i)
48             {
49                 ProtocolVersion next = versions[i];
50                 if (null != next && next.isDTLS())
51                 {
52                     if (null == earliest || next.getMinorVersion() > earliest.getMinorVersion())
53                     {
54                         earliest = next;
55                     }
56                 }
57             }
58         }
59         return earliest;
60     }
61 
getEarliestTLS(ProtocolVersion[] versions)62     public static ProtocolVersion getEarliestTLS(ProtocolVersion[] versions)
63     {
64         ProtocolVersion earliest = null;
65         if (null != versions)
66         {
67             for (int i = 0; i < versions.length; ++i)
68             {
69                 ProtocolVersion next = versions[i];
70                 if (null != next && next.isTLS())
71                 {
72                     if (null == earliest || next.getMinorVersion() < earliest.getMinorVersion())
73                     {
74                         earliest = next;
75                     }
76                 }
77             }
78         }
79         return earliest;
80     }
81 
getLatestDTLS(ProtocolVersion[] versions)82     public static ProtocolVersion getLatestDTLS(ProtocolVersion[] versions)
83     {
84         ProtocolVersion latest = null;
85         if (null != versions)
86         {
87             for (int i = 0; i < versions.length; ++i)
88             {
89                 ProtocolVersion next = versions[i];
90                 if (null != next && next.isDTLS())
91                 {
92                     if (null == latest || next.getMinorVersion() < latest.getMinorVersion())
93                     {
94                         latest = next;
95                     }
96                 }
97             }
98         }
99         return latest;
100     }
101 
getLatestTLS(ProtocolVersion[] versions)102     public static ProtocolVersion getLatestTLS(ProtocolVersion[] versions)
103     {
104         ProtocolVersion latest = null;
105         if (null != versions)
106         {
107             for (int i = 0; i < versions.length; ++i)
108             {
109                 ProtocolVersion next = versions[i];
110                 if (null != next && next.isTLS())
111                 {
112                     if (null == latest || next.getMinorVersion() > latest.getMinorVersion())
113                     {
114                         latest = next;
115                     }
116                 }
117             }
118         }
119         return latest;
120     }
121 
isSupportedDTLSVersionClient(ProtocolVersion version)122     static boolean isSupportedDTLSVersionClient(ProtocolVersion version)
123     {
124         return null != version
125             && version.isEqualOrLaterVersionOf(CLIENT_EARLIEST_SUPPORTED_DTLS)
126             && version.isEqualOrEarlierVersionOf(CLIENT_LATEST_SUPPORTED_DTLS);
127     }
128 
isSupportedDTLSVersionServer(ProtocolVersion version)129     static boolean isSupportedDTLSVersionServer(ProtocolVersion version)
130     {
131         return null != version
132             && version.isEqualOrLaterVersionOf(SERVER_EARLIEST_SUPPORTED_DTLS)
133             && version.isEqualOrEarlierVersionOf(SERVER_LATEST_SUPPORTED_DTLS);
134     }
135 
isSupportedTLSVersionClient(ProtocolVersion version)136     static boolean isSupportedTLSVersionClient(ProtocolVersion version)
137     {
138         if (null == version)
139         {
140             return false;
141         }
142 
143         int fullVersion = version.getFullVersion();
144 
145         return fullVersion >= CLIENT_EARLIEST_SUPPORTED_TLS.getFullVersion()
146             && fullVersion <= CLIENT_LATEST_SUPPORTED_TLS.getFullVersion();
147     }
148 
isSupportedTLSVersionServer(ProtocolVersion version)149     static boolean isSupportedTLSVersionServer(ProtocolVersion version)
150     {
151         if (null == version)
152         {
153             return false;
154         }
155 
156         int fullVersion = version.getFullVersion();
157 
158         return fullVersion >= SERVER_EARLIEST_SUPPORTED_TLS.getFullVersion()
159             && fullVersion <= SERVER_LATEST_SUPPORTED_TLS.getFullVersion();
160     }
161 
162     private int version;
163     private String name;
164 
ProtocolVersion(int v, String name)165     private ProtocolVersion(int v, String name)
166     {
167         this.version = v & 0xFFFF;
168         this.name = name;
169     }
170 
downTo(ProtocolVersion min)171     public ProtocolVersion[] downTo(ProtocolVersion min)
172     {
173         if (!isEqualOrLaterVersionOf(min))
174         {
175             throw new IllegalArgumentException("'min' must be an equal or earlier version of this one");
176         }
177 
178         Vector result = new Vector();
179         result.addElement(this);
180 
181         ProtocolVersion current = this;
182         while (!current.equals(min))
183         {
184             current = current.getPreviousVersion();
185             result.addElement(current);
186         }
187 
188         ProtocolVersion[] versions = new ProtocolVersion[result.size()];
189         for (int i = 0; i < result.size(); ++i)
190         {
191             versions[i] = (ProtocolVersion)result.elementAt(i);
192         }
193         return versions;
194     }
195 
getFullVersion()196     public int getFullVersion()
197     {
198         return version;
199     }
200 
getMajorVersion()201     public int getMajorVersion()
202     {
203         return version >> 8;
204     }
205 
getMinorVersion()206     public int getMinorVersion()
207     {
208         return version & 0xFF;
209     }
210 
getName()211     public String getName()
212     {
213         return name;
214     }
215 
isDTLS()216     public boolean isDTLS()
217     {
218         return getMajorVersion() == 0xFE;
219     }
220 
isSSL()221     public boolean isSSL()
222     {
223         return this == SSLv3;
224     }
225 
isTLS()226     public boolean isTLS()
227     {
228         return getMajorVersion() == 0x03;
229     }
230 
getEquivalentTLSVersion()231     public ProtocolVersion getEquivalentTLSVersion()
232     {
233         switch (getMajorVersion())
234         {
235         case 0x03:  return this;
236         case 0xFE:
237             switch(getMinorVersion())
238             {
239             case 0xFF:  return TLSv11;
240             case 0xFD:  return TLSv12;
241             default:    return null;
242             }
243         default:    return null;
244         }
245     }
246 
getNextVersion()247     public ProtocolVersion getNextVersion()
248     {
249         int major = getMajorVersion(), minor = getMinorVersion();
250         switch (major)
251         {
252         case 0x03:
253             switch (minor)
254             {
255             case 0xFF: return null;
256             default  : return get(major, minor + 1);
257             }
258         case 0xFE:
259             switch(minor)
260             {
261             case 0x00: return null;
262             case 0xFF: return DTLSv12;
263             default  : return get(major, minor - 1);
264             }
265         default:    return null;
266         }
267     }
268 
getPreviousVersion()269     public ProtocolVersion getPreviousVersion()
270     {
271         int major = getMajorVersion(), minor = getMinorVersion();
272         switch (major)
273         {
274         case 0x03:
275             switch (minor)
276             {
277             case 0x00: return null;
278             default  : return get(major, minor - 1);
279             }
280         case 0xFE:
281             switch(minor)
282             {
283             case 0xFF: return null;
284             case 0xFD: return DTLSv10;
285             default  : return get(major, minor + 1);
286             }
287         default:    return null;
288         }
289     }
290 
isEarlierVersionOf(ProtocolVersion version)291     public boolean isEarlierVersionOf(ProtocolVersion version)
292     {
293         if (null == version || getMajorVersion() != version.getMajorVersion())
294         {
295             return false;
296         }
297         int diffMinorVersion = getMinorVersion() - version.getMinorVersion();
298         return isDTLS() ? diffMinorVersion > 0 : diffMinorVersion < 0;
299     }
300 
isEqualOrEarlierVersionOf(ProtocolVersion version)301     public boolean isEqualOrEarlierVersionOf(ProtocolVersion version)
302     {
303         if (null == version || getMajorVersion() != version.getMajorVersion())
304         {
305             return false;
306         }
307         int diffMinorVersion = getMinorVersion() - version.getMinorVersion();
308         return isDTLS() ? diffMinorVersion >= 0 : diffMinorVersion <= 0;
309     }
310 
isEqualOrLaterVersionOf(ProtocolVersion version)311     public boolean isEqualOrLaterVersionOf(ProtocolVersion version)
312     {
313         if (null == version || getMajorVersion() != version.getMajorVersion())
314         {
315             return false;
316         }
317         int diffMinorVersion = getMinorVersion() - version.getMinorVersion();
318         return isDTLS() ? diffMinorVersion <= 0 : diffMinorVersion >= 0;
319     }
320 
isLaterVersionOf(ProtocolVersion version)321     public boolean isLaterVersionOf(ProtocolVersion version)
322     {
323         if (null == version || getMajorVersion() != version.getMajorVersion())
324         {
325             return false;
326         }
327         int diffMinorVersion = getMinorVersion() - version.getMinorVersion();
328         return isDTLS() ? diffMinorVersion < 0 : diffMinorVersion > 0;
329     }
330 
equals(Object other)331     public boolean equals(Object other)
332     {
333         return this == other || (other instanceof ProtocolVersion && equals((ProtocolVersion)other));
334     }
335 
equals(ProtocolVersion other)336     public boolean equals(ProtocolVersion other)
337     {
338         return other != null && this.version == other.version;
339     }
340 
hashCode()341     public int hashCode()
342     {
343         return version;
344     }
345 
get(int major, int minor)346     public static ProtocolVersion get(int major, int minor)
347     {
348         switch (major)
349         {
350         case 0x03:
351         {
352             switch (minor)
353             {
354             case 0x00:
355                 return SSLv3;
356             case 0x01:
357                 return TLSv10;
358             case 0x02:
359                 return TLSv11;
360             case 0x03:
361                 return TLSv12;
362             case 0x04:
363                 return TLSv13;
364             }
365             return getUnknownVersion(major, minor, "TLS");
366         }
367         case 0xFE:
368         {
369             switch (minor)
370             {
371             case 0xFF:
372                 return DTLSv10;
373             case 0xFE:
374                 throw new IllegalArgumentException("{0xFE, 0xFE} is a reserved protocol version");
375             case 0xFD:
376                 return DTLSv12;
377             }
378             return getUnknownVersion(major, minor, "DTLS");
379         }
380         default:
381         {
382             return getUnknownVersion(major, minor, "UNKNOWN");
383         }
384         }
385     }
386 
only()387     public ProtocolVersion[] only()
388     {
389         return new ProtocolVersion[]{ this };
390     }
391 
toString()392     public String toString()
393     {
394         return name;
395     }
396 
checkUint8(int versionOctet)397     private static void checkUint8(int versionOctet)
398     {
399         if (!TlsUtils.isValidUint8(versionOctet))
400         {
401             throw new IllegalArgumentException("'versionOctet' is not a valid octet");
402         }
403     }
404 
getUnknownVersion(int major, int minor, String prefix)405     private static ProtocolVersion getUnknownVersion(int major, int minor, String prefix)
406     {
407         checkUint8(major);
408         checkUint8(minor);
409 
410         int v = (major << 8) | minor;
411         String hex = Strings.toUpperCase(Integer.toHexString(0x10000 | v).substring(1));
412         return new ProtocolVersion(v, prefix + " 0x" + hex);
413     }
414 }
415