1 /*
2  * Copyright (c) 2015, 2018, 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 
26 package sun.security.ssl;
27 
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30 import java.text.MessageFormat;
31 import java.util.Locale;
32 import javax.net.ssl.SSLProtocolException;
33 import static sun.security.ssl.SSLExtension.CH_SUPPORTED_VERSIONS;
34 import sun.security.ssl.SSLExtension.ExtensionConsumer;
35 import static sun.security.ssl.SSLExtension.HRR_SUPPORTED_VERSIONS;
36 import static sun.security.ssl.SSLExtension.SH_SUPPORTED_VERSIONS;
37 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
38 import sun.security.ssl.SSLHandshake.HandshakeMessage;
39 
40 /**
41  * Pack of the "supported_versions" extensions.
42  */
43 final class SupportedVersionsExtension {
44     static final HandshakeProducer chNetworkProducer =
45             new CHSupportedVersionsProducer();
46     static final ExtensionConsumer chOnLoadConsumer =
47             new CHSupportedVersionsConsumer();
48     static final SSLStringizer chStringizer =
49             new CHSupportedVersionsStringizer();
50 
51     static final HandshakeProducer shNetworkProducer =
52             new SHSupportedVersionsProducer();
53     static final ExtensionConsumer shOnLoadConsumer =
54             new SHSupportedVersionsConsumer();
55     static final SSLStringizer shStringizer =
56             new SHSupportedVersionsStringizer();
57 
58     static final HandshakeProducer hrrNetworkProducer =
59             new HRRSupportedVersionsProducer();
60     static final ExtensionConsumer hrrOnLoadConsumer =
61             new HRRSupportedVersionsConsumer();
62     static final HandshakeProducer hrrReproducer =
63             new HRRSupportedVersionsReproducer();
64     static final SSLStringizer hrrStringizer =
65             new SHSupportedVersionsStringizer();
66     /**
67      * The "supported_versions" extension in ClientHello.
68      */
69     static final class CHSupportedVersionsSpec implements SSLExtensionSpec {
70         final int[] requestedProtocols;
71 
CHSupportedVersionsSpec(int[] requestedProtocols)72         private CHSupportedVersionsSpec(int[] requestedProtocols) {
73             this.requestedProtocols = requestedProtocols;
74         }
75 
CHSupportedVersionsSpec(ByteBuffer m)76         private CHSupportedVersionsSpec(ByteBuffer m) throws IOException  {
77             if (m.remaining() < 3) {        //  1: the length of the list
78                                             // +2: one version at least
79                 throw new SSLProtocolException(
80                     "Invalid supported_versions extension: insufficient data");
81             }
82 
83             byte[] vbs = Record.getBytes8(m);   // Get the version bytes.
84             if (m.hasRemaining()) {
85                 throw new SSLProtocolException(
86                     "Invalid supported_versions extension: unknown extra data");
87             }
88 
89             if (vbs == null || vbs.length == 0 || (vbs.length & 0x01) != 0) {
90                 throw new SSLProtocolException(
91                     "Invalid supported_versions extension: incomplete data");
92             }
93 
94             int[] protocols = new int[vbs.length >> 1];
95             for (int i = 0, j = 0; i < vbs.length;) {
96                 byte major = vbs[i++];
97                 byte minor = vbs[i++];
98                 protocols[j++] = ((major & 0xFF) << 8) | (minor & 0xFF);
99             }
100 
101             this.requestedProtocols = protocols;
102         }
103 
104         @Override
toString()105         public String toString() {
106             MessageFormat messageFormat = new MessageFormat(
107                 "\"versions\": '['{0}']'", Locale.ENGLISH);
108 
109             if (requestedProtocols == null || requestedProtocols.length == 0) {
110                 Object[] messageFields = {
111                         "<no supported version specified>"
112                     };
113                 return messageFormat.format(messageFields);
114             } else {
115                 StringBuilder builder = new StringBuilder(512);
116                 boolean isFirst = true;
117                 for (int pv : requestedProtocols) {
118                     if (isFirst) {
119                         isFirst = false;
120                     } else {
121                         builder.append(", ");
122                     }
123 
124                     builder.append(ProtocolVersion.nameOf(pv));
125                 }
126 
127                 Object[] messageFields = {
128                         builder.toString()
129                     };
130 
131                 return messageFormat.format(messageFields);
132             }
133         }
134     }
135 
136     private static final
137             class CHSupportedVersionsStringizer implements SSLStringizer {
138         @Override
toString(ByteBuffer buffer)139         public String toString(ByteBuffer buffer) {
140             try {
141                 return (new CHSupportedVersionsSpec(buffer)).toString();
142             } catch (IOException ioe) {
143                 // For debug logging only, so please swallow exceptions.
144                 return ioe.getMessage();
145             }
146         }
147     }
148 
149     /**
150      * Network data producer of a "supported_versions" extension in ClientHello.
151      */
152     private static final
153             class CHSupportedVersionsProducer implements HandshakeProducer {
154         // Prevent instantiation of this class.
CHSupportedVersionsProducer()155         private CHSupportedVersionsProducer() {
156             // blank
157         }
158 
159         @Override
produce(ConnectionContext context, HandshakeMessage message)160         public byte[] produce(ConnectionContext context,
161                 HandshakeMessage message) throws IOException {
162             // The producing happens in client side only.
163             ClientHandshakeContext chc = (ClientHandshakeContext)context;
164 
165             // Is it a supported and enabled extension?
166             if (!chc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) {
167                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
168                     SSLLogger.fine(
169                         "Ignore unavailable extension: " +
170                         CH_SUPPORTED_VERSIONS.name);
171                 }
172                 return null;
173             }
174 
175             // Produce the extension.
176             //
177             // The activated protocols are used as the supported versions.
178             int[] protocols = new int[chc.activeProtocols.size()];
179             int verLen = protocols.length * 2;
180             byte[] extData = new byte[verLen + 1];      // 1: versions length
181             extData[0] = (byte)(verLen & 0xFF);
182             int i = 0, j = 1;
183             for (ProtocolVersion pv : chc.activeProtocols) {
184                 protocols[i++] = pv.id;
185                 extData[j++] = pv.major;
186                 extData[j++] = pv.minor;
187             }
188 
189             // Update the context.
190             chc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS,
191                     new CHSupportedVersionsSpec(protocols));
192 
193             return extData;
194         }
195     }
196 
197     /**
198      * Network data consumer of a "supported_versions" extension in ClientHello.
199      */
200     private static final
201             class CHSupportedVersionsConsumer implements ExtensionConsumer {
202         // Prevent instantiation of this class.
CHSupportedVersionsConsumer()203         private CHSupportedVersionsConsumer() {
204             // blank
205         }
206 
207         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)208         public void consume(ConnectionContext context,
209             HandshakeMessage message, ByteBuffer buffer) throws IOException {
210             // The consuming happens in server side only.
211             ServerHandshakeContext shc = (ServerHandshakeContext)context;
212 
213             // Is it a supported and enabled extension?
214             if (!shc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) {
215                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
216                     SSLLogger.fine(
217                         "Ignore unavailable extension: " +
218                         CH_SUPPORTED_VERSIONS.name);
219                 }
220                 return;     // ignore the extension
221             }
222 
223             // Parse the extension.
224             CHSupportedVersionsSpec spec;
225             try {
226                 spec = new CHSupportedVersionsSpec(buffer);
227             } catch (IOException ioe) {
228                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
229             }
230 
231             // Update the context.
232             shc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, spec);
233 
234             // No impact on session resumption.
235             //
236             // Note that the protocol version negotiation happens before the
237             // session resumption negotiation.  And the session resumption
238             // negotiation depends on the negotiated protocol version.
239         }
240     }
241 
242     /**
243      * The "supported_versions" extension in ServerHello and HelloRetryRequest.
244      */
245     static final class SHSupportedVersionsSpec implements SSLExtensionSpec {
246         final int selectedVersion;
247 
SHSupportedVersionsSpec(ProtocolVersion selectedVersion)248         private SHSupportedVersionsSpec(ProtocolVersion selectedVersion) {
249             this.selectedVersion = selectedVersion.id;
250         }
251 
SHSupportedVersionsSpec(ByteBuffer m)252         private SHSupportedVersionsSpec(ByteBuffer m) throws IOException  {
253             if (m.remaining() != 2) {       // 2: the selected version
254                 throw new SSLProtocolException(
255                     "Invalid supported_versions: insufficient data");
256             }
257 
258             byte major = m.get();
259             byte minor = m.get();
260             this.selectedVersion = ((major & 0xFF) << 8) | (minor & 0xFF);
261         }
262 
263         @Override
toString()264         public String toString() {
265             MessageFormat messageFormat = new MessageFormat(
266                 "\"selected version\": '['{0}']'", Locale.ENGLISH);
267 
268             Object[] messageFields = {
269                     ProtocolVersion.nameOf(selectedVersion)
270                 };
271             return messageFormat.format(messageFields);
272         }
273     }
274 
275     private static final
276             class SHSupportedVersionsStringizer implements SSLStringizer {
277         @Override
toString(ByteBuffer buffer)278         public String toString(ByteBuffer buffer) {
279             try {
280                 return (new SHSupportedVersionsSpec(buffer)).toString();
281             } catch (IOException ioe) {
282                 // For debug logging only, so please swallow exceptions.
283                 return ioe.getMessage();
284             }
285         }
286     }
287 
288     /**
289      * Network data producer of a "supported_versions" extension in ServerHello.
290      */
291     private static final
292             class SHSupportedVersionsProducer implements HandshakeProducer {
293         // Prevent instantiation of this class.
SHSupportedVersionsProducer()294         private SHSupportedVersionsProducer() {
295             // blank
296         }
297 
298         @Override
produce(ConnectionContext context, HandshakeMessage message)299         public byte[] produce(ConnectionContext context,
300                 HandshakeMessage message) throws IOException {
301             // The producing happens in server side only.
302             ServerHandshakeContext shc = (ServerHandshakeContext)context;
303 
304             // In response to supported_versions request only
305             CHSupportedVersionsSpec svs = (CHSupportedVersionsSpec)
306                     shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS);
307             if (svs == null) {
308                 // Unlikely, no key_share extension requested.
309                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
310                     SSLLogger.warning(
311                             "Ignore unavailable supported_versions extension");
312                 }
313                 return null;
314             }
315 
316             // Is it a supported and enabled extension?
317             if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) {
318                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
319                     SSLLogger.fine(
320                         "Ignore unavailable extension: " +
321                         SH_SUPPORTED_VERSIONS.name);
322                 }
323                 return null;
324             }
325 
326             // Produce the extension.
327             byte[] extData = new byte[2];
328             extData[0] = shc.negotiatedProtocol.major;
329             extData[1] = shc.negotiatedProtocol.minor;
330 
331             // Update the context.
332             shc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS,
333                     new SHSupportedVersionsSpec(shc.negotiatedProtocol));
334 
335             return extData;
336         }
337     }
338 
339     /**
340      * Network data consumer of a "supported_versions" extension in ServerHello.
341      */
342     private static final
343             class SHSupportedVersionsConsumer implements ExtensionConsumer {
344         // Prevent instantiation of this class.
SHSupportedVersionsConsumer()345         private SHSupportedVersionsConsumer() {
346             // blank
347         }
348 
349         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)350         public void consume(ConnectionContext context,
351             HandshakeMessage message, ByteBuffer buffer) throws IOException {
352             // The consuming happens in client side only.
353             ClientHandshakeContext chc = (ClientHandshakeContext)context;
354 
355             // Is it a supported and enabled extension?
356             if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) {
357                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
358                     SSLLogger.fine(
359                         "Ignore unavailable extension: " +
360                         SH_SUPPORTED_VERSIONS.name);
361                 }
362                 return;     // ignore the extension
363             }
364 
365             // Parse the extension.
366             SHSupportedVersionsSpec spec;
367             try {
368                 spec = new SHSupportedVersionsSpec(buffer);
369             } catch (IOException ioe) {
370                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
371             }
372 
373             // Update the context.
374             chc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, spec);
375 
376             // No impact on session resumption.
377             //
378             // Note that the protocol version negotiation happens before the
379             // session resumption negotiation.  And the session resumption
380             // negotiation depends on the negotiated protocol version.
381         }
382     }
383 
384     /**
385      * Network data producer of a "supported_versions" extension in
386      * HelloRetryRequest.
387      */
388     private static final
389             class HRRSupportedVersionsProducer implements HandshakeProducer {
390 
391         // Prevent instantiation of this class.
HRRSupportedVersionsProducer()392         private HRRSupportedVersionsProducer() {
393             // blank
394         }
395 
396         @Override
produce(ConnectionContext context, HandshakeMessage message)397         public byte[] produce(ConnectionContext context,
398                 HandshakeMessage message) throws IOException {
399             // The producing happens in server side only.
400             ServerHandshakeContext shc = (ServerHandshakeContext)context;
401 
402             // Is it a supported and enabled extension?
403             if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) {
404                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
405                     SSLLogger.fine(
406                         "Ignore unavailable extension: " +
407                         HRR_SUPPORTED_VERSIONS.name);
408                 }
409                 return null;
410             }
411 
412             // Produce the extension.
413             byte[] extData = new byte[2];
414             extData[0] = shc.negotiatedProtocol.major;
415             extData[1] = shc.negotiatedProtocol.minor;
416 
417             // Update the context.
418             shc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS,
419                     new SHSupportedVersionsSpec(shc.negotiatedProtocol));
420 
421             return extData;
422         }
423     }
424 
425     /**
426      * Network data consumer of a "supported_versions" extension in
427      * HelloRetryRequest.
428      */
429     private static final
430             class HRRSupportedVersionsConsumer implements ExtensionConsumer {
431 
432         // Prevent instantiation of this class.
HRRSupportedVersionsConsumer()433         private HRRSupportedVersionsConsumer() {
434             // blank
435         }
436 
437         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)438         public void consume(ConnectionContext context,
439             HandshakeMessage message, ByteBuffer buffer) throws IOException {
440 
441             // The consuming happens in client side only.
442             ClientHandshakeContext chc = (ClientHandshakeContext)context;
443 
444             // Is it a supported and enabled extension?
445             if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) {
446                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
447                     SSLLogger.fine(
448                         "Ignore unavailable extension: " +
449                         HRR_SUPPORTED_VERSIONS.name);
450                 }
451                 return;     // ignore the extension
452             }
453 
454             // Parse the extension.
455             SHSupportedVersionsSpec spec;
456             try {
457                 spec = new SHSupportedVersionsSpec(buffer);
458             } catch (IOException ioe) {
459                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
460             }
461 
462             // Update the context.
463             chc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, spec);
464 
465             // No impact on session resumption.
466             //
467             // Note that the protocol version negotiation happens before the
468             // session resumption negotiation.  And the session resumption
469             // negotiation depends on the negotiated protocol version.
470         }
471     }
472 
473     /**
474      * Network data producer of a "supported_versions" extension for stateless
475      * HelloRetryRequest reconstruction.
476      */
477     private static final
478             class HRRSupportedVersionsReproducer implements HandshakeProducer {
479         // Prevent instantiation of this class.
HRRSupportedVersionsReproducer()480         private HRRSupportedVersionsReproducer() {
481             // blank
482         }
483 
484         @Override
produce(ConnectionContext context, HandshakeMessage message)485         public byte[] produce(ConnectionContext context,
486                 HandshakeMessage message) throws IOException {
487             // The producing happens in server side only.
488             ServerHandshakeContext shc = (ServerHandshakeContext)context;
489 
490             // Is it a supported and enabled extension?
491             if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) {
492                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
493                     SSLLogger.fine(
494                         "[Reproduce] Ignore unavailable extension: " +
495                         HRR_SUPPORTED_VERSIONS.name);
496                 }
497                 return null;
498             }
499 
500             // Produce the extension.
501             byte[] extData = new byte[2];
502             extData[0] = shc.negotiatedProtocol.major;
503             extData[1] = shc.negotiatedProtocol.minor;
504 
505             return extData;
506         }
507     }
508 }
509