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 javax.net.ssl.SSLProtocolException; 30 import java.security.MessageDigest; 31 import java.security.SecureRandom; 32 33 import sun.security.ssl.HandshakeMessage.ClientHello; 34 35 /* 36 * HelloVerifyRequest cookie manager 37 */ 38 final class HelloCookieManager { 39 // the cookie secret life time 40 private static long COOKIE_TIMING_WINDOW = 3600000; // in milliseconds 41 private static int COOKIE_MAX_LENGTH_DTLS10 = 32; // 32 bytes 42 private static int COOKIE_MAX_LENGTH_DTLS12 = 0xFF; // 2^8 -1 bytes 43 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 54 HelloCookieManager(SecureRandom secureRandom) { 55 this.secureRandom = secureRandom; 56 this.cookieDigest = JsseJce.getMessageDigest("SHA-256"); 57 58 this.cookieVersion = secureRandom.nextInt(); 59 this.secretLifetime = 0; 60 this.cookieSecret = null; 61 62 this.prevCookieVersion = 0; 63 this.prevCookieSecret = null; 64 } 65 66 // Used by server side to generate cookies in HelloVerifyRequest message. 67 synchronized byte[] getCookie(ClientHello clientHelloMsg) { 68 if (secretLifetime < System.currentTimeMillis()) { 69 if (cookieSecret != null) { 70 prevCookieVersion = cookieVersion; 71 prevCookieSecret = cookieSecret.clone(); 72 } else { 73 cookieSecret = new byte[32]; 74 } 75 76 cookieVersion++; 77 secureRandom.nextBytes(cookieSecret); 78 secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW; 79 } 80 81 clientHelloMsg.updateHelloCookie(cookieDigest); 82 byte[] cookie = cookieDigest.digest(cookieSecret); // 32 bytes 83 cookie[0] = (byte)((cookieVersion >> 24) & 0xFF); 84 cookie[1] = (byte)((cookieVersion >> 16) & 0xFF); 85 cookie[2] = (byte)((cookieVersion >> 8) & 0xFF); 86 cookie[3] = (byte)(cookieVersion & 0xFF); 87 88 return cookie; 89 } 90 91 // Used by server side to check the cookie in ClientHello message. 92 synchronized boolean isValid(ClientHello clientHelloMsg) { 93 byte[] cookie = clientHelloMsg.cookie; 94 95 // no cookie exchange or not a valid cookie length 96 if ((cookie == null) || (cookie.length != 32)) { 97 return false; 98 } 99 100 int version = ((cookie[0] & 0xFF) << 24) | 101 ((cookie[1] & 0xFF) << 16) | 102 ((cookie[2] & 0xFF) << 8) | 103 (cookie[3] & 0xFF); 104 105 byte[] secret; 106 if (version == cookieVersion) { 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 } 125 126 // Used by client side to check the cookie in HelloVerifyRequest message. 127 static void checkCookie(ProtocolVersion protocolVersion, 128 byte[] cookie) throws IOException { 129 if (cookie != null && cookie.length != 0) { 130 int limit = COOKIE_MAX_LENGTH_DTLS12; 131 if (protocolVersion.v == ProtocolVersion.DTLS10.v) { 132 limit = COOKIE_MAX_LENGTH_DTLS10; 133 } 134 135 if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) { 136 throw new SSLProtocolException( 137 "Invalid HelloVerifyRequest.cookie (length = " + 138 cookie.length + " bytes)"); 139 } 140 } 141 142 // Otherwise, no cookie exchange. 143 } 144 } | 1 /* 2 * Copyright (c) 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.security.MessageDigest; 30 import java.security.SecureRandom; 31 import java.util.Arrays; 32 import static sun.security.ssl.ClientHello.ClientHelloMessage; 33 34 /** 35 * (D)TLS handshake cookie manager 36 */ 37 class HelloCookieManager { 38 final SecureRandom secureRandom; 39 40 private volatile D10HelloCookieManager d10HelloCookieManager; 41 private volatile D13HelloCookieManager d13HelloCookieManager; 42 private volatile T13HelloCookieManager t13HelloCookieManager; 43 44 HelloCookieManager(SecureRandom secureRandom) { 45 this.secureRandom = secureRandom; 46 } 47 48 HelloCookieManager valueOf(ProtocolVersion protocolVersion) { 49 if (protocolVersion.isDTLS) { 50 if (protocolVersion.useTLS13PlusSpec()) { 51 if (d13HelloCookieManager != null) { 52 return d13HelloCookieManager; 53 } 54 55 synchronized (this) { 56 if (d13HelloCookieManager == null) { 57 d13HelloCookieManager = 58 new D13HelloCookieManager(secureRandom); 59 } 60 } 61 62 return d13HelloCookieManager; 63 } else { 64 if (d10HelloCookieManager != null) { 65 return d10HelloCookieManager; 66 } 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 123 secureRandom.nextBytes(cookieSecret); 124 System.arraycopy(cookieSecret, 0, legacySecret, 0, 32); 125 } 126 127 @Override 128 byte[] createCookie(ConnectionContext context, 129 ClientHelloMessage clientHello) throws IOException { 130 int version; 131 byte[] secret; 132 133 synchronized (this) { 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(ConnectionContext 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 } 180 } 181 182 private static final 183 class D13HelloCookieManager extends HelloCookieManager { 184 D13HelloCookieManager(SecureRandom secureRandom) { 185 super(secureRandom); 186 } 187 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 } 345 } 346 } |