Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/HelloCookieManager.java
38830 views
/*1* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ssl;2627import java.io.IOException;28import java.security.MessageDigest;29import java.security.SecureRandom;30import java.util.Arrays;31import static sun.security.ssl.ClientHello.ClientHelloMessage;3233/**34* TLS handshake cookie manager35*/36abstract class HelloCookieManager {3738static class Builder {3940final SecureRandom secureRandom;4142private volatile T13HelloCookieManager t13HelloCookieManager;4344Builder(SecureRandom secureRandom) {45this.secureRandom = secureRandom;46}4748HelloCookieManager valueOf(ProtocolVersion protocolVersion) {49if (protocolVersion.useTLS13PlusSpec()) {50if (t13HelloCookieManager != null) {51return t13HelloCookieManager;52}5354synchronized (this) {55if (t13HelloCookieManager == null) {56t13HelloCookieManager =57new T13HelloCookieManager(secureRandom);58}59}6061return t13HelloCookieManager;62}6364return null;65}66}6768abstract byte[] createCookie(ServerHandshakeContext context,69ClientHelloMessage clientHello) throws IOException;7071abstract boolean isCookieValid(ServerHandshakeContext context,72ClientHelloMessage clientHello, byte[] cookie) throws IOException;7374private static final75class T13HelloCookieManager extends HelloCookieManager {7677final SecureRandom secureRandom;78private int cookieVersion; // version + sequence79private final byte[] cookieSecret;80private final byte[] legacySecret;8182T13HelloCookieManager(SecureRandom secureRandom) {83this.secureRandom = secureRandom;84this.cookieVersion = secureRandom.nextInt();85this.cookieSecret = new byte[64];86this.legacySecret = new byte[64];8788secureRandom.nextBytes(cookieSecret);89System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);90}9192@Override93byte[] createCookie(ServerHandshakeContext context,94ClientHelloMessage clientHello) throws IOException {95int version;96byte[] secret;9798synchronized (this) {99version = cookieVersion;100secret = cookieSecret;101102// the cookie secret usage limit is 2^24103if ((cookieVersion & 0xFFFFFF) == 0) { // reset the secret104System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);105secureRandom.nextBytes(cookieSecret);106}107108cookieVersion++; // allow wrapped version number109}110111MessageDigest md = JsseJce.getMessageDigest(112context.negotiatedCipherSuite.hashAlg.name);113byte[] headerBytes = clientHello.getHeaderBytes();114md.update(headerBytes);115byte[] headerCookie = md.digest(secret);116117// hash of ClientHello handshake message118context.handshakeHash.update();119byte[] clientHelloHash = context.handshakeHash.digest();120121// version and cipher suite122//123// Store the negotiated cipher suite in the cookie as well.124// cookie[0]/[1]: cipher suite125// cookie[2]: cookie version126// + (hash length): Mac(ClientHello header)127// + (hash length): Hash(ClientHello)128byte[] prefix = new byte[] {129(byte)((context.negotiatedCipherSuite.id >> 8) & 0xFF),130(byte)(context.negotiatedCipherSuite.id & 0xFF),131(byte)((version >> 24) & 0xFF)132};133134byte[] cookie = Arrays.copyOf(prefix,135prefix.length + headerCookie.length + clientHelloHash.length);136System.arraycopy(headerCookie, 0, cookie,137prefix.length, headerCookie.length);138System.arraycopy(clientHelloHash, 0, cookie,139prefix.length + headerCookie.length, clientHelloHash.length);140141return cookie;142}143144@Override145boolean isCookieValid(ServerHandshakeContext context,146ClientHelloMessage clientHello, byte[] cookie) throws IOException {147// no cookie exchange or not a valid cookie length148if ((cookie == null) || (cookie.length <= 32)) { // 32: roughly149return false;150}151152int csId = ((cookie[0] & 0xFF) << 8) | (cookie[1] & 0xFF);153CipherSuite cs = CipherSuite.valueOf(csId);154if (cs == null || cs.hashAlg == null || cs.hashAlg.hashLength == 0) {155return false;156}157158int hashLen = cs.hashAlg.hashLength;159if (cookie.length != (3 + hashLen * 2)) {160return false;161}162163byte[] prevHeadCookie =164Arrays.copyOfRange(cookie, 3, 3 + hashLen);165byte[] prevClientHelloHash =166Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length);167168byte[] secret;169synchronized (this) {170if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) {171secret = cookieSecret;172} else {173secret = legacySecret; // including out of window cookies174}175}176177MessageDigest md = JsseJce.getMessageDigest(cs.hashAlg.name);178byte[] headerBytes = clientHello.getHeaderBytes();179md.update(headerBytes);180byte[] headerCookie = md.digest(secret);181182if (!Arrays.equals(headerCookie, prevHeadCookie)) {183return false;184}185186// Use the ClientHello hash in the cookie for transtript187// hash calculation for stateless HelloRetryRequest.188//189// Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =190// Hash(message_hash || /* Handshake type */191// 00 00 Hash.length || /* Handshake message length (bytes) */192// Hash(ClientHello1) || /* Hash of ClientHello1 */193// HelloRetryRequest || ... || Mn)194195// Reproduce HelloRetryRequest handshake message196byte[] hrrMessage =197ServerHello.hrrReproducer.produce(context, clientHello);198context.handshakeHash.push(hrrMessage);199200// Construct the 1st ClientHello message for transcript hash201byte[] hashedClientHello = new byte[4 + hashLen];202hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;203hashedClientHello[1] = (byte)0x00;204hashedClientHello[2] = (byte)0x00;205hashedClientHello[3] = (byte)(hashLen & 0xFF);206System.arraycopy(prevClientHelloHash, 0,207hashedClientHello, 4, hashLen);208209context.handshakeHash.push(hashedClientHello);210211return true;212}213}214}215216217