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.LinkedList;
  32 import java.util.List;
  33 import java.util.Locale;
  34 import javax.net.ssl.SSLProtocolException;
  35 import sun.security.ssl.SSLExtension.ExtensionConsumer;
  36 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
  37 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  38 
  39 /**
  40  * Pack of the "signature_algorithms" extensions [RFC 5246].
  41  */
  42 final class SignatureAlgorithmsExtension {
  43     static final HandshakeProducer chNetworkProducer =
  44             new CHSignatureSchemesProducer();
  45     static final ExtensionConsumer chOnLoadConcumer =
  46             new CHSignatureSchemesConsumer();
  47     static final HandshakeAbsence chOnLoadAbsence =
  48             new CHSignatureSchemesAbsence();
  49     static final HandshakeConsumer chOnTradeConsumer =
  50             new CHSignatureSchemesUpdate();
  51 
  52     static final HandshakeProducer crNetworkProducer =
  53             new CRSignatureSchemesProducer();
  54     static final ExtensionConsumer crOnLoadConcumer =
  55             new CRSignatureSchemesConsumer();
  56     static final HandshakeAbsence crOnLoadAbsence =
  57             new CRSignatureSchemesAbsence();
  58     static final HandshakeConsumer crOnTradeConsumer =
  59             new CRSignatureSchemesUpdate();
  60 
  61     static final SSLStringize ssStringize =
  62             new SignatureSchemesStringize();
  63 
  64     /**
  65      * The "signature_algorithms" extension.
  66      */
  67     static final class SignatureSchemesSpec implements SSLExtensionSpec {
  68         final int[] signatureSchemes;
  69 
  70         SignatureSchemesSpec(List<SignatureScheme> schemes) {
  71             if (schemes != null) {
  72                 signatureSchemes = new int[schemes.size()];
  73                 int i = 0;
  74                 for (SignatureScheme scheme : schemes) {
  75                     signatureSchemes[i++] = scheme.id;
  76                 }
  77             } else {
  78                 this.signatureSchemes = new int[0];
  79             }
  80         }
  81 
  82         SignatureSchemesSpec(ByteBuffer buffer) throws IOException {
  83             if (buffer.remaining() < 2) {      // 2: the length of the list
  84                 throw new SSLProtocolException(
  85                     "Invalid signature_algorithms: insufficient data");
  86             }
  87 
  88             byte[] algs = Record.getBytes16(buffer);
  89             if (buffer.hasRemaining()) {
  90                 throw new SSLProtocolException(
  91                     "Invalid signature_algorithms: unknown extra data");
  92             }
  93 
  94             if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) {
  95                 throw new SSLProtocolException(
  96                     "Invalid signature_algorithms: incomplete data");
  97             }
  98 
  99             int[] schemes = new int[algs.length / 2];
 100             for (int i = 0, j = 0; i < algs.length;) {
 101                 byte hash = algs[i++];
 102                 byte sign = algs[i++];
 103                 schemes[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF);
 104             }
 105 
 106             this.signatureSchemes = schemes;
 107         }
 108 
 109         @Override
 110         public String toString() {
 111             MessageFormat messageFormat = new MessageFormat(
 112                 "\"signature schemes\": '['{0}']'", Locale.ENGLISH);
 113 
 114             if (signatureSchemes == null || signatureSchemes.length == 0) {
 115                 Object[] messageFields = {
 116                         "<no supported signature schemes specified>"
 117                     };
 118                 return messageFormat.format(messageFields);
 119             } else {
 120                 StringBuilder builder = new StringBuilder(512);
 121                 boolean isFirst = true;
 122                 for (int pv : signatureSchemes) {
 123                     if (isFirst) {
 124                         isFirst = false;
 125                     } else {
 126                         builder.append(", ");
 127                     }
 128 
 129                     builder.append(SignatureScheme.nameOf(pv));
 130                 }
 131 
 132                 Object[] messageFields = {
 133                         builder.toString()
 134                     };
 135 
 136                 return messageFormat.format(messageFields);
 137             }
 138         }
 139     }
 140 
 141     private static final
 142             class SignatureSchemesStringize implements SSLStringize {
 143         @Override
 144         public String toString(ByteBuffer buffer) {
 145             try {
 146                 return (new SignatureSchemesSpec(buffer)).toString();
 147             } catch (IOException ioe) {
 148                 // For debug logging only, so please swallow exceptions.
 149                 return ioe.getMessage();
 150             }
 151         }
 152     }
 153 
 154     /**
 155      * Network data producer of a "signature_algorithms" extension in
 156      * the ClientHello handshake message.
 157      */
 158     private static final
 159             class CHSignatureSchemesProducer implements HandshakeProducer {
 160         // Prevent instantiation of this class.
 161         private CHSignatureSchemesProducer() {
 162             // blank
 163         }
 164 
 165         @Override
 166         public byte[] produce(ConnectionContext context,
 167                 HandshakeMessage message) throws IOException {
 168             // The producing happens in client side only.
 169             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 170 
 171             // Is it a supported and enabled extension?
 172             if (!chc.sslConfig.isAvailable(
 173                     SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
 174                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 175                     SSLLogger.fine(
 176                         "Ignore unavailable signature_algorithms extension");
 177                 }
 178                 return null;
 179             }
 180 
 181             // Produce the extension.
 182             if (chc.localSupportedSignAlgs == null) {
 183                 chc.localSupportedSignAlgs =
 184                     SignatureScheme.getSupportedAlgorithms(
 185                             chc.algorithmConstraints, chc.activeProtocols);
 186             }
 187 
 188             int vectorLen = SignatureScheme.sizeInRecord() *
 189                     chc.localSupportedSignAlgs.size();
 190             byte[] extData = new byte[vectorLen + 2];
 191             ByteBuffer m = ByteBuffer.wrap(extData);
 192             Record.putInt16(m, vectorLen);
 193             for (SignatureScheme ss : chc.localSupportedSignAlgs) {
 194                 Record.putInt16(m, ss.id);
 195             }
 196 
 197             // Update the context.
 198             chc.handshakeExtensions.put(
 199                     SSLExtension.CH_SIGNATURE_ALGORITHMS,
 200                     new SignatureSchemesSpec(chc.localSupportedSignAlgs));
 201 
 202             return extData;
 203         }
 204     }
 205 
 206     /**
 207      * Network data consumer of a "signature_algorithms" extension in
 208      * the ClientHello handshake message.
 209      */
 210     private static final
 211             class CHSignatureSchemesConsumer implements ExtensionConsumer {
 212         // Prevent instantiation of this class.
 213         private CHSignatureSchemesConsumer() {
 214             // blank
 215         }
 216 
 217         @Override
 218         public void consume(ConnectionContext context,
 219             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 220             // The comsuming happens in server side only.
 221             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 222 
 223             // Is it a supported and enabled extension?
 224             if (!shc.sslConfig.isAvailable(
 225                     SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
 226                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 227                     SSLLogger.fine(
 228                         "Ignore unavailable signature_algorithms extension");
 229                 }
 230                 return;     // ignore the extension
 231             }
 232 
 233             // Parse the extension.
 234             SignatureSchemesSpec spec;
 235             try {
 236                 spec = new SignatureSchemesSpec(buffer);
 237             } catch (IOException ioe) {
 238                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 239                 return;     // fatal() always throws, make the compiler happy.
 240             }
 241 
 242             // Update the context.
 243             shc.handshakeExtensions.put(
 244                     SSLExtension.CH_SIGNATURE_ALGORITHMS, spec);
 245 
 246             // No impact on session resumption.
 247         }
 248     }
 249 
 250     /**
 251      * After session creation consuming of a "signature_algorithms"
 252      * extension in the ClientHello handshake message.
 253      */
 254     private static final class CHSignatureSchemesUpdate
 255             implements HandshakeConsumer {
 256         // Prevent instantiation of this class.
 257         private CHSignatureSchemesUpdate() {
 258             // blank
 259         }
 260 
 261         @Override
 262         public void consume(ConnectionContext context,
 263                 HandshakeMessage message) throws IOException {
 264             // The comsuming happens in server side only.
 265             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 266 
 267             SignatureSchemesSpec spec =
 268                     (SignatureSchemesSpec)shc.handshakeExtensions.get(
 269                             SSLExtension.CH_SIGNATURE_ALGORITHMS);
 270             if (spec == null) {
 271                 // Ignore, no "signature_algorithms" extension requested.
 272                 return;
 273             }
 274 
 275             // update the context
 276             List<SignatureScheme> shemes =
 277                     SignatureScheme.getSupportedAlgorithms(
 278                             shc.algorithmConstraints, shc.negotiatedProtocol,
 279                             spec.signatureSchemes);
 280             shc.peerRequestedSignatureSchemes = shemes;
 281 
 282             // If no "signature_algorithms_cert" extension is present, then
 283             // the "signature_algorithms" extension also applies to
 284             // signatures appearing in certificates.
 285             SignatureSchemesSpec certSpec =
 286                     (SignatureSchemesSpec)shc.handshakeExtensions.get(
 287                             SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
 288             if (certSpec == null) {
 289                 shc.peerRequestedCertSignSchemes = shemes;
 290             }
 291 
 292             shc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
 293 
 294             if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
 295                 if (shc.sslConfig.clientAuthType !=
 296                         ClientAuthType.CLIENT_AUTH_NONE) {
 297                     shc.handshakeProducers.putIfAbsent(
 298                             SSLHandshake.CERTIFICATE_REQUEST.id,
 299                             SSLHandshake.CERTIFICATE_REQUEST);
 300                 }
 301                 shc.handshakeProducers.put(
 302                         SSLHandshake.CERTIFICATE.id,
 303                         SSLHandshake.CERTIFICATE);
 304                 shc.handshakeProducers.putIfAbsent(
 305                         SSLHandshake.CERTIFICATE_VERIFY.id,
 306                         SSLHandshake.CERTIFICATE_VERIFY);
 307             }
 308         }
 309     }
 310 
 311     /**
 312      * The absence processing if a "signature_algorithms" extension is
 313      * not present in the ClientHello handshake message.
 314      */
 315     private static final
 316             class CHSignatureSchemesAbsence implements HandshakeAbsence {
 317         @Override
 318         public void absent(ConnectionContext context,
 319                 HandshakeMessage message) throws IOException {
 320             // The comsuming happens in server side only.
 321             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 322 
 323             // This is a mandatory extension for certificate authentication
 324             // in TLS 1.3.
 325             //
 326             // We may support the server authentication other than X.509
 327             // certificate later.
 328             if (shc.negotiatedProtocol.useTLS13PlusSpec()) {
 329                 shc.conContext.fatal(Alert.MISSING_EXTENSION,
 330                     "No mandatory signature_algorithms extension in the " +
 331                     "received CertificateRequest handshake message");
 332             }
 333         }
 334     }
 335 
 336     /**
 337      * Network data producer of a "signature_algorithms" extension in
 338      * the CertificateRequest handshake message.
 339      */
 340     private static final
 341             class CRSignatureSchemesProducer implements HandshakeProducer {
 342         // Prevent instantiation of this class.
 343         private CRSignatureSchemesProducer() {
 344             // blank
 345         }
 346 
 347         @Override
 348         public byte[] produce(ConnectionContext context,
 349                 HandshakeMessage message) throws IOException {
 350             // The producing happens in server side only.
 351             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 352 
 353             // Is it a supported and enabled extension?
 354             //
 355             // Note that this is a mandatory extension for CertificateRequest
 356             // handshake message in TLS 1.3.
 357             if (!shc.sslConfig.isAvailable(
 358                     SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
 359                 shc.conContext.fatal(Alert.MISSING_EXTENSION,
 360                         "No available signature_algorithms extension " +
 361                         "for client certificate authentication");
 362                 return null;    // make the compiler happy
 363             }
 364 
 365             // Produce the extension.
 366             if (shc.localSupportedSignAlgs == null) {
 367                 shc.localSupportedSignAlgs =
 368                     SignatureScheme.getSupportedAlgorithms(
 369                             shc.algorithmConstraints, shc.activeProtocols);
 370             }
 371 
 372             int vectorLen = SignatureScheme.sizeInRecord() *
 373                     shc.localSupportedSignAlgs.size();
 374             byte[] extData = new byte[vectorLen + 2];
 375             ByteBuffer m = ByteBuffer.wrap(extData);
 376             Record.putInt16(m, vectorLen);
 377             for (SignatureScheme ss : shc.localSupportedSignAlgs) {
 378                 Record.putInt16(m, ss.id);
 379             }
 380 
 381             // Update the context.
 382             shc.handshakeExtensions.put(
 383                     SSLExtension.CR_SIGNATURE_ALGORITHMS,
 384                     new SignatureSchemesSpec(shc.localSupportedSignAlgs));
 385 
 386             return extData;
 387         }
 388     }
 389 
 390     /**
 391      * Network data consumer of a "signature_algorithms" extension in
 392      * the CertificateRequest handshake message.
 393      */
 394     private static final
 395             class CRSignatureSchemesConsumer implements ExtensionConsumer {
 396         // Prevent instantiation of this class.
 397         private CRSignatureSchemesConsumer() {
 398             // blank
 399         }
 400         @Override
 401         public void consume(ConnectionContext context,
 402             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 403             // The comsuming happens in client side only.
 404             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 405 
 406             // Is it a supported and enabled extension?
 407             //
 408             // Note that this is a mandatory extension for CertificateRequest
 409             // handshake message in TLS 1.3.
 410             if (!chc.sslConfig.isAvailable(
 411                     SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
 412                 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 413                         "No available signature_algorithms extension " +
 414                         "for client certificate authentication");
 415                 return;     // make the compiler happy
 416             }
 417 
 418             // Parse the extension.
 419             SignatureSchemesSpec spec;
 420             try {
 421                 spec = new SignatureSchemesSpec(buffer);
 422             } catch (IOException ioe) {
 423                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 424                 return;     // fatal() always throws, make the compiler happy.
 425             }
 426 
 427             List<SignatureScheme> knownSignatureSchemes = new LinkedList<>();
 428             for (int id : spec.signatureSchemes) {
 429                 SignatureScheme ss = SignatureScheme.valueOf(id);
 430                 if (ss != null) {
 431                     knownSignatureSchemes.add(ss);
 432                 }
 433             }
 434 
 435             // Update the context.
 436             // chc.peerRequestedSignatureSchemes = knownSignatureSchemes;
 437             chc.handshakeExtensions.put(
 438                     SSLExtension.CR_SIGNATURE_ALGORITHMS, spec);
 439 
 440             // No impact on session resumption.
 441         }
 442     }
 443 
 444     /**
 445      * After session creation consuming of a "signature_algorithms"
 446      * extension in the CertificateRequest handshake message.
 447      */
 448     private static final class CRSignatureSchemesUpdate
 449             implements HandshakeConsumer {
 450         // Prevent instantiation of this class.
 451         private CRSignatureSchemesUpdate() {
 452             // blank
 453         }
 454 
 455         @Override
 456         public void consume(ConnectionContext context,
 457                 HandshakeMessage message) throws IOException {
 458             // The comsuming happens in client side only.
 459             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 460 
 461             SignatureSchemesSpec spec =
 462                     (SignatureSchemesSpec)chc.handshakeExtensions.get(
 463                             SSLExtension.CR_SIGNATURE_ALGORITHMS);
 464             if (spec == null) {
 465                 // Ignore, no "signature_algorithms" extension requested.
 466                 return;
 467             }
 468 
 469             // update the context
 470             List<SignatureScheme> shemes =
 471                     SignatureScheme.getSupportedAlgorithms(
 472                             chc.algorithmConstraints, chc.negotiatedProtocol,
 473                             spec.signatureSchemes);
 474             chc.peerRequestedSignatureSchemes = shemes;
 475 
 476             // If no "signature_algorithms_cert" extension is present, then
 477             // the "signature_algorithms" extension also applies to
 478             // signatures appearing in certificates.
 479             SignatureSchemesSpec certSpec =
 480                     (SignatureSchemesSpec)chc.handshakeExtensions.get(
 481                             SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
 482             if (certSpec == null) {
 483                 chc.peerRequestedCertSignSchemes = shemes;
 484             }
 485 
 486             chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
 487         }
 488     }
 489 
 490     /**
 491      * The absence processing if a "signature_algorithms" extension is
 492      * not present in the CertificateRequest handshake message.
 493      */
 494     private static final
 495             class CRSignatureSchemesAbsence implements HandshakeAbsence {
 496         @Override
 497         public void absent(ConnectionContext context,
 498                 HandshakeMessage message) throws IOException {
 499             // The comsuming happens in client side only.
 500             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 501 
 502             // This is a mandatory extension for CertificateRequest handshake
 503             // message in TLS 1.3.
 504             chc.conContext.fatal(Alert.MISSING_EXTENSION,
 505                     "No mandatory signature_algorithms extension in the " +
 506                     "received CertificateRequest handshake message");
 507         }
 508     }
 509 }