1 /*
   2  * Copyright (c) 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 
  26 package sun.security.ssl;
  27 
  28 import java.io.IOException;
  29 import java.nio.ByteBuffer;
  30 import java.util.Objects;
  31 import javax.net.ssl.SSLException;
  32 
  33 /*
  34  * RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),
  35  * which allows the client to request that the server perform OCSP
  36  * on the client's behalf.
  37  *
  38  * The RFC defines an CertStatusReqItemV2 structure:
  39  *
  40  *      struct {
  41  *          CertificateStatusType status_type;
  42  *          uint16 request_length;
  43  *          select (status_type) {
  44  *              case ocsp: OCSPStatusRequest;
  45  *              case ocsp_multi: OCSPStatusRequest;
  46  *          } request;
  47  *      } CertificateStatusRequestItemV2;
  48  *
  49  *      enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
  50  */
  51 
  52 final class CertStatusReqItemV2 {
  53 
  54     private final StatusRequestType statReqType;
  55     private final StatusRequest request;
  56 
  57     /**
  58      * Construct a {@code CertStatusReqItemV2} object using a type value
  59      *      and empty ResponderId and Extension lists.
  60      *
  61      * @param reqType the type of request (e.g. ocsp).  A {@code null} value
  62      *      is not allowed.
  63      * @param statReq the {@code StatusRequest} object used to provide the
  64      *      encoding for this {@code CertStatusReqItemV2}.  A {@code null}
  65      *      value is not allowed.
  66      *
  67      * @throws IllegalArgumentException if the provided {@code StatusRequest}
  68      *      does not match the type.
  69      * @throws NullPointerException if either the reqType or statReq arguments
  70      *      are {@code null}.
  71      */
  72     CertStatusReqItemV2(StatusRequestType reqType, StatusRequest statReq) {
  73         statReqType = Objects.requireNonNull(reqType,
  74                 "Unallowed null value for status_type");
  75         request = Objects.requireNonNull(statReq,
  76                 "Unallowed null value for request");
  77 
  78         // There is currently only one known status type (OCSP)
  79         // We can add more clauses to cover other types in the future
  80         if (statReqType.equals(StatusRequestType.OCSP) ||
  81                 statReqType.equals(StatusRequestType.OCSP_MULTI)) {
  82             if (!(statReq instanceof OCSPStatusRequest)) {
  83                 throw new IllegalArgumentException("StatusRequest not " +
  84                         "of type OCSPStatusRequest");
  85             }
  86         }
  87     }
  88 
  89     /**
  90      * Construct a {@code CertStatusReqItemV2} object from encoded bytes
  91      *
  92      * @param requestBytes the encoded bytes for the {@code CertStatusReqItemV2}
  93      *
  94      * @throws IOException if any decoding errors take place
  95      * @throws IllegalArgumentException if the parsed reqType value is not a
  96      *      supported status request type.
  97      */
  98     CertStatusReqItemV2(byte[] reqItemBytes) throws IOException {
  99         ByteBuffer reqBuf = ByteBuffer.wrap(reqItemBytes);
 100         statReqType = StatusRequestType.get(reqBuf.get());
 101         int requestLength = Short.toUnsignedInt(reqBuf.getShort());
 102 
 103         if (requestLength == reqBuf.remaining()) {
 104             byte[] statReqBytes = new byte[requestLength];
 105             reqBuf.get(statReqBytes);
 106             if (statReqType == StatusRequestType.OCSP ||
 107                     statReqType == StatusRequestType.OCSP_MULTI) {
 108                 request = new OCSPStatusRequest(statReqBytes);
 109             } else {
 110                 request = new UnknownStatusRequest(statReqBytes);
 111             }
 112         } else {
 113             throw new SSLException("Incorrect request_length: " +
 114                     "Expected " + reqBuf.remaining() + ", got " +
 115                     requestLength);
 116         }
 117     }
 118 
 119     /**
 120      * Construct an {@code CertStatusReqItemV2} object from data read from
 121      * a {@code HandshakeInputStream}
 122      *
 123      * @param s the {@code HandshakeInputStream} providing the encoded data
 124      *
 125      * @throws IOException if any decoding errors happen during object
 126      *      construction.
 127      * @throws IllegalArgumentException if the parsed reqType value is not a
 128      *      supported status request type.
 129      */
 130     CertStatusReqItemV2(HandshakeInStream in) throws IOException {
 131         statReqType = StatusRequestType.get(in.getInt8());
 132         int requestLength = in.getInt16();
 133 
 134         if (statReqType == StatusRequestType.OCSP ||
 135                 statReqType == StatusRequestType.OCSP_MULTI) {
 136             request = new OCSPStatusRequest(in);
 137         } else {
 138             request = new UnknownStatusRequest(in, requestLength);
 139         }
 140     }
 141 
 142     /**
 143      * Return the length of this {@code CertStatusReqItemV2} in its encoded form
 144      *
 145      * @return the encoded length of this {@code CertStatusReqItemV2}
 146      */
 147     int length() {
 148         // The length is the status type (1 byte) + the request length
 149         // field (2 bytes) + the StatusRequest data length.
 150         return request.length() + 3;
 151     }
 152 
 153     /**
 154      * Send the encoded {@code CertStatusReqItemV2} through a
 155      *      {@code HandshakeOutputStream}
 156      *
 157      * @param s the {@code HandshakeOutputStream} used to send the encoded data
 158      *
 159      * @throws IOException if any errors occur during the encoding process
 160      */
 161     void send(HandshakeOutStream s) throws IOException {
 162         s.putInt8(statReqType.id);
 163         s.putInt16(request.length());
 164         request.send(s);
 165     }
 166 
 167     /**
 168      * Create a string representation of this {@code CertStatusReqItemV2}
 169      *
 170      * @return the string representation of this {@code CertStatusReqItemV2}
 171      */
 172     @Override
 173     public String toString() {
 174         StringBuilder sb = new StringBuilder();
 175         sb.append("CertStatusReqItemV2: ").append(statReqType).append(", ");
 176         sb.append(request.toString());
 177 
 178         return sb.toString();
 179     }
 180 
 181     /**
 182      * Return the type field for this {@code CertStatusReqItemV2}
 183      *
 184      * @return the {@code StatusRequestType} for this extension.
 185      */
 186     StatusRequestType getType() {
 187         return statReqType;
 188     }
 189 
 190     /**
 191      * Get the underlying {@code StatusRequest} for this
 192      *      {@code CertStatusReqItemV2}
 193      *
 194      * @return the {@code StatusRequest}
 195      */
 196     StatusRequest getRequest() {
 197         return request;
 198     }
 199 }