src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java
changeset 50768 68fa3d4026ea
parent 47216 71c04702a3d5
child 53734 cb1642ccc732
equal deleted inserted replaced
50767:356eaea05bf0 50768:68fa3d4026ea
     1 /*
     1 /*
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    24  */
    24  */
    25 
    25 
    26 package sun.security.ssl;
    26 package sun.security.ssl;
    27 
    27 
    28 import java.io.IOException;
    28 import java.io.IOException;
    29 import javax.net.ssl.SSLProtocolException;
       
    30 import java.security.MessageDigest;
    29 import java.security.MessageDigest;
    31 import java.security.SecureRandom;
    30 import java.security.SecureRandom;
    32 
    31 import java.util.Arrays;
    33 import sun.security.ssl.HandshakeMessage.ClientHello;
    32 import static sun.security.ssl.ClientHello.ClientHelloMessage;
    34 
    33 
    35 /*
    34 /**
    36  * HelloVerifyRequest cookie manager
    35  *  (D)TLS handshake cookie manager
    37  */
    36  */
    38 final class HelloCookieManager {
    37 abstract class HelloCookieManager {
    39     // the cookie secret life time
    38 
    40     private static long COOKIE_TIMING_WINDOW = 3600000;     // in milliseconds
    39     static class Builder {
    41     private static int  COOKIE_MAX_LENGTH_DTLS10 = 32;      // 32 bytes
    40 
    42     private static int  COOKIE_MAX_LENGTH_DTLS12 = 0xFF;    // 2^8 -1 bytes
    41         final SecureRandom secureRandom;
    43 
    42 
    44     private final SecureRandom          secureRandom;
    43         private volatile D10HelloCookieManager d10HelloCookieManager;
    45     private final MessageDigest         cookieDigest;
    44         private volatile D13HelloCookieManager d13HelloCookieManager;
    46 
    45         private volatile T13HelloCookieManager t13HelloCookieManager;
    47     private int                         cookieVersion;      // allow to wrap
    46 
    48     private long                        secretLifetime;
    47         Builder(SecureRandom secureRandom) {
    49     private byte[]                      cookieSecret;
    48             this.secureRandom = secureRandom;
    50 
    49         }
    51     private int                         prevCookieVersion;
    50 
    52     private byte[]                      prevCookieSecret;
    51         HelloCookieManager valueOf(ProtocolVersion protocolVersion) {
    53 
    52             if (protocolVersion.isDTLS) {
    54     HelloCookieManager(SecureRandom secureRandom) {
    53                 if (protocolVersion.useTLS13PlusSpec()) {
    55         this.secureRandom = secureRandom;
    54                     if (d13HelloCookieManager != null) {
    56         this.cookieDigest = JsseJce.getMessageDigest("SHA-256");
    55                         return d13HelloCookieManager;
    57 
    56                     }
    58         this.cookieVersion = secureRandom.nextInt();
    57 
    59         this.secretLifetime = 0;
    58                     synchronized (this) {
    60         this.cookieSecret = null;
    59                         if (d13HelloCookieManager == null) {
    61 
    60                             d13HelloCookieManager =
    62         this.prevCookieVersion = 0;
    61                                     new D13HelloCookieManager(secureRandom);
    63         this.prevCookieSecret = null;
    62                         }
       
    63                     }
       
    64 
       
    65                     return d13HelloCookieManager;
       
    66                 } else {
       
    67                     if (d10HelloCookieManager != null) {
       
    68                         return d10HelloCookieManager;
       
    69                     }
       
    70 
       
    71                     synchronized (this) {
       
    72                         if (d10HelloCookieManager == null) {
       
    73                             d10HelloCookieManager =
       
    74                                     new D10HelloCookieManager(secureRandom);
       
    75                         }
       
    76                     }
       
    77 
       
    78                     return d10HelloCookieManager;
       
    79                 }
       
    80             } else {
       
    81                 if (protocolVersion.useTLS13PlusSpec()) {
       
    82                     if (t13HelloCookieManager != null) {
       
    83                         return t13HelloCookieManager;
       
    84                     }
       
    85 
       
    86                     synchronized (this) {
       
    87                         if (t13HelloCookieManager == null) {
       
    88                             t13HelloCookieManager =
       
    89                                     new T13HelloCookieManager(secureRandom);
       
    90                         }
       
    91                     }
       
    92 
       
    93                     return t13HelloCookieManager;
       
    94                 }
       
    95             }
       
    96 
       
    97             return null;
       
    98         }
    64     }
    99     }
    65 
   100 
    66     // Used by server side to generate cookies in HelloVerifyRequest message.
   101     abstract byte[] createCookie(ServerHandshakeContext context,
    67     synchronized byte[] getCookie(ClientHello clientHelloMsg) {
   102                 ClientHelloMessage clientHello) throws IOException;
    68         if (secretLifetime < System.currentTimeMillis()) {
   103 
    69             if (cookieSecret != null) {
   104     abstract boolean isCookieValid(ServerHandshakeContext context,
    70                 prevCookieVersion = cookieVersion;
   105             ClientHelloMessage clientHello, byte[] cookie) throws IOException;
    71                 prevCookieSecret = cookieSecret.clone();
   106 
    72             } else {
   107     // DTLS 1.0/1.2
    73                 cookieSecret = new byte[32];
   108     private static final
    74             }
   109             class D10HelloCookieManager extends HelloCookieManager {
    75 
   110 
    76             cookieVersion++;
   111         final SecureRandom secureRandom;
       
   112         private int         cookieVersion;  // allow to wrap, version + sequence
       
   113         private byte[]      cookieSecret;
       
   114         private byte[]      legacySecret;
       
   115 
       
   116         D10HelloCookieManager(SecureRandom secureRandom) {
       
   117             this.secureRandom = secureRandom;
       
   118 
       
   119             this.cookieVersion = secureRandom.nextInt();
       
   120             this.cookieSecret = new byte[32];
       
   121             this.legacySecret = new byte[32];
       
   122 
    77             secureRandom.nextBytes(cookieSecret);
   123             secureRandom.nextBytes(cookieSecret);
    78             secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW;
   124             System.arraycopy(cookieSecret, 0, legacySecret, 0, 32);
    79         }
   125         }
    80 
   126 
    81         clientHelloMsg.updateHelloCookie(cookieDigest);
   127         @Override
    82         byte[] cookie = cookieDigest.digest(cookieSecret);      // 32 bytes
   128         byte[] createCookie(ServerHandshakeContext context,
    83         cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
   129                 ClientHelloMessage clientHello) throws IOException {
    84         cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
   130             int version;
    85         cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
   131             byte[] secret;
    86         cookie[3] = (byte)(cookieVersion & 0xFF);
   132 
    87 
   133             synchronized (this) {
    88         return cookie;
   134                 version = cookieVersion;
       
   135                 secret = cookieSecret;
       
   136 
       
   137                 // the cookie secret usage limit is 2^24
       
   138                 if ((cookieVersion & 0xFFFFFF) == 0) {  // reset the secret
       
   139                     System.arraycopy(cookieSecret, 0, legacySecret, 0, 32);
       
   140                     secureRandom.nextBytes(cookieSecret);
       
   141                 }
       
   142 
       
   143                 cookieVersion++;
       
   144             }
       
   145 
       
   146             MessageDigest md = JsseJce.getMessageDigest("SHA-256");
       
   147             byte[] helloBytes = clientHello.getHelloCookieBytes();
       
   148             md.update(helloBytes);
       
   149             byte[] cookie = md.digest(secret);      // 32 bytes
       
   150             cookie[0] = (byte)((version >> 24) & 0xFF);
       
   151 
       
   152             return cookie;
       
   153         }
       
   154 
       
   155         @Override
       
   156         boolean isCookieValid(ServerHandshakeContext context,
       
   157             ClientHelloMessage clientHello, byte[] cookie) throws IOException {
       
   158             // no cookie exchange or not a valid cookie length
       
   159             if ((cookie == null) || (cookie.length != 32)) {
       
   160                 return false;
       
   161             }
       
   162 
       
   163             byte[] secret;
       
   164             synchronized (this) {
       
   165                 if (((cookieVersion >> 24) & 0xFF) == cookie[0]) {
       
   166                     secret = cookieSecret;
       
   167                 } else {
       
   168                     secret = legacySecret;  // including out of window cookies
       
   169                 }
       
   170             }
       
   171 
       
   172             MessageDigest md = JsseJce.getMessageDigest("SHA-256");
       
   173             byte[] helloBytes = clientHello.getHelloCookieBytes();
       
   174             md.update(helloBytes);
       
   175             byte[] target = md.digest(secret);      // 32 bytes
       
   176             target[0] = cookie[0];
       
   177 
       
   178             return Arrays.equals(target, cookie);
       
   179         }
    89     }
   180     }
    90 
   181 
    91     // Used by server side to check the cookie in ClientHello message.
   182     private static final
    92     synchronized boolean isValid(ClientHello clientHelloMsg) {
   183             class D13HelloCookieManager extends HelloCookieManager {
    93         byte[] cookie = clientHelloMsg.cookie;
   184         D13HelloCookieManager(SecureRandom secureRandom) {
    94 
   185         }
    95         // no cookie exchange or not a valid cookie length
   186 
    96         if ((cookie == null) || (cookie.length != 32)) {
   187         @Override
    97             return false;
   188         byte[] createCookie(ServerHandshakeContext context,
    98         }
   189                 ClientHelloMessage clientHello) throws IOException {
    99 
   190             throw new UnsupportedOperationException("Not supported yet.");
   100         int version = ((cookie[0] & 0xFF) << 24) |
   191         }
   101                       ((cookie[1] & 0xFF) << 16) |
   192 
   102                       ((cookie[2] & 0xFF) << 8) |
   193         @Override
   103                        (cookie[3] & 0xFF);
   194         boolean isCookieValid(ServerHandshakeContext context,
   104 
   195             ClientHelloMessage clientHello, byte[] cookie) throws IOException {
   105         byte[] secret;
   196             throw new UnsupportedOperationException("Not supported yet.");
   106         if (version == cookieVersion) {
   197         }
   107             secret = cookieSecret;
       
   108         } else if (version == prevCookieVersion) {
       
   109             secret = prevCookieSecret;
       
   110         } else {
       
   111             return false;       // may be out of the timing window
       
   112         }
       
   113 
       
   114         clientHelloMsg.updateHelloCookie(cookieDigest);
       
   115         byte[] target = cookieDigest.digest(secret);            // 32 bytes
       
   116 
       
   117         for (int i = 4; i < 32; i++) {
       
   118             if (cookie[i] != target[i]) {
       
   119                 return false;
       
   120             }
       
   121         }
       
   122 
       
   123         return true;
       
   124     }
   198     }
   125 
   199 
   126     // Used by client side to check the cookie in HelloVerifyRequest message.
   200     private static final
   127     static void checkCookie(ProtocolVersion protocolVersion,
   201             class T13HelloCookieManager extends HelloCookieManager {
   128             byte[] cookie) throws IOException {
   202 
   129         if (cookie != null && cookie.length != 0) {
   203         final SecureRandom secureRandom;
   130             int limit = COOKIE_MAX_LENGTH_DTLS12;
   204         private int             cookieVersion;      // version + sequence
   131             if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
   205         private final byte[]    cookieSecret;
   132                 limit = COOKIE_MAX_LENGTH_DTLS10;
   206         private final byte[]    legacySecret;
   133             }
   207 
   134 
   208         T13HelloCookieManager(SecureRandom secureRandom) {
   135             if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
   209             this.secureRandom = secureRandom;
   136                 throw new SSLProtocolException(
   210             this.cookieVersion = secureRandom.nextInt();
   137                         "Invalid HelloVerifyRequest.cookie (length = " +
   211             this.cookieSecret = new byte[64];
   138                          cookie.length + " bytes)");
   212             this.legacySecret = new byte[64];
   139             }
   213 
   140         }
   214             secureRandom.nextBytes(cookieSecret);
   141 
   215             System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
   142         // Otherwise, no cookie exchange.
   216         }
       
   217 
       
   218         @Override
       
   219         byte[] createCookie(ServerHandshakeContext context,
       
   220                 ClientHelloMessage clientHello) throws IOException {
       
   221             int version;
       
   222             byte[] secret;
       
   223 
       
   224             synchronized (this) {
       
   225                 version = cookieVersion;
       
   226                 secret = cookieSecret;
       
   227 
       
   228                 // the cookie secret usage limit is 2^24
       
   229                 if ((cookieVersion & 0xFFFFFF) == 0) {  // reset the secret
       
   230                     System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
       
   231                     secureRandom.nextBytes(cookieSecret);
       
   232                 }
       
   233 
       
   234                 cookieVersion++;        // allow wrapped version number
       
   235             }
       
   236 
       
   237             MessageDigest md = JsseJce.getMessageDigest(
       
   238                     context.negotiatedCipherSuite.hashAlg.name);
       
   239             byte[] headerBytes = clientHello.getHeaderBytes();
       
   240             md.update(headerBytes);
       
   241             byte[] headerCookie = md.digest(secret);
       
   242 
       
   243             // hash of ClientHello handshake message
       
   244             context.handshakeHash.update();
       
   245             byte[] clientHelloHash = context.handshakeHash.digest();
       
   246 
       
   247             // version and cipher suite
       
   248             //
       
   249             // Store the negotiated cipher suite in the cookie as well.
       
   250             // cookie[0]/[1]: cipher suite
       
   251             // cookie[2]: cookie version
       
   252             // + (hash length): Mac(ClientHello header)
       
   253             // + (hash length): Hash(ClientHello)
       
   254             byte[] prefix = new byte[] {
       
   255                     (byte)((context.negotiatedCipherSuite.id >> 8) & 0xFF),
       
   256                     (byte)(context.negotiatedCipherSuite.id & 0xFF),
       
   257                     (byte)((version >> 24) & 0xFF)
       
   258                 };
       
   259 
       
   260             byte[] cookie = Arrays.copyOf(prefix,
       
   261                 prefix.length + headerCookie.length + clientHelloHash.length);
       
   262             System.arraycopy(headerCookie, 0, cookie,
       
   263                 prefix.length, headerCookie.length);
       
   264             System.arraycopy(clientHelloHash, 0, cookie,
       
   265                 prefix.length + headerCookie.length, clientHelloHash.length);
       
   266 
       
   267             return cookie;
       
   268         }
       
   269 
       
   270         @Override
       
   271         boolean isCookieValid(ServerHandshakeContext context,
       
   272             ClientHelloMessage clientHello, byte[] cookie) throws IOException {
       
   273             // no cookie exchange or not a valid cookie length
       
   274             if ((cookie == null) || (cookie.length <= 32)) {    // 32: roughly
       
   275                 return false;
       
   276             }
       
   277 
       
   278             int csId = ((cookie[0] & 0xFF) << 8) | (cookie[1] & 0xFF);
       
   279             CipherSuite cs = CipherSuite.valueOf(csId);
       
   280             if (cs == null || cs.hashAlg == null || cs.hashAlg.hashLength == 0) {
       
   281                 return false;
       
   282             }
       
   283 
       
   284             int hashLen = cs.hashAlg.hashLength;
       
   285             if (cookie.length != (3 + hashLen * 2)) {
       
   286                 return false;
       
   287             }
       
   288 
       
   289             byte[] prevHeadCookie =
       
   290                     Arrays.copyOfRange(cookie, 3, 3 + hashLen);
       
   291             byte[] prevClientHelloHash =
       
   292                     Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length);
       
   293 
       
   294             byte[] secret;
       
   295             synchronized (this) {
       
   296                 if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) {
       
   297                     secret = cookieSecret;
       
   298                 } else {
       
   299                     secret = legacySecret;  // including out of window cookies
       
   300                 }
       
   301             }
       
   302 
       
   303             MessageDigest md = JsseJce.getMessageDigest(cs.hashAlg.name);
       
   304             byte[] headerBytes = clientHello.getHeaderBytes();
       
   305             md.update(headerBytes);
       
   306             byte[] headerCookie = md.digest(secret);
       
   307 
       
   308             if (!Arrays.equals(headerCookie, prevHeadCookie)) {
       
   309                 return false;
       
   310             }
       
   311 
       
   312             // Use the ClientHello hash in the cookie for transtript
       
   313             // hash calculation for stateless HelloRetryRequest.
       
   314             //
       
   315             // Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
       
   316             //   Hash(message_hash ||    /* Handshake type */
       
   317             //     00 00 Hash.length ||  /* Handshake message length (bytes) */
       
   318             //     Hash(ClientHello1) || /* Hash of ClientHello1 */
       
   319             //     HelloRetryRequest || ... || Mn)
       
   320 
       
   321             // Reproduce HelloRetryRequest handshake message
       
   322             byte[] hrrMessage =
       
   323                     ServerHello.hrrReproducer.produce(context, clientHello);
       
   324             context.handshakeHash.push(hrrMessage);
       
   325 
       
   326             // Construct the 1st ClientHello message for transcript hash
       
   327             byte[] hashedClientHello = new byte[4 + hashLen];
       
   328             hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;
       
   329             hashedClientHello[1] = (byte)0x00;
       
   330             hashedClientHello[2] = (byte)0x00;
       
   331             hashedClientHello[3] = (byte)(hashLen & 0xFF);
       
   332             System.arraycopy(prevClientHelloHash, 0,
       
   333                     hashedClientHello, 4, hashLen);
       
   334 
       
   335             context.handshakeHash.push(hashedClientHello);
       
   336 
       
   337             return true;
       
   338         }
   143     }
   339     }
   144 }
   340 }