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