1 /*
2  * Copyright (c) 2015, 2020, 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.io.ByteArrayInputStream;
30 import java.nio.ByteBuffer;
31 import java.security.cert.Extension;
32 import java.security.cert.CertificateFactory;
33 import java.security.cert.CertificateException;
34 import java.security.cert.X509Certificate;
35 import java.text.MessageFormat;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Locale;
39 import javax.net.ssl.SSLProtocolException;
40 import sun.security.provider.certpath.OCSPResponse;
41 import sun.security.provider.certpath.ResponderId;
42 import sun.security.ssl.SSLExtension.ExtensionConsumer;
43 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
44 import sun.security.ssl.SSLHandshake.HandshakeMessage;
45 import sun.security.util.DerInputStream;
46 import sun.security.util.DerValue;
47 import sun.security.util.HexDumpEncoder;
48 
49 /**
50  * Pack of "status_request" and "status_request_v2" extensions.
51  */
52 final class CertStatusExtension {
53     static final HandshakeProducer chNetworkProducer =
54             new CHCertStatusReqProducer();
55     static final ExtensionConsumer chOnLoadConsumer =
56             new CHCertStatusReqConsumer();
57 
58     static final HandshakeProducer shNetworkProducer =
59             new SHCertStatusReqProducer();
60     static final ExtensionConsumer shOnLoadConsumer =
61             new SHCertStatusReqConsumer();
62 
63     static final HandshakeProducer ctNetworkProducer =
64             new CTCertStatusResponseProducer();
65     static final ExtensionConsumer ctOnLoadConsumer =
66             new CTCertStatusResponseConsumer();
67 
68     static final SSLStringizer certStatusReqStringizer =
69             new CertStatusRequestStringizer();
70 
71     static final HandshakeProducer chV2NetworkProducer =
72             new CHCertStatusReqV2Producer();
73     static final ExtensionConsumer chV2OnLoadConsumer =
74             new CHCertStatusReqV2Consumer();
75 
76     static final HandshakeProducer shV2NetworkProducer =
77             new SHCertStatusReqV2Producer();
78     static final ExtensionConsumer shV2OnLoadConsumer =
79             new SHCertStatusReqV2Consumer();
80 
81     static final SSLStringizer certStatusReqV2Stringizer =
82             new CertStatusRequestsStringizer();
83 
84     static final SSLStringizer certStatusRespStringizer =
85             new CertStatusRespStringizer();
86 
87     /**
88      * The "status_request" extension.
89      *
90      * RFC6066 defines the TLS extension,"status_request" (type 0x5),
91      * which allows the client to request that the server perform OCSP
92      * on the client's behalf.
93      *
94      * The "extension data" field of this extension contains a
95      * "CertificateStatusRequest" structure:
96      *
97      *      struct {
98      *          CertificateStatusType status_type;
99      *          select (status_type) {
100      *              case ocsp: OCSPStatusRequest;
101      *          } request;
102      *      } CertificateStatusRequest;
103      *
104      *      enum { ocsp(1), (255) } CertificateStatusType;
105      *
106      *      struct {
107      *          ResponderID responder_id_list<0..2^16-1>;
108      *          Extensions  request_extensions;
109      *      } OCSPStatusRequest;
110      *
111      *      opaque ResponderID<1..2^16-1>;
112      *      opaque Extensions<0..2^16-1>;
113      */
114     static final class CertStatusRequestSpec implements SSLExtensionSpec {
115         static final CertStatusRequestSpec DEFAULT =
116                 new CertStatusRequestSpec(OCSPStatusRequest.EMPTY_OCSP);
117 
118         final CertStatusRequest statusRequest;
119 
CertStatusRequestSpec(CertStatusRequest statusRequest)120         private CertStatusRequestSpec(CertStatusRequest statusRequest) {
121             this.statusRequest = statusRequest;
122         }
123 
CertStatusRequestSpec(ByteBuffer buffer)124         private CertStatusRequestSpec(ByteBuffer buffer) throws IOException {
125             // Is it a empty extension_data?
126             if (buffer.remaining() == 0) {
127                 // server response
128                 this.statusRequest = null;
129                 return;
130             }
131 
132             if (buffer.remaining() < 1) {
133                 throw new SSLProtocolException(
134                     "Invalid status_request extension: insufficient data");
135             }
136 
137             byte statusType = (byte)Record.getInt8(buffer);
138             byte[] encoded = new byte[buffer.remaining()];
139             if (encoded.length != 0) {
140                 buffer.get(encoded);
141             }
142             if (statusType == CertStatusRequestType.OCSP.id) {
143                 this.statusRequest = new OCSPStatusRequest(statusType, encoded);
144             } else {
145                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
146                     SSLLogger.info(
147                         "Unknown certificate status request " +
148                         "(status type: " + statusType + ")");
149                 }
150 
151                 this.statusRequest = new CertStatusRequest(statusType, encoded);
152             }
153         }
154 
155         @Override
toString()156         public String toString() {
157             return statusRequest == null ?
158                         "<empty>" : statusRequest.toString();
159         }
160     }
161 
162     /**
163      * Defines the CertificateStatus response structure as outlined in
164      * RFC 6066.  This will contain a status response type, plus a single,
165      * non-empty OCSP response in DER-encoded form.
166      *
167      * struct {
168      *     CertificateStatusType status_type;
169      *     select (status_type) {
170      *         case ocsp: OCSPResponse;
171      *     } response;
172      * } CertificateStatus;
173      */
174     static final class CertStatusResponseSpec implements SSLExtensionSpec {
175         final CertStatusResponse statusResponse;
176 
CertStatusResponseSpec(CertStatusResponse resp)177         private CertStatusResponseSpec(CertStatusResponse resp) {
178             this.statusResponse = resp;
179         }
180 
CertStatusResponseSpec(ByteBuffer buffer)181         private CertStatusResponseSpec(ByteBuffer buffer) throws IOException {
182             if (buffer.remaining() < 2) {
183                 throw new SSLProtocolException(
184                     "Invalid status_request extension: insufficient data");
185             }
186 
187             // Get the status type (1 byte) and response data (vector)
188             byte type = (byte)Record.getInt8(buffer);
189             byte[] respData = Record.getBytes24(buffer);
190 
191             // Create the CertStatusResponse based on the type
192             if (type == CertStatusRequestType.OCSP.id) {
193                 this.statusResponse = new OCSPStatusResponse(type, respData);
194             } else {
195                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
196                     SSLLogger.info(
197                         "Unknown certificate status response " +
198                         "(status type: " + type + ")");
199                 }
200 
201                 this.statusResponse = new CertStatusResponse(type, respData);
202             }
203         }
204 
205         @Override
toString()206         public String toString() {
207             return statusResponse == null ?
208                         "<empty>" : statusResponse.toString();
209         }
210     }
211 
212     private static final
213             class CertStatusRequestStringizer implements SSLStringizer {
214         @Override
toString(ByteBuffer buffer)215         public String toString(ByteBuffer buffer) {
216             try {
217                 return (new CertStatusRequestSpec(buffer)).toString();
218             } catch (IOException ioe) {
219                 // For debug logging only, so please swallow exceptions.
220                 return ioe.getMessage();
221             }
222         }
223     }
224 
225     private static final
226             class CertStatusRespStringizer implements SSLStringizer {
227         @Override
toString(ByteBuffer buffer)228         public String toString(ByteBuffer buffer) {
229             try {
230                 return (new CertStatusResponseSpec(buffer)).toString();
231             } catch (IOException ioe) {
232                  // For debug logging only, so please swallow exceptions.
233                 return ioe.getMessage();
234             }
235         }
236     }
237 
238     static enum CertStatusRequestType {
239         OCSP        ((byte)0x01,    "ocsp"),        // RFC 6066/6961
240         OCSP_MULTI  ((byte)0x02,    "ocsp_multi");  // RFC 6961
241 
242         final byte id;
243         final String name;
244 
CertStatusRequestType(byte id, String name)245         private CertStatusRequestType(byte id, String name) {
246             this.id = id;
247             this.name = name;
248         }
249 
250         /**
251          * Returns the enum constant of the specified id (see RFC 6066).
252          */
valueOf(byte id)253         static CertStatusRequestType valueOf(byte id) {
254             for (CertStatusRequestType srt : CertStatusRequestType.values()) {
255                 if (srt.id == id) {
256                     return srt;
257                 }
258             }
259 
260             return null;
261         }
262 
nameOf(byte id)263         static String nameOf(byte id) {
264             for (CertStatusRequestType srt : CertStatusRequestType.values()) {
265                 if (srt.id == id) {
266                     return srt.name;
267                 }
268             }
269 
270             return "UNDEFINED-CERT-STATUS-TYPE(" + id + ")";
271         }
272     }
273 
274     static class CertStatusRequest {
275         final byte statusType;
276         final byte[] encodedRequest;
277 
CertStatusRequest(byte statusType, byte[] encodedRequest)278         protected CertStatusRequest(byte statusType, byte[] encodedRequest) {
279             this.statusType = statusType;
280             this.encodedRequest = encodedRequest;
281         }
282 
283         @Override
toString()284         public String toString() {
285             MessageFormat messageFormat = new MessageFormat(
286                 "\"certificate status type\": {0}\n" +
287                 "\"encoded certificate status\": '{'\n" +
288                 "{1}\n" +
289                 "'}'",
290                 Locale.ENGLISH);
291 
292             HexDumpEncoder hexEncoder = new HexDumpEncoder();
293             String encoded = hexEncoder.encodeBuffer(encodedRequest);
294 
295             Object[] messageFields = {
296                 CertStatusRequestType.nameOf(statusType),
297                 Utilities.indent(encoded)
298             };
299 
300             return messageFormat.format(messageFields);
301         }
302     }
303 
304     /*
305      * RFC6066 defines the TLS extension,"status_request" (type 0x5),
306      * which allows the client to request that the server perform OCSP
307      * on the client's behalf.
308      *
309      * The RFC defines an OCSPStatusRequest structure:
310      *
311      *      struct {
312      *          ResponderID responder_id_list<0..2^16-1>;
313      *          Extensions  request_extensions;
314      *      } OCSPStatusRequest;
315      */
316     static final class OCSPStatusRequest extends CertStatusRequest {
317         static final OCSPStatusRequest EMPTY_OCSP;
318         static final OCSPStatusRequest EMPTY_OCSP_MULTI;
319 
320         final List<ResponderId> responderIds;
321         final List<Extension> extensions;
322         private final int ridListLen;
323         private final int extListLen;
324 
325         static {
326             OCSPStatusRequest ocspReq = null;
327             OCSPStatusRequest multiReq = null;
328 
329             try {
330                 ocspReq = new OCSPStatusRequest(
331                         CertStatusRequestType.OCSP.id,
332                         new byte[] {0x00, 0x00, 0x00, 0x00});
333                 multiReq = new OCSPStatusRequest(
334                     CertStatusRequestType.OCSP_MULTI.id,
335                     new byte[] {0x00, 0x00, 0x00, 0x00});
336             } catch (IOException ioe) {
337                 // unlikely
338             }
339 
340             EMPTY_OCSP = ocspReq;
341             EMPTY_OCSP_MULTI = multiReq;
342         }
343 
OCSPStatusRequest(byte statusType, byte[] encoded)344         private OCSPStatusRequest(byte statusType,
345                 byte[] encoded) throws IOException {
346             super(statusType, encoded);
347 
348             if (encoded == null || encoded.length < 4) {
349                                         //  2: length of responder_id_list
350                                         // +2: length of request_extensions
351                 throw new SSLProtocolException(
352                         "Invalid OCSP status request: insufficient data");
353             }
354 
355             List<ResponderId> rids = new ArrayList<>();
356             List<Extension> exts = new ArrayList<>();
357             ByteBuffer m = ByteBuffer.wrap(encoded);
358 
359             this.ridListLen = Record.getInt16(m);
360             if (m.remaining() < (ridListLen + 2)) {
361                 throw new SSLProtocolException(
362                         "Invalid OCSP status request: insufficient data");
363             }
364 
365             int ridListBytesRemaining = ridListLen;
366             while (ridListBytesRemaining >= 2) {    // 2: length of responder_id
367                 byte[] ridBytes = Record.getBytes16(m);
368                 try {
369                     rids.add(new ResponderId(ridBytes));
370                 } catch (IOException ioe) {
371                     throw new SSLProtocolException(
372                         "Invalid OCSP status request: invalid responder ID");
373                 }
374                 ridListBytesRemaining -= ridBytes.length + 2;
375             }
376 
377             if (ridListBytesRemaining != 0) {
378                     throw new SSLProtocolException(
379                         "Invalid OCSP status request: incomplete data");
380             }
381 
382             byte[] extListBytes = Record.getBytes16(m);
383             this.extListLen = extListBytes.length;
384             if (extListLen > 0) {
385                 try {
386                     DerInputStream dis = new DerInputStream(extListBytes);
387                     DerValue[] extSeqContents =
388                             dis.getSequence(extListBytes.length);
389                     for (DerValue extDerVal : extSeqContents) {
390                         exts.add(new sun.security.x509.Extension(extDerVal));
391                     }
392                 } catch (IOException ioe) {
393                     throw new SSLProtocolException(
394                         "Invalid OCSP status request: invalid extension");
395                 }
396             }
397 
398             this.responderIds = rids;
399             this.extensions = exts;
400         }
401 
402         @Override
toString()403         public String toString() {
404             MessageFormat messageFormat = new MessageFormat(
405                 "\"certificate status type\": {0}\n" +
406                 "\"OCSP status request\": '{'\n" +
407                 "{1}\n" +
408                 "'}'",
409                 Locale.ENGLISH);
410 
411             MessageFormat requestFormat = new MessageFormat(
412                 "\"responder_id\": {0}\n" +
413                 "\"request extensions\": '{'\n" +
414                 "{1}\n" +
415                 "'}'",
416                 Locale.ENGLISH);
417 
418             String ridStr = "<empty>";
419             if (!responderIds.isEmpty()) {
420                 ridStr = responderIds.toString();
421             }
422 
423             String extsStr = "<empty>";
424             if (!extensions.isEmpty()) {
425                 StringBuilder extBuilder = new StringBuilder(512);
426                 boolean isFirst = true;
427                 for (Extension ext : this.extensions) {
428                     if (isFirst) {
429                         isFirst = false;
430                     } else {
431                         extBuilder.append(",\n");
432                     }
433                     extBuilder.append("{\n").
434                             append(Utilities.indent(ext.toString())).
435                             append("}");
436                 }
437 
438                 extsStr = extBuilder.toString();
439             }
440 
441             Object[] requestFields = {
442                     ridStr,
443                     Utilities.indent(extsStr)
444                 };
445             String ocspStatusRequest = requestFormat.format(requestFields);
446 
447             Object[] messageFields = {
448                     CertStatusRequestType.nameOf(statusType),
449                     Utilities.indent(ocspStatusRequest)
450                 };
451 
452             return messageFormat.format(messageFields);
453         }
454     }
455 
456     static class CertStatusResponse {
457         final byte statusType;
458         final byte[] encodedResponse;
459 
CertStatusResponse(byte statusType, byte[] respDer)460         protected CertStatusResponse(byte statusType, byte[] respDer) {
461             this.statusType = statusType;
462             this.encodedResponse = respDer;
463         }
464 
toByteArray()465         byte[] toByteArray() throws IOException {
466             // Create a byte array large enough to handle the status_type
467             // field (1) + OCSP length (3) + OCSP data (variable)
468             byte[] outData = new byte[encodedResponse.length + 4];
469             ByteBuffer buf = ByteBuffer.wrap(outData);
470             Record.putInt8(buf, statusType);
471             Record.putBytes24(buf, encodedResponse);
472             return buf.array();
473         }
474 
475         @Override
toString()476         public String toString() {
477             MessageFormat messageFormat = new MessageFormat(
478                 "\"certificate status response type\": {0}\n" +
479                 "\"encoded certificate status\": '{'\n" +
480                 "{1}\n" +
481                 "'}'",
482                 Locale.ENGLISH);
483 
484             HexDumpEncoder hexEncoder = new HexDumpEncoder();
485             String encoded = hexEncoder.encodeBuffer(encodedResponse);
486 
487             Object[] messageFields = {
488                 CertStatusRequestType.nameOf(statusType),
489                 Utilities.indent(encoded)
490             };
491 
492             return messageFormat.format(messageFields);
493         }
494     }
495 
496     static final class OCSPStatusResponse extends CertStatusResponse {
497         final OCSPResponse ocspResponse;
498 
OCSPStatusResponse(byte statusType, byte[] encoded)499         private OCSPStatusResponse(byte statusType,
500                 byte[] encoded) throws IOException {
501             super(statusType, encoded);
502 
503             // The DER-encoded OCSP response must not be zero length
504             if (encoded == null || encoded.length < 1) {
505                 throw new SSLProtocolException(
506                         "Invalid OCSP status response: insufficient data");
507             }
508 
509             // Otherwise, make an OCSPResponse object from the data
510             ocspResponse = new OCSPResponse(encoded);
511         }
512 
513         @Override
toString()514         public String toString() {
515             MessageFormat messageFormat = new MessageFormat(
516                 "\"certificate status response type\": {0}\n" +
517                 "\"OCSP status response\": '{'\n" +
518                 "{1}\n" +
519                 "'}'",
520                 Locale.ENGLISH);
521 
522             Object[] messageFields = {
523                 CertStatusRequestType.nameOf(statusType),
524                 Utilities.indent(ocspResponse.toString())
525             };
526 
527             return messageFormat.format(messageFields);
528         }
529     }
530 
531     /**
532      * Network data producer of a "status_request" extension in the
533      * ClientHello handshake message.
534      */
535     private static final
536             class CHCertStatusReqProducer implements HandshakeProducer {
537         // Prevent instantiation of this class.
CHCertStatusReqProducer()538         private CHCertStatusReqProducer() {
539             // blank
540         }
541 
542         @Override
produce(ConnectionContext context, HandshakeMessage message)543         public byte[] produce(ConnectionContext context,
544                 HandshakeMessage message) throws IOException {
545             // The producing happens in client side only.
546             ClientHandshakeContext chc = (ClientHandshakeContext)context;
547 
548             if (!chc.sslContext.isStaplingEnabled(true)) {
549                 return null;
550             }
551 
552             if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) {
553                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
554                     SSLLogger.fine(
555                         "Ignore unavailable extension: " +
556                         SSLExtension.CH_STATUS_REQUEST.name);
557                 }
558                 return null;
559             }
560 
561             // Produce the extension.
562             //
563             // We are using empty OCSPStatusRequest at present. May extend to
564             // support specific responder or extensions later.
565             byte[] extData = new byte[] {0x01, 0x00, 0x00, 0x00, 0x00};
566 
567             // Update the context.
568             chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST,
569                     CertStatusRequestSpec.DEFAULT);
570 
571             return extData;
572         }
573     }
574 
575     /**
576      * Network data consumer of a "status_request" extension in the
577      * ClientHello handshake message.
578      */
579     private static final
580             class CHCertStatusReqConsumer implements ExtensionConsumer {
581         // Prevent instantiation of this class.
CHCertStatusReqConsumer()582         private CHCertStatusReqConsumer() {
583             // blank
584         }
585 
586         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)587         public void consume(ConnectionContext context,
588             HandshakeMessage message, ByteBuffer buffer) throws IOException {
589 
590             // The consuming happens in server side only.
591             ServerHandshakeContext shc = (ServerHandshakeContext)context;
592 
593             if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) {
594                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
595                     SSLLogger.fine("Ignore unavailable extension: " +
596                         SSLExtension.CH_STATUS_REQUEST.name);
597                 }
598                 return;     // ignore the extension
599             }
600 
601             // Parse the extension.
602             CertStatusRequestSpec spec;
603             try {
604                 spec = new CertStatusRequestSpec(buffer);
605             } catch (IOException ioe) {
606                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
607             }
608 
609             // Update the context.
610             shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST, spec);
611             if (!shc.isResumption &&
612                     !shc.negotiatedProtocol.useTLS13PlusSpec()) {
613                 shc.handshakeProducers.put(SSLHandshake.CERTIFICATE_STATUS.id,
614                     SSLHandshake.CERTIFICATE_STATUS);
615             }   // Otherwise, the certificate status presents in server cert.
616 
617             // No impact on session resumption.
618         }
619     }
620 
621     /**
622      * Network data producer of a "status_request" extension in the
623      * ServerHello handshake message.
624      */
625     private static final
626             class SHCertStatusReqProducer implements HandshakeProducer {
627         // Prevent instantiation of this class.
SHCertStatusReqProducer()628         private SHCertStatusReqProducer() {
629             // blank
630         }
631 
632         @Override
produce(ConnectionContext context, HandshakeMessage message)633         public byte[] produce(ConnectionContext context,
634                 HandshakeMessage message) throws IOException {
635             // The producing happens in client side only.
636             ServerHandshakeContext shc = (ServerHandshakeContext)context;
637 
638             // The StaplingParameters in the ServerHandshakeContext will
639             // contain the info about what kind of stapling (if any) to
640             // perform and whether this status_request extension should be
641             // produced or the status_request_v2 (found in a different producer)
642             // No explicit check is required for isStaplingEnabled here.  If
643             // it is false then stapleParams will be null.  If it is true
644             // then stapleParams may or may not be false and the check below
645             // is sufficient.
646             if ((shc.stapleParams == null) ||
647                     (shc.stapleParams.statusRespExt !=
648                     SSLExtension.CH_STATUS_REQUEST)) {
649                 return null;    // Do not produce status_request in ServerHello
650             }
651 
652             // In response to "status_request" extension request only.
653             CertStatusRequestSpec spec = (CertStatusRequestSpec)
654                     shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST);
655             if (spec == null) {
656                 // Ignore, no status_request extension requested.
657                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
658                     SSLLogger.finest("Ignore unavailable extension: " +
659                         SSLExtension.CH_STATUS_REQUEST.name);
660                 }
661 
662                 return null;        // ignore the extension
663             }
664 
665             // Is it a session resuming?
666             if (shc.isResumption) {
667                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
668                     SSLLogger.finest(
669                         "No status_request response for session resuming");
670                 }
671 
672                 return null;        // ignore the extension
673             }
674 
675             // The "extension_data" in the extended ServerHello handshake
676             // message MUST be empty.
677             byte[] extData = new byte[0];
678 
679             // Update the context.
680             shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST,
681                     CertStatusRequestSpec.DEFAULT);
682 
683             return extData;
684         }
685     }
686 
687     /**
688      * Network data consumer of a "status_request" extension in the
689      * ServerHello handshake message.
690      */
691     private static final
692             class SHCertStatusReqConsumer implements ExtensionConsumer {
693         // Prevent instantiation of this class.
SHCertStatusReqConsumer()694         private SHCertStatusReqConsumer() {
695             // blank
696         }
697 
698         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)699         public void consume(ConnectionContext context,
700             HandshakeMessage message, ByteBuffer buffer) throws IOException {
701 
702             // The producing happens in client side only.
703             ClientHandshakeContext chc = (ClientHandshakeContext)context;
704 
705             // In response to "status_request" extension request only.
706             CertStatusRequestSpec requestedCsr = (CertStatusRequestSpec)
707                     chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST);
708             if (requestedCsr == null) {
709                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
710                     "Unexpected status_request extension in ServerHello");
711             }
712 
713             // Parse the extension.
714             if (buffer.hasRemaining()) {
715                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
716                   "Invalid status_request extension in ServerHello message: " +
717                   "the extension data must be empty");
718             }
719 
720             // Update the context.
721             chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST,
722                     CertStatusRequestSpec.DEFAULT);
723 
724             // Since we've received a legitimate status_request in the
725             // ServerHello, stapling is active if it's been enabled.
726             chc.staplingActive = chc.sslContext.isStaplingEnabled(true);
727             if (chc.staplingActive) {
728                 chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
729                     SSLHandshake.CERTIFICATE_STATUS);
730             }
731 
732             // No impact on session resumption.
733         }
734     }
735 
736     /**
737      * The "status_request_v2" extension.
738      *
739      * RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),
740      * which allows the client to request that the server perform OCSP
741      * on the client's behalf.
742      *
743      * The RFC defines an CertStatusReqItemV2 structure:
744      *
745      *      struct {
746      *          CertificateStatusType status_type;
747      *          uint16 request_length;
748      *          select (status_type) {
749      *              case ocsp: OCSPStatusRequest;
750      *              case ocsp_multi: OCSPStatusRequest;
751      *          } request;
752      *      } CertificateStatusRequestItemV2;
753      *
754      *      enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
755      *      struct {
756      *        ResponderID responder_id_list<0..2^16-1>;
757      *        Extensions request_extensions;
758      *      } OCSPStatusRequest;
759      *
760      *      opaque ResponderID<1..2^16-1>;
761      *      opaque Extensions<0..2^16-1>;
762      *
763      *      struct {
764      *        CertificateStatusRequestItemV2
765      *                         certificate_status_req_list<1..2^16-1>;
766      *      } CertificateStatusRequestListV2;
767      */
768     static final class CertStatusRequestV2Spec implements SSLExtensionSpec {
769         static final CertStatusRequestV2Spec DEFAULT =
770                 new CertStatusRequestV2Spec(new CertStatusRequest[] {
771                         OCSPStatusRequest.EMPTY_OCSP_MULTI});
772 
773         final CertStatusRequest[] certStatusRequests;
774 
CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests)775         private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) {
776             this.certStatusRequests = certStatusRequests;
777         }
778 
CertStatusRequestV2Spec(ByteBuffer message)779         private CertStatusRequestV2Spec(ByteBuffer message) throws IOException {
780             // Is it a empty extension_data?
781             if (message.remaining() == 0) {
782                 // server response
783                 this.certStatusRequests = new CertStatusRequest[0];
784                 return;
785             }
786 
787             if (message.remaining() < 5) {  //  2: certificate_status_req_list
788                                             // +1: status_type
789                                             // +2: request_length
790                 throw new SSLProtocolException(
791                     "Invalid status_request_v2 extension: insufficient data");
792             }
793 
794             int listLen = Record.getInt16(message);
795             if (listLen <= 0) {
796                 throw new SSLProtocolException(
797                     "certificate_status_req_list length must be positive " +
798                     "(received length: " + listLen + ")");
799             }
800 
801             int remaining = listLen;
802             List<CertStatusRequest> statusRequests = new ArrayList<>();
803             while (remaining > 0) {
804                 byte statusType = (byte)Record.getInt8(message);
805                 int requestLen = Record.getInt16(message);
806 
807                 if (message.remaining() < requestLen) {
808                     throw new SSLProtocolException(
809                             "Invalid status_request_v2 extension: " +
810                             "insufficient data (request_length=" + requestLen +
811                             ", remining=" + message.remaining() + ")");
812                 }
813 
814                 byte[] encoded = new byte[requestLen];
815                 if (encoded.length != 0) {
816                     message.get(encoded);
817                 }
818                 remaining -= 3;     // 1(status type) + 2(request_length) bytes
819                 remaining -= requestLen;
820 
821                 if (statusType == CertStatusRequestType.OCSP.id ||
822                         statusType == CertStatusRequestType.OCSP_MULTI.id) {
823                     if (encoded.length < 4) {
824                                         //  2: length of responder_id_list
825                                         // +2: length of request_extensions
826                         throw new SSLProtocolException(
827                             "Invalid status_request_v2 extension: " +
828                             "insufficient data");
829                     }
830                     statusRequests.add(
831                             new OCSPStatusRequest(statusType, encoded));
832                 } else {
833                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
834                         SSLLogger.info(
835                                 "Unknown certificate status request " +
836                                 "(status type: " + statusType + ")");
837                     }
838                     statusRequests.add(
839                             new CertStatusRequest(statusType, encoded));
840                 }
841             }
842 
843             certStatusRequests =
844                     statusRequests.toArray(new CertStatusRequest[0]);
845         }
846 
847         @Override
toString()848         public String toString() {
849             if (certStatusRequests == null || certStatusRequests.length == 0) {
850                 return "<empty>";
851             } else {
852                 MessageFormat messageFormat = new MessageFormat(
853                     "\"cert status request\": '{'\n{0}\n'}'", Locale.ENGLISH);
854 
855                 StringBuilder builder = new StringBuilder(512);
856                 boolean isFirst = true;
857                 for (CertStatusRequest csr : certStatusRequests) {
858                     if (isFirst) {
859                         isFirst = false;
860                     } else {
861                         builder.append(", ");
862                     }
863                     Object[] messageFields = {
864                             Utilities.indent(csr.toString())
865                         };
866                     builder.append(messageFormat.format(messageFields));
867                 }
868 
869                 return builder.toString();
870             }
871         }
872     }
873 
874     private static final
875             class CertStatusRequestsStringizer implements SSLStringizer {
876         @Override
toString(ByteBuffer buffer)877         public String toString(ByteBuffer buffer) {
878             try {
879                 return (new CertStatusRequestV2Spec(buffer)).toString();
880             } catch (IOException ioe) {
881                 // For debug logging only, so please swallow exceptions.
882                 return ioe.getMessage();
883             }
884         }
885     }
886 
887     /**
888      * Network data producer of a "status_request_v2" extension in the
889      * ClientHello handshake message.
890      */
891     private static final
892             class CHCertStatusReqV2Producer implements HandshakeProducer {
893         // Prevent instantiation of this class.
CHCertStatusReqV2Producer()894         private CHCertStatusReqV2Producer() {
895             // blank
896         }
897 
898         @Override
produce(ConnectionContext context, HandshakeMessage message)899         public byte[] produce(ConnectionContext context,
900                 HandshakeMessage message) throws IOException {
901             // The producing happens in client side only.
902             ClientHandshakeContext chc = (ClientHandshakeContext)context;
903 
904             if (!chc.sslContext.isStaplingEnabled(true)) {
905                 return null;
906             }
907 
908             if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) {
909                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
910                     SSLLogger.finest(
911                         "Ignore unavailable status_request_v2 extension");
912                 }
913 
914                 return null;
915             }
916 
917             // Produce the extension.
918             //
919             // We are using empty OCSPStatusRequest at present. May extend to
920             // support specific responder or extensions later.
921             byte[] extData = new byte[] {
922                 0x00, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00};
923 
924             // Update the context.
925             chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2,
926                     CertStatusRequestV2Spec.DEFAULT);
927 
928             return extData;
929         }
930     }
931 
932     /**
933      * Network data consumer of a "status_request_v2" extension in the
934      * ClientHello handshake message.
935      */
936     private static final
937             class CHCertStatusReqV2Consumer implements ExtensionConsumer {
938         // Prevent instantiation of this class.
CHCertStatusReqV2Consumer()939         private CHCertStatusReqV2Consumer() {
940             // blank
941         }
942 
943         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)944         public void consume(ConnectionContext context,
945             HandshakeMessage message, ByteBuffer buffer) throws IOException {
946 
947             // The consuming happens in server side only.
948             ServerHandshakeContext shc = (ServerHandshakeContext)context;
949 
950             if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) {
951                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
952                     SSLLogger.finest(
953                         "Ignore unavailable status_request_v2 extension");
954                 }
955 
956                 return;     // ignore the extension
957             }
958 
959             // Parse the extension.
960             CertStatusRequestV2Spec spec;
961             try {
962                 spec = new CertStatusRequestV2Spec(buffer);
963             } catch (IOException ioe) {
964                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
965             }
966 
967             // Update the context.
968             shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2,
969                     spec);
970             if (!shc.isResumption) {
971                 shc.handshakeProducers.putIfAbsent(
972                         SSLHandshake.CERTIFICATE_STATUS.id,
973                         SSLHandshake.CERTIFICATE_STATUS);
974             }
975 
976             // No impact on session resumption.
977         }
978     }
979 
980     /**
981      * Network data producer of a "status_request_v2" extension in the
982      * ServerHello handshake message.
983      */
984     private static final
985             class SHCertStatusReqV2Producer implements HandshakeProducer {
986         // Prevent instantiation of this class.
SHCertStatusReqV2Producer()987         private SHCertStatusReqV2Producer() {
988             // blank
989         }
990 
991         @Override
produce(ConnectionContext context, HandshakeMessage message)992         public byte[] produce(ConnectionContext context,
993                 HandshakeMessage message) throws IOException {
994             // The producing happens in client side only.
995 
996             ServerHandshakeContext shc = (ServerHandshakeContext)context;
997             // The StaplingParameters in the ServerHandshakeContext will
998             // contain the info about what kind of stapling (if any) to
999             // perform and whether this status_request extension should be
1000             // produced or the status_request_v2 (found in a different producer)
1001             // No explicit check is required for isStaplingEnabled here.  If
1002             // it is false then stapleParams will be null.  If it is true
1003             // then stapleParams may or may not be false and the check below
1004             // is sufficient.
1005             if ((shc.stapleParams == null) ||
1006                     (shc.stapleParams.statusRespExt !=
1007                     SSLExtension.CH_STATUS_REQUEST_V2)) {
1008                 return null;    // Do not produce status_request_v2 in SH
1009             }
1010 
1011             // In response to "status_request_v2" extension request only
1012             CertStatusRequestV2Spec spec = (CertStatusRequestV2Spec)
1013                 shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2);
1014             if (spec == null) {
1015                 // Ignore, no status_request_v2 extension requested.
1016                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
1017                     SSLLogger.finest(
1018                         "Ignore unavailable status_request_v2 extension");
1019                 }
1020 
1021                 return null;        // ignore the extension
1022             }
1023 
1024             // Is it a session resuming?
1025             if (shc.isResumption) {
1026                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
1027                     SSLLogger.finest(
1028                         "No status_request_v2 response for session resumption");
1029                 }
1030                 return null;        // ignore the extension
1031             }
1032 
1033             // The "extension_data" in the extended ServerHello handshake
1034             // message MUST be empty.
1035             byte[] extData = new byte[0];
1036 
1037             // Update the context.
1038             shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2,
1039                     CertStatusRequestV2Spec.DEFAULT);
1040 
1041             return extData;
1042         }
1043     }
1044 
1045     /**
1046      * Network data consumer of a "status_request_v2" extension in the
1047      * ServerHello handshake message.
1048      */
1049     private static final
1050             class SHCertStatusReqV2Consumer implements ExtensionConsumer {
1051         // Prevent instantiation of this class.
SHCertStatusReqV2Consumer()1052         private SHCertStatusReqV2Consumer() {
1053             // blank
1054         }
1055 
1056         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)1057         public void consume(ConnectionContext context,
1058             HandshakeMessage message, ByteBuffer buffer) throws IOException {
1059 
1060             // The consumption happens in client side only.
1061             ClientHandshakeContext chc = (ClientHandshakeContext)context;
1062 
1063             // In response to "status_request" extension request only
1064             CertStatusRequestV2Spec requestedCsr = (CertStatusRequestV2Spec)
1065                 chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2);
1066             if (requestedCsr == null) {
1067                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
1068                     "Unexpected status_request_v2 extension in ServerHello");
1069             }
1070 
1071             // Parse the extension.
1072             if (buffer.hasRemaining()) {
1073                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
1074                   "Invalid status_request_v2 extension in ServerHello: " +
1075                   "the extension data must be empty");
1076             }
1077 
1078             // Update the context.
1079             chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2,
1080                     CertStatusRequestV2Spec.DEFAULT);
1081 
1082             // Since we've received a legitimate status_request in the
1083             // ServerHello, stapling is active if it's been enabled.  If it
1084             // is active, make sure we add the CertificateStatus message
1085             // consumer.
1086             chc.staplingActive = chc.sslContext.isStaplingEnabled(true);
1087             if (chc.staplingActive) {
1088                 chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
1089                     SSLHandshake.CERTIFICATE_STATUS);
1090             }
1091 
1092             // No impact on session resumption.
1093         }
1094     }
1095 
1096     private static final
1097             class CTCertStatusResponseProducer implements HandshakeProducer {
1098         // Prevent instantiation of this class.
CTCertStatusResponseProducer()1099         private CTCertStatusResponseProducer() {
1100             // blank
1101         }
1102 
1103         @Override
produce(ConnectionContext context, HandshakeMessage message)1104         public byte[] produce(ConnectionContext context,
1105                 HandshakeMessage message) throws IOException {
1106             ServerHandshakeContext shc = (ServerHandshakeContext)context;
1107             byte[] producedData = null;
1108 
1109             // Stapling needs to be active and have valid data to proceed
1110             if (shc.stapleParams == null) {
1111                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
1112                     SSLLogger.finest(
1113                         "Stapling is disabled for this connection");
1114                 }
1115                 return null;
1116             }
1117 
1118             // There needs to be a non-null CertificateEntry to proceed
1119             if (shc.currentCertEntry == null) {
1120                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
1121                     SSLLogger.finest("Found null CertificateEntry in context");
1122                 }
1123                 return null;
1124             }
1125 
1126             // Pull the certificate from the CertificateEntry and find
1127             // a response from the response map.  If one exists we will
1128             // staple it.
1129             try {
1130                 CertificateFactory cf = CertificateFactory.getInstance("X.509");
1131                 X509Certificate x509Cert =
1132                         (X509Certificate)cf.generateCertificate(
1133                                 new ByteArrayInputStream(
1134                                         shc.currentCertEntry.encoded));
1135                 byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert);
1136                 if (respBytes == null) {
1137                     // We're done with this entry.  Clear it from the context
1138                     if (SSLLogger.isOn &&
1139                             SSLLogger.isOn("ssl,handshake,verbose")) {
1140                         SSLLogger.finest("No status response found for " +
1141                                 x509Cert.getSubjectX500Principal());
1142                     }
1143                     shc.currentCertEntry = null;
1144                     return null;
1145                 }
1146 
1147                 // Build a proper response buffer from the stapling information
1148                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
1149                     SSLLogger.finest("Found status response for " +
1150                             x509Cert.getSubjectX500Principal() +
1151                             ", response length: " + respBytes.length);
1152                 }
1153                 CertStatusResponse certResp = (shc.stapleParams.statReqType ==
1154                         CertStatusRequestType.OCSP) ?
1155                         new OCSPStatusResponse(shc.stapleParams.statReqType.id,
1156                                 respBytes) :
1157                         new CertStatusResponse(shc.stapleParams.statReqType.id,
1158                                 respBytes);
1159                 producedData = certResp.toByteArray();
1160             } catch (CertificateException ce) {
1161                 throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,
1162                         "Failed to parse server certificates", ce);
1163             } catch (IOException ioe) {
1164                 throw shc.conContext.fatal(Alert.BAD_CERT_STATUS_RESPONSE,
1165                         "Failed to parse certificate status response", ioe);
1166             }
1167 
1168             // Clear the pinned CertificateEntry from the context
1169             shc.currentCertEntry = null;
1170             return producedData;
1171         }
1172     }
1173 
1174     private static final
1175         class CTCertStatusResponseConsumer implements ExtensionConsumer {
1176         // Prevent instantiation of this class.
CTCertStatusResponseConsumer()1177         private CTCertStatusResponseConsumer() {
1178             // blank
1179         }
1180 
1181         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)1182         public void consume(ConnectionContext context,
1183                 HandshakeMessage message, ByteBuffer buffer) throws IOException {
1184             // The consumption happens in client side only.
1185             ClientHandshakeContext chc = (ClientHandshakeContext)context;
1186 
1187             // Parse the extension.
1188             CertStatusResponseSpec spec;
1189             try {
1190                 spec = new CertStatusResponseSpec(buffer);
1191             } catch (IOException ioe) {
1192                 throw chc.conContext.fatal(Alert.DECODE_ERROR, ioe);
1193             }
1194 
1195             if (chc.sslContext.isStaplingEnabled(true)) {
1196                 // Activate stapling
1197                 chc.staplingActive = true;
1198             } else {
1199                 // Do no further processing of stapled responses
1200                 return;
1201             }
1202 
1203             // Get response list from the session.  This is unmodifiable
1204             // so we need to create a new list.  Then add this new response
1205             // to the end and submit it back to the session object.
1206             if ((chc.handshakeSession != null) && (!chc.isResumption)) {
1207                 List<byte[]> respList = new ArrayList<>(
1208                         chc.handshakeSession.getStatusResponses());
1209                 respList.add(spec.statusResponse.encodedResponse);
1210                 chc.handshakeSession.setStatusResponses(respList);
1211             } else {
1212                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
1213                     SSLLogger.finest(
1214                             "Ignoring stapled data on resumed session");
1215                 }
1216             }
1217         }
1218     }
1219 }
1220