Path: blob/master/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java
41159 views
/*1* Copyright (c) 2003, 2019, 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.ByteArrayOutputStream;28import java.io.IOException;29import java.nio.ByteBuffer;30import java.security.MessageDigest;31import java.security.NoSuchAlgorithmException;32import java.util.Arrays;33import java.util.LinkedList;34import javax.crypto.SecretKey;35import sun.security.util.MessageDigestSpi2;3637final class HandshakeHash {38private TranscriptHash transcriptHash;39private LinkedList<byte[]> reserves; // one handshake message per entry40private boolean hasBeenUsed;4142HandshakeHash() {43this.transcriptHash = new CacheOnlyHash();44this.reserves = new LinkedList<>();45this.hasBeenUsed = false;46}4748// fix the negotiated protocol version and cipher suite49void determine(ProtocolVersion protocolVersion,50CipherSuite cipherSuite) {51if (!(transcriptHash instanceof CacheOnlyHash)) {52throw new IllegalStateException(53"Not expected instance of transcript hash");54}5556CacheOnlyHash coh = (CacheOnlyHash)transcriptHash;57if (protocolVersion.useTLS13PlusSpec()) {58transcriptHash = new T13HandshakeHash(cipherSuite);59} else if (protocolVersion.useTLS12PlusSpec()) {60transcriptHash = new T12HandshakeHash(cipherSuite);61} else if (protocolVersion.useTLS10PlusSpec()) {62transcriptHash = new T10HandshakeHash(cipherSuite);63} else {64transcriptHash = new S30HandshakeHash(cipherSuite);65}6667byte[] reserved = coh.baos.toByteArray();68if (reserved.length != 0) {69transcriptHash.update(reserved, 0, reserved.length);70}71}7273HandshakeHash copy() {74if (transcriptHash instanceof CacheOnlyHash) {75HandshakeHash result = new HandshakeHash();76result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy();77result.reserves = new LinkedList<>(reserves);78result.hasBeenUsed = hasBeenUsed;79return result;80} else {81throw new IllegalStateException("Hash does not support copying");82}83}8485void receive(byte[] input) {86reserves.add(Arrays.copyOf(input, input.length));87}8889void receive(ByteBuffer input, int length) {90if (input.hasArray()) {91int from = input.position() + input.arrayOffset();92int to = from + length;93reserves.add(Arrays.copyOfRange(input.array(), from, to));94} else {95int inPos = input.position();96byte[] holder = new byte[length];97input.get(holder);98input.position(inPos);99reserves.add(Arrays.copyOf(holder, holder.length));100}101}102void receive(ByteBuffer input) {103receive(input, input.remaining());104}105106// For HelloRetryRequest only! Please use this method very carefully!107void push(byte[] input) {108reserves.push(Arrays.copyOf(input, input.length));109}110111// For PreSharedKey to modify the state of the PSK binder hash112byte[] removeLastReceived() {113return reserves.removeLast();114}115116void deliver(byte[] input) {117update();118transcriptHash.update(input, 0, input.length);119}120121void deliver(byte[] input, int offset, int length) {122update();123transcriptHash.update(input, offset, length);124}125126void deliver(ByteBuffer input) {127update();128if (input.hasArray()) {129transcriptHash.update(input.array(),130input.position() + input.arrayOffset(), input.remaining());131} else {132int inPos = input.position();133byte[] holder = new byte[input.remaining()];134input.get(holder);135input.position(inPos);136transcriptHash.update(holder, 0, holder.length);137}138}139140// Use one handshake message if it has not been used.141void utilize() {142if (hasBeenUsed) {143return;144}145if (reserves.size() != 0) {146byte[] holder = reserves.remove();147transcriptHash.update(holder, 0, holder.length);148hasBeenUsed = true;149}150}151152// Consume one handshake message if it has not been consumed.153void consume() {154if (hasBeenUsed) {155hasBeenUsed = false;156return;157}158if (reserves.size() != 0) {159byte[] holder = reserves.remove();160transcriptHash.update(holder, 0, holder.length);161}162}163164void update() {165while (reserves.size() != 0) {166byte[] holder = reserves.remove();167transcriptHash.update(holder, 0, holder.length);168}169hasBeenUsed = false;170}171172byte[] digest() {173// Note that the reserve handshake message may be not a part of174// the expected digest.175return transcriptHash.digest();176}177178void finish() {179this.transcriptHash = new CacheOnlyHash();180this.reserves = new LinkedList<>();181this.hasBeenUsed = false;182}183184// Optional185byte[] archived() {186// Note that the reserve handshake message may be not a part of187// the expected digest.188return transcriptHash.archived();189}190191// Optional, TLS 1.0/1.1 only192byte[] digest(String algorithm) {193T10HandshakeHash hh = (T10HandshakeHash)transcriptHash;194return hh.digest(algorithm);195}196197// Optional, SSL 3.0 only198byte[] digest(String algorithm, SecretKey masterSecret) {199S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;200return hh.digest(algorithm, masterSecret);201}202203// Optional, SSL 3.0 only204byte[] digest(boolean useClientLabel, SecretKey masterSecret) {205S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;206return hh.digest(useClientLabel, masterSecret);207}208209public boolean isHashable(byte handshakeType) {210return handshakeType != SSLHandshake.HELLO_REQUEST.id &&211handshakeType != SSLHandshake.HELLO_VERIFY_REQUEST.id;212}213214interface TranscriptHash {215void update(byte[] input, int offset, int length);216byte[] digest();217byte[] archived(); // optional218}219220// For cache only.221private static final class CacheOnlyHash implements TranscriptHash {222private final ByteArrayOutputStream baos;223224CacheOnlyHash() {225this.baos = new ByteArrayOutputStream();226}227228@Override229public void update(byte[] input, int offset, int length) {230baos.write(input, offset, length);231}232233@Override234public byte[] digest() {235throw new IllegalStateException(236"Not expected call to handshake hash digest");237}238239@Override240public byte[] archived() {241return baos.toByteArray();242}243244CacheOnlyHash copy() {245CacheOnlyHash result = new CacheOnlyHash();246try {247baos.writeTo(result.baos);248} catch (IOException ex) {249throw new RuntimeException("unable to clone hash state");250}251return result;252}253}254255static final class S30HandshakeHash implements TranscriptHash {256static final byte[] MD5_pad1 = genPad(0x36, 48);257static final byte[] MD5_pad2 = genPad(0x5c, 48);258259static final byte[] SHA_pad1 = genPad(0x36, 40);260static final byte[] SHA_pad2 = genPad(0x5c, 40);261262private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };263private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };264265private final MessageDigest mdMD5;266private final MessageDigest mdSHA;267private final TranscriptHash md5;268private final TranscriptHash sha;269private final ByteArrayOutputStream baos;270271S30HandshakeHash(CipherSuite cipherSuite) {272try {273this.mdMD5 = MessageDigest.getInstance("MD5");274this.mdSHA = MessageDigest.getInstance("SHA");275} catch (NoSuchAlgorithmException nsae) {276throw new RuntimeException(277"Hash algorithm MD5 or SHA is not available", nsae);278}279280boolean hasArchived = false;281if (mdMD5 instanceof Cloneable) {282md5 = new CloneableHash(mdMD5);283} else {284hasArchived = true;285md5 = new NonCloneableHash(mdMD5);286}287if (mdSHA instanceof Cloneable) {288sha = new CloneableHash(mdSHA);289} else {290hasArchived = true;291sha = new NonCloneableHash(mdSHA);292}293294if (hasArchived) {295this.baos = null;296} else {297this.baos = new ByteArrayOutputStream();298}299}300301@Override302public void update(byte[] input, int offset, int length) {303md5.update(input, offset, length);304sha.update(input, offset, length);305if (baos != null) {306baos.write(input, offset, length);307}308}309310@Override311public byte[] digest() {312byte[] digest = new byte[36];313System.arraycopy(md5.digest(), 0, digest, 0, 16);314System.arraycopy(sha.digest(), 0, digest, 16, 20);315316return digest;317}318319@Override320public byte[] archived() {321if (baos != null) {322return baos.toByteArray();323} else if (md5 instanceof NonCloneableHash) {324return md5.archived();325} else {326return sha.archived();327}328}329330byte[] digest(boolean useClientLabel, SecretKey masterSecret) {331MessageDigest md5Clone = cloneMd5();332MessageDigest shaClone = cloneSha();333334if (useClientLabel) {335md5Clone.update(SSL_CLIENT);336shaClone.update(SSL_CLIENT);337} else {338md5Clone.update(SSL_SERVER);339shaClone.update(SSL_SERVER);340}341342updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);343updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);344345byte[] digest = new byte[36];346System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);347System.arraycopy(shaClone.digest(), 0, digest, 16, 20);348349return digest;350}351352byte[] digest(String algorithm, SecretKey masterSecret) {353if ("RSA".equalsIgnoreCase(algorithm)) {354MessageDigest md5Clone = cloneMd5();355MessageDigest shaClone = cloneSha();356updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);357updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);358359byte[] digest = new byte[36];360System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);361System.arraycopy(shaClone.digest(), 0, digest, 16, 20);362363return digest;364} else {365MessageDigest shaClone = cloneSha();366updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);367return shaClone.digest();368}369}370371private static byte[] genPad(int b, int count) {372byte[] padding = new byte[count];373Arrays.fill(padding, (byte)b);374return padding;375}376377private MessageDigest cloneMd5() {378MessageDigest md5Clone;379if (mdMD5 instanceof Cloneable) {380try {381md5Clone = (MessageDigest)mdMD5.clone();382} catch (CloneNotSupportedException ex) { // unlikely383throw new RuntimeException(384"MessageDigest does no support clone operation");385}386} else {387try {388md5Clone = MessageDigest.getInstance("MD5");389} catch (NoSuchAlgorithmException nsae) {390throw new RuntimeException(391"Hash algorithm MD5 is not available", nsae);392}393md5Clone.update(md5.archived());394}395396return md5Clone;397}398399private MessageDigest cloneSha() {400MessageDigest shaClone;401if (mdSHA instanceof Cloneable) {402try {403shaClone = (MessageDigest)mdSHA.clone();404} catch (CloneNotSupportedException ex) { // unlikely405throw new RuntimeException(406"MessageDigest does no support clone operation");407}408} else {409try {410shaClone = MessageDigest.getInstance("SHA");411} catch (NoSuchAlgorithmException nsae) {412throw new RuntimeException(413"Hash algorithm SHA is not available", nsae);414}415shaClone.update(sha.archived());416}417418return shaClone;419}420421private static void updateDigest(MessageDigest md,422byte[] pad1, byte[] pad2, SecretKey masterSecret) {423byte[] keyBytes = "RAW".equals(masterSecret.getFormat())424? masterSecret.getEncoded() : null;425if (keyBytes != null) {426md.update(keyBytes);427} else {428digestKey(md, masterSecret);429}430md.update(pad1);431byte[] temp = md.digest();432433if (keyBytes != null) {434md.update(keyBytes);435} else {436digestKey(md, masterSecret);437}438md.update(pad2);439md.update(temp);440}441442private static void digestKey(MessageDigest md, SecretKey key) {443try {444if (md instanceof MessageDigestSpi2) {445((MessageDigestSpi2)md).engineUpdate(key);446} else {447throw new Exception(448"Digest does not support implUpdate(SecretKey)");449}450} catch (Exception e) {451throw new RuntimeException(452"Could not obtain encoded key and "453+ "MessageDigest cannot digest key", e);454}455}456}457458// TLS 1.0 and TLS 1.1459static final class T10HandshakeHash implements TranscriptHash {460private final TranscriptHash md5;461private final TranscriptHash sha;462private final ByteArrayOutputStream baos;463464T10HandshakeHash(CipherSuite cipherSuite) {465MessageDigest mdMD5;466MessageDigest mdSHA;467try {468mdMD5 = MessageDigest.getInstance("MD5");469mdSHA = MessageDigest.getInstance("SHA");470} catch (NoSuchAlgorithmException nsae) {471throw new RuntimeException(472"Hash algorithm MD5 or SHA is not available", nsae);473}474475boolean hasArchived = false;476if (mdMD5 instanceof Cloneable) {477md5 = new CloneableHash(mdMD5);478} else {479hasArchived = true;480md5 = new NonCloneableHash(mdMD5);481}482if (mdSHA instanceof Cloneable) {483sha = new CloneableHash(mdSHA);484} else {485hasArchived = true;486sha = new NonCloneableHash(mdSHA);487}488489if (hasArchived) {490this.baos = null;491} else {492this.baos = new ByteArrayOutputStream();493}494}495496@Override497public void update(byte[] input, int offset, int length) {498md5.update(input, offset, length);499sha.update(input, offset, length);500if (baos != null) {501baos.write(input, offset, length);502}503}504505@Override506public byte[] digest() {507byte[] digest = new byte[36];508System.arraycopy(md5.digest(), 0, digest, 0, 16);509System.arraycopy(sha.digest(), 0, digest, 16, 20);510511return digest;512}513514byte[] digest(String algorithm) {515if ("RSA".equalsIgnoreCase(algorithm)) {516return digest();517} else {518return sha.digest();519}520}521522@Override523public byte[] archived() {524if (baos != null) {525return baos.toByteArray();526} else if (md5 instanceof NonCloneableHash) {527return md5.archived();528} else {529return sha.archived();530}531}532}533534static final class T12HandshakeHash implements TranscriptHash {535private final TranscriptHash transcriptHash;536private final ByteArrayOutputStream baos;537538T12HandshakeHash(CipherSuite cipherSuite) {539MessageDigest md;540try {541md = MessageDigest.getInstance(cipherSuite.hashAlg.name);542} catch (NoSuchAlgorithmException nsae) {543throw new RuntimeException(544"Hash algorithm " +545cipherSuite.hashAlg.name + " is not available", nsae);546}547548if (md instanceof Cloneable) {549transcriptHash = new CloneableHash(md);550this.baos = new ByteArrayOutputStream();551} else {552transcriptHash = new NonCloneableHash(md);553this.baos = null;554}555}556557@Override558public void update(byte[] input, int offset, int length) {559transcriptHash.update(input, offset, length);560if (baos != null) {561baos.write(input, offset, length);562}563}564565@Override566public byte[] digest() {567return transcriptHash.digest();568}569570@Override571public byte[] archived() {572if (baos != null) {573return baos.toByteArray();574} else {575return transcriptHash.archived();576}577}578}579580static final class T13HandshakeHash implements TranscriptHash {581private final TranscriptHash transcriptHash;582583T13HandshakeHash(CipherSuite cipherSuite) {584MessageDigest md;585try {586md = MessageDigest.getInstance(cipherSuite.hashAlg.name);587} catch (NoSuchAlgorithmException nsae) {588throw new RuntimeException(589"Hash algorithm " +590cipherSuite.hashAlg.name + " is not available", nsae);591}592593if (md instanceof Cloneable) {594transcriptHash = new CloneableHash(md);595} else {596transcriptHash = new NonCloneableHash(md);597}598}599600@Override601public void update(byte[] input, int offset, int length) {602transcriptHash.update(input, offset, length);603}604605@Override606public byte[] digest() {607return transcriptHash.digest();608}609610@Override611public byte[] archived() {612// This method is not necessary in T13613throw new UnsupportedOperationException(614"TLS 1.3 does not require archived.");615}616}617618static final class CloneableHash implements TranscriptHash {619private final MessageDigest md;620621CloneableHash(MessageDigest md) {622this.md = md;623}624625@Override626public void update(byte[] input, int offset, int length) {627md.update(input, offset, length);628}629630@Override631public byte[] digest() {632try {633return ((MessageDigest)md.clone()).digest();634} catch (CloneNotSupportedException ex) {635// unlikely636return new byte[0];637}638}639640@Override641public byte[] archived() {642throw new UnsupportedOperationException("Not supported yet.");643}644}645646static final class NonCloneableHash implements TranscriptHash {647private final MessageDigest md;648private final ByteArrayOutputStream baos = new ByteArrayOutputStream();649650NonCloneableHash(MessageDigest md) {651this.md = md;652}653654@Override655public void update(byte[] input, int offset, int length) {656baos.write(input, offset, length);657}658659@Override660public byte[] digest() {661byte[] bytes = baos.toByteArray();662md.reset();663return md.digest(bytes);664}665666@Override667public byte[] archived() {668return baos.toByteArray();669}670}671}672673674