src/java.base/share/classes/sun/security/ssl/HandshakeHash.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 48358 38493aecb3d1
child 56646 e57205a6e4ee
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
     1 /*
     1 /*
     2  * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2003, 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
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    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
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 
       
    27 package sun.security.ssl;
    26 package sun.security.ssl;
    28 
    27 
    29 import java.io.ByteArrayOutputStream;
    28 import java.io.ByteArrayOutputStream;
    30 import java.security.*;
    29 import java.io.IOException;
    31 import java.util.Locale;
       
    32 import java.nio.ByteBuffer;
    30 import java.nio.ByteBuffer;
    33 
    31 import java.security.MessageDigest;
    34 /**
    32 import java.util.Arrays;
    35  * Abstraction for the SSL/TLS hash of all handshake messages that is
    33 import java.util.LinkedList;
    36  * maintained to verify the integrity of the negotiation. Internally,
    34 import javax.crypto.SecretKey;
    37  * it consists of an MD5 and an SHA1 digest. They are used in the client
    35 import sun.security.util.MessageDigestSpi2;
    38  * and server finished messages and in certificate verify messages (if sent).
    36 
    39  *
       
    40  * This class transparently deals with cloneable and non-cloneable digests.
       
    41  *
       
    42  * This class now supports TLS 1.2 also. The key difference for TLS 1.2
       
    43  * is that you cannot determine the hash algorithms for CertificateVerify
       
    44  * at a early stage. On the other hand, it's simpler than TLS 1.1 (and earlier)
       
    45  * that there is no messy MD5+SHA1 digests.
       
    46  *
       
    47  * You need to obey these conventions when using this class:
       
    48  *
       
    49  * 1. protocolDetermined(version) should be called when the negotiated
       
    50  * protocol version is determined.
       
    51  *
       
    52  * 2. Before protocolDetermined() is called, only update(), and reset()
       
    53  * and setFinishedAlg() can be called.
       
    54  *
       
    55  * 3. After protocolDetermined() is called, reset() cannot be called.
       
    56  *
       
    57  * 4. After protocolDetermined() is called, if the version is pre-TLS 1.2,
       
    58  * getFinishedHash() cannot be called. Otherwise,
       
    59  * getMD5Clone() and getSHAClone() cannot be called.
       
    60  *
       
    61  * 5. getMD5Clone() and getSHAClone() can only be called after
       
    62  * protocolDetermined() is called and version is pre-TLS 1.2.
       
    63  *
       
    64  * 6. getFinishedHash() can only be called after protocolDetermined()
       
    65  * and setFinishedAlg() have been called and the version is TLS 1.2.
       
    66  *
       
    67  * Suggestion: Call protocolDetermined() and setFinishedAlg()
       
    68  * as early as possible.
       
    69  *
       
    70  * Example:
       
    71  * <pre>
       
    72  * HandshakeHash hh = new HandshakeHash(...)
       
    73  * hh.protocolDetermined(ProtocolVersion.TLS12);
       
    74  * hh.update(clientHelloBytes);
       
    75  * hh.setFinishedAlg("SHA-256");
       
    76  * hh.update(serverHelloBytes);
       
    77  * ...
       
    78  * hh.update(CertificateVerifyBytes);
       
    79  * ...
       
    80  * hh.update(finished1);
       
    81  * byte[] finDigest1 = hh.getFinishedHash();
       
    82  * hh.update(finished2);
       
    83  * byte[] finDigest2 = hh.getFinishedHash();
       
    84  * </pre>
       
    85  */
       
    86 final class HandshakeHash {
    37 final class HandshakeHash {
    87 
    38     private TranscriptHash transcriptHash;
    88     // Common
    39     private LinkedList<byte[]> reserves;    // one handshake message per entry
    89 
    40     private boolean hasBeenUsed;
    90     // -1:  unknown
    41 
    91     //  1:  <=TLS 1.1
    42     HandshakeHash() {
    92     //  2:  TLS 1.2
    43         this.transcriptHash = new CacheOnlyHash();
    93     private int version = -1;
    44         this.reserves = new LinkedList<>();
    94     private ByteArrayOutputStream data = new ByteArrayOutputStream();
    45         this.hasBeenUsed = false;
    95 
    46     }
    96     // For TLS 1.1
    47 
    97     private MessageDigest md5, sha;
    48     // fix the negotiated protocol version and cipher suite
    98     private final int clonesNeeded;    // needs to be saved for later use
    49     void determine(ProtocolVersion protocolVersion,
    99 
    50             CipherSuite cipherSuite) {
   100     // For TLS 1.2
    51         if (!(transcriptHash instanceof CacheOnlyHash)) {
   101     private MessageDigest finMD;
    52             throw new IllegalStateException(
   102 
    53                     "Not expected instance of transcript hash");
   103     // Cache for input record handshake hash computation
    54         }
   104     private ByteArrayOutputStream reserve = new ByteArrayOutputStream();
    55 
   105 
    56         CacheOnlyHash coh = (CacheOnlyHash)transcriptHash;
   106     /**
    57         if (protocolVersion.useTLS13PlusSpec()) {
   107      * Create a new HandshakeHash. needCertificateVerify indicates whether
    58             transcriptHash = new T13HandshakeHash(cipherSuite);
   108      * a hash for the certificate verify message is required.
    59         } else if (protocolVersion.useTLS12PlusSpec()) {
   109      */
    60             transcriptHash = new T12HandshakeHash(cipherSuite);
   110     HandshakeHash(boolean needCertificateVerify) {
    61         } else if (protocolVersion.useTLS10PlusSpec()) {
   111         // We may rework the code later, but for now we use hard-coded number
    62             transcriptHash = new T10HandshakeHash(cipherSuite);
   112         // of clones if the underlying MessageDigests are not cloneable.
    63         } else {
   113         //
    64             transcriptHash = new S30HandshakeHash(cipherSuite);
   114         // The number used here is based on the current handshake protocols and
    65         }
   115         // implementation.  It may be changed if the handshake processe gets
    66 
   116         // changed in the future, for example adding a new extension that
    67         byte[] reserved = coh.baos.toByteArray();
   117         // requires handshake hash.  Please be careful about the number of
    68         if (reserved.length != 0) {
   118         // clones if additional handshak hash is required in the future.
    69             transcriptHash.update(reserved, 0, reserved.length);
   119         //
    70         }
   120         // For the current implementation, the handshake hash is required for
    71     }
   121         // the following items:
    72 
   122         //     . CertificateVerify handshake message (optional)
    73     HandshakeHash copy() {
   123         //     . client Finished handshake message
    74         if (transcriptHash instanceof CacheOnlyHash) {
   124         //     . server Finished Handshake message
    75             HandshakeHash result = new HandshakeHash();
   125         //     . the extended Master Secret extension [RFC 7627]
    76             result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy();
   126         //
    77             result.reserves = new LinkedList<>(reserves);
   127         // Note that a late call to server setNeedClientAuth dose not update
    78             result.hasBeenUsed = hasBeenUsed;
   128         // the number of clones.  We may address the issue later.
    79             return result;
   129         //
    80         } else {
   130         // Note for safety, we allocate one more clone for the current
    81             throw new IllegalStateException("Hash does not support copying");
   131         // implementation.  We may consider it more carefully in the future
    82         }
   132         // for the exact number or rework the code in a different way.
    83     }
   133         clonesNeeded = needCertificateVerify ? 5 : 4;
    84 
   134     }
    85     void receive(byte[] input) {
   135 
    86         reserves.add(Arrays.copyOf(input, input.length));
   136     void reserve(ByteBuffer input) {
    87     }
       
    88 
       
    89     void receive(byte[] input, int offset, int length) {
       
    90         reserves.add(Arrays.copyOfRange(input, offset, offset + length));
       
    91     }
       
    92 
       
    93     void receive(ByteBuffer input, int length) {
   137         if (input.hasArray()) {
    94         if (input.hasArray()) {
   138             reserve.write(input.array(),
    95             int from = input.position() + input.arrayOffset();
       
    96             int to = from + length;
       
    97             reserves.add(Arrays.copyOfRange(input.array(), from, to));
       
    98         } else {
       
    99             int inPos = input.position();
       
   100             byte[] holder = new byte[length];
       
   101             input.get(holder);
       
   102             input.position(inPos);
       
   103             reserves.add(Arrays.copyOf(holder, holder.length));
       
   104         }
       
   105     }
       
   106     void receive(ByteBuffer input) {
       
   107         receive(input, input.remaining());
       
   108     }
       
   109 
       
   110     // For HelloRetryRequest only! Please use this method very carefully!
       
   111     void push(byte[] input) {
       
   112         reserves.push(Arrays.copyOf(input, input.length));
       
   113     }
       
   114 
       
   115     // For PreSharedKey to modify the state of the PSK binder hash
       
   116     byte[] removeLastReceived() {
       
   117         return reserves.removeLast();
       
   118     }
       
   119 
       
   120     void deliver(byte[] input) {
       
   121         update();
       
   122         transcriptHash.update(input, 0, input.length);
       
   123     }
       
   124 
       
   125     void deliver(byte[] input, int offset, int length) {
       
   126         update();
       
   127         transcriptHash.update(input, offset, length);
       
   128     }
       
   129 
       
   130     void deliver(ByteBuffer input) {
       
   131         update();
       
   132         if (input.hasArray()) {
       
   133             transcriptHash.update(input.array(),
   139                     input.position() + input.arrayOffset(), input.remaining());
   134                     input.position() + input.arrayOffset(), input.remaining());
   140         } else {
   135         } else {
   141             int inPos = input.position();
   136             int inPos = input.position();
   142             byte[] holder = new byte[input.remaining()];
   137             byte[] holder = new byte[input.remaining()];
   143             input.get(holder);
   138             input.get(holder);
   144             input.position(inPos);
   139             input.position(inPos);
   145             reserve.write(holder, 0, holder.length);
   140             transcriptHash.update(holder, 0, holder.length);
   146         }
   141         }
   147     }
   142     }
   148 
   143 
   149     void reserve(byte[] b, int offset, int len) {
   144     // Use one handshake message if it has not been used.
   150         reserve.write(b, offset, len);
   145     void utilize() {
   151     }
   146         if (hasBeenUsed) {
   152 
   147             return;
   153     void reload() {
   148         }
   154         if (reserve.size() != 0) {
   149         if (reserves.size() != 0) {
   155             byte[] bytes = reserve.toByteArray();
   150             byte[] holder = reserves.remove();
   156             reserve.reset();
   151             transcriptHash.update(holder, 0, holder.length);
   157             update(bytes, 0, bytes.length);
   152             hasBeenUsed = true;
   158         }
   153         }
   159     }
   154     }
   160 
   155 
   161     void update(ByteBuffer input) {
   156     // Consume one handshake message if it has not been consumed.
   162 
   157     void consume() {
   163         // reload if there are reserved messages.
   158         if (hasBeenUsed) {
   164         reload();
   159             hasBeenUsed = false;
   165 
   160             return;
   166         int inPos = input.position();
   161         }
   167         switch (version) {
   162         if (reserves.size() != 0) {
   168             case 1:
   163             byte[] holder = reserves.remove();
   169                 md5.update(input);
   164             transcriptHash.update(holder, 0, holder.length);
   170                 input.position(inPos);
   165         }
   171 
   166     }
   172                 sha.update(input);
   167 
   173                 input.position(inPos);
   168     void update() {
   174 
   169         while (reserves.size() != 0) {
   175                 break;
   170             byte[] holder = reserves.remove();
   176             default:
   171             transcriptHash.update(holder, 0, holder.length);
   177                 if (finMD != null) {
   172         }
   178                     finMD.update(input);
   173         hasBeenUsed = false;
   179                     input.position(inPos);
   174     }
       
   175 
       
   176     byte[] digest() {
       
   177         // Note that the reserve handshake message may be not a part of
       
   178         // the expected digest.
       
   179         return transcriptHash.digest();
       
   180     }
       
   181 
       
   182     void finish() {
       
   183         this.transcriptHash = new CacheOnlyHash();
       
   184         this.reserves = new LinkedList<>();
       
   185         this.hasBeenUsed = false;
       
   186     }
       
   187 
       
   188     // Optional
       
   189     byte[] archived() {
       
   190         // Note that the reserve handshake message may be not a part of
       
   191         // the expected digest.
       
   192         return transcriptHash.archived();
       
   193     }
       
   194 
       
   195     // Optional, TLS 1.0/1.1 only
       
   196     byte[] digest(String algorithm) {
       
   197         T10HandshakeHash hh = (T10HandshakeHash)transcriptHash;
       
   198         return hh.digest(algorithm);
       
   199     }
       
   200 
       
   201     // Optional, SSL 3.0 only
       
   202     byte[] digest(String algorithm, SecretKey masterSecret) {
       
   203         S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
       
   204         return hh.digest(algorithm, masterSecret);
       
   205     }
       
   206 
       
   207     // Optional, SSL 3.0 only
       
   208     byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
       
   209         S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
       
   210         return hh.digest(useClientLabel, masterSecret);
       
   211     }
       
   212 
       
   213     public boolean isHashable(byte handshakeType) {
       
   214         return handshakeType != SSLHandshake.HELLO_REQUEST.id &&
       
   215                handshakeType != SSLHandshake.HELLO_VERIFY_REQUEST.id;
       
   216     }
       
   217 
       
   218     interface TranscriptHash {
       
   219         void update(byte[] input, int offset, int length);
       
   220         byte[] digest();
       
   221         byte[] archived();  // optional
       
   222     }
       
   223 
       
   224     // For cache only.
       
   225     private static final class CacheOnlyHash implements TranscriptHash {
       
   226         private final ByteArrayOutputStream baos;
       
   227 
       
   228         CacheOnlyHash() {
       
   229             this.baos = new ByteArrayOutputStream();
       
   230         }
       
   231 
       
   232         @Override
       
   233         public void update(byte[] input, int offset, int length) {
       
   234             baos.write(input, offset, length);
       
   235         }
       
   236 
       
   237         @Override
       
   238         public byte[] digest() {
       
   239             throw new IllegalStateException(
       
   240                     "Not expected call to handshake hash digest");
       
   241         }
       
   242 
       
   243         @Override
       
   244         public byte[] archived() {
       
   245             return baos.toByteArray();
       
   246         }
       
   247 
       
   248         CacheOnlyHash copy() {
       
   249             CacheOnlyHash result = new CacheOnlyHash();
       
   250             try {
       
   251                 baos.writeTo(result.baos);
       
   252             } catch (IOException ex) {
       
   253                 throw new RuntimeException("unable to to clone hash state");
       
   254             }
       
   255             return result;
       
   256         }
       
   257     }
       
   258 
       
   259     static final class S30HandshakeHash implements TranscriptHash {
       
   260         static final byte[] MD5_pad1 = genPad(0x36, 48);
       
   261         static final byte[] MD5_pad2 = genPad(0x5c, 48);
       
   262 
       
   263         static final byte[] SHA_pad1 = genPad(0x36, 40);
       
   264         static final byte[] SHA_pad2 = genPad(0x5c, 40);
       
   265 
       
   266         private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
       
   267         private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
       
   268 
       
   269         private final MessageDigest mdMD5;
       
   270         private final MessageDigest mdSHA;
       
   271         private final TranscriptHash md5;
       
   272         private final TranscriptHash sha;
       
   273         private final ByteArrayOutputStream baos;
       
   274 
       
   275         S30HandshakeHash(CipherSuite cipherSuite) {
       
   276             this.mdMD5 = JsseJce.getMessageDigest("MD5");
       
   277             this.mdSHA = JsseJce.getMessageDigest("SHA");
       
   278 
       
   279             boolean hasArchived = false;
       
   280             if (mdMD5 instanceof Cloneable) {
       
   281                 md5 = new CloneableHash(mdMD5);
       
   282             } else {
       
   283                 hasArchived = true;
       
   284                 md5 = new NonCloneableHash(mdMD5);
       
   285             }
       
   286             if (mdSHA instanceof Cloneable) {
       
   287                 sha = new CloneableHash(mdSHA);
       
   288             } else {
       
   289                 hasArchived = true;
       
   290                 sha = new NonCloneableHash(mdSHA);
       
   291             }
       
   292 
       
   293             if (hasArchived) {
       
   294                 this.baos = null;
       
   295             } else {
       
   296                 this.baos = new ByteArrayOutputStream();
       
   297             }
       
   298         }
       
   299 
       
   300         @Override
       
   301         public void update(byte[] input, int offset, int length) {
       
   302             md5.update(input, offset, length);
       
   303             sha.update(input, offset, length);
       
   304             if (baos != null) {
       
   305                 baos.write(input, offset, length);
       
   306             }
       
   307         }
       
   308 
       
   309         @Override
       
   310         public byte[] digest() {
       
   311             byte[] digest = new byte[36];
       
   312             System.arraycopy(md5.digest(), 0, digest, 0, 16);
       
   313             System.arraycopy(sha.digest(), 0, digest, 16, 20);
       
   314 
       
   315             return digest;
       
   316         }
       
   317 
       
   318         @Override
       
   319         public byte[] archived() {
       
   320             if (baos != null) {
       
   321                 return baos.toByteArray();
       
   322             } else if (md5 instanceof NonCloneableHash) {
       
   323                 return md5.archived();
       
   324             } else {
       
   325                 return sha.archived();
       
   326             }
       
   327         }
       
   328 
       
   329         byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
       
   330             MessageDigest md5Clone = cloneMd5();
       
   331             MessageDigest shaClone = cloneSha();
       
   332 
       
   333             if (useClientLabel) {
       
   334                 md5Clone.update(SSL_CLIENT);
       
   335                 shaClone.update(SSL_CLIENT);
       
   336             } else {
       
   337                 md5Clone.update(SSL_SERVER);
       
   338                 shaClone.update(SSL_SERVER);
       
   339             }
       
   340 
       
   341             updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
       
   342             updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
       
   343 
       
   344             byte[] digest = new byte[36];
       
   345             System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
       
   346             System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
       
   347 
       
   348             return digest;
       
   349         }
       
   350 
       
   351         byte[] digest(String algorithm, SecretKey masterSecret) {
       
   352             if ("RSA".equalsIgnoreCase(algorithm)) {
       
   353                 MessageDigest md5Clone = cloneMd5();
       
   354                 MessageDigest shaClone = cloneSha();
       
   355                 updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
       
   356                 updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
       
   357 
       
   358                 byte[] digest = new byte[36];
       
   359                 System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
       
   360                 System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
       
   361 
       
   362                 return digest;
       
   363             } else {
       
   364                 MessageDigest shaClone = cloneSha();
       
   365                 updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
       
   366                 return shaClone.digest();
       
   367             }
       
   368         }
       
   369 
       
   370         private static byte[] genPad(int b, int count) {
       
   371             byte[] padding = new byte[count];
       
   372             Arrays.fill(padding, (byte)b);
       
   373             return padding;
       
   374         }
       
   375 
       
   376         private MessageDigest cloneMd5() {
       
   377             MessageDigest md5Clone;
       
   378             if (mdMD5 instanceof Cloneable) {
       
   379                 try {
       
   380                     md5Clone = (MessageDigest)mdMD5.clone();
       
   381                 } catch (CloneNotSupportedException ex) {   // unlikely
       
   382                     throw new RuntimeException(
       
   383                             "MessageDigest does no support clone operation");
   180                 }
   384                 }
   181                 if (input.hasArray()) {
   385             } else {
   182                     data.write(input.array(),
   386                 md5Clone = JsseJce.getMessageDigest("MD5");
   183                             inPos + input.arrayOffset(), input.remaining());
   387                 md5Clone.update(md5.archived());
       
   388             }
       
   389 
       
   390             return md5Clone;
       
   391         }
       
   392 
       
   393         private MessageDigest cloneSha() {
       
   394             MessageDigest shaClone;
       
   395             if (mdSHA instanceof Cloneable) {
       
   396                 try {
       
   397                     shaClone = (MessageDigest)mdSHA.clone();
       
   398                 } catch (CloneNotSupportedException ex) {   // unlikely
       
   399                     throw new RuntimeException(
       
   400                             "MessageDigest does no support clone operation");
       
   401                 }
       
   402             } else {
       
   403                 shaClone = JsseJce.getMessageDigest("SHA");
       
   404                 shaClone.update(sha.archived());
       
   405             }
       
   406 
       
   407             return shaClone;
       
   408         }
       
   409 
       
   410         private static void updateDigest(MessageDigest md,
       
   411                 byte[] pad1, byte[] pad2, SecretKey masterSecret) {
       
   412             byte[] keyBytes = "RAW".equals(masterSecret.getFormat())
       
   413                             ? masterSecret.getEncoded() : null;
       
   414             if (keyBytes != null) {
       
   415                 md.update(keyBytes);
       
   416             } else {
       
   417                 digestKey(md, masterSecret);
       
   418             }
       
   419             md.update(pad1);
       
   420             byte[] temp = md.digest();
       
   421 
       
   422             if (keyBytes != null) {
       
   423                 md.update(keyBytes);
       
   424             } else {
       
   425                 digestKey(md, masterSecret);
       
   426             }
       
   427             md.update(pad2);
       
   428             md.update(temp);
       
   429         }
       
   430 
       
   431         private static void digestKey(MessageDigest md, SecretKey key) {
       
   432             try {
       
   433                 if (md instanceof MessageDigestSpi2) {
       
   434                     ((MessageDigestSpi2)md).engineUpdate(key);
   184                 } else {
   435                 } else {
   185                     byte[] holder = new byte[input.remaining()];
   436                     throw new Exception(
   186                     input.get(holder);
   437                         "Digest does not support implUpdate(SecretKey)");
   187                     input.position(inPos);
       
   188                     data.write(holder, 0, holder.length);
       
   189                 }
   438                 }
   190                 break;
   439             } catch (Exception e) {
   191         }
   440                 throw new RuntimeException(
   192     }
   441                     "Could not obtain encoded key and "
   193 
   442                     + "MessageDigest cannot digest key", e);
   194     void update(byte handshakeType, byte[] handshakeBody) {
   443             }
   195 
   444         }
   196         // reload if there are reserved messages.
   445     }
   197         reload();
   446 
   198 
   447     // TLS 1.0 and TLS 1.1
   199         switch (version) {
   448     static final class T10HandshakeHash implements TranscriptHash {
   200             case 1:
   449         private final TranscriptHash md5;
   201                 md5.update(handshakeType);
   450         private final TranscriptHash sha;
   202                 sha.update(handshakeType);
   451         private final ByteArrayOutputStream baos;
   203 
   452 
   204                 md5.update((byte)((handshakeBody.length >> 16) & 0xFF));
   453         T10HandshakeHash(CipherSuite cipherSuite) {
   205                 sha.update((byte)((handshakeBody.length >> 16) & 0xFF));
   454             MessageDigest mdMD5 = JsseJce.getMessageDigest("MD5");
   206                 md5.update((byte)((handshakeBody.length >> 8) & 0xFF));
   455             MessageDigest mdSHA = JsseJce.getMessageDigest("SHA");
   207                 sha.update((byte)((handshakeBody.length >> 8) & 0xFF));
   456 
   208                 md5.update((byte)(handshakeBody.length & 0xFF));
   457             boolean hasArchived = false;
   209                 sha.update((byte)(handshakeBody.length & 0xFF));
   458             if (mdMD5 instanceof Cloneable) {
   210 
   459                 md5 = new CloneableHash(mdMD5);
   211                 md5.update(handshakeBody);
   460             } else {
   212                 sha.update(handshakeBody);
   461                 hasArchived = true;
   213                 break;
   462                 md5 = new NonCloneableHash(mdMD5);
   214             default:
   463             }
   215                 if (finMD != null) {
   464             if (mdSHA instanceof Cloneable) {
   216                     finMD.update(handshakeType);
   465                 sha = new CloneableHash(mdSHA);
   217                     finMD.update((byte)((handshakeBody.length >> 16) & 0xFF));
   466             } else {
   218                     finMD.update((byte)((handshakeBody.length >> 8) & 0xFF));
   467                 hasArchived = true;
   219                     finMD.update((byte)(handshakeBody.length & 0xFF));
   468                 sha = new NonCloneableHash(mdSHA);
   220                     finMD.update(handshakeBody);
   469             }
   221                 }
   470 
   222                 data.write(handshakeType);
   471             if (hasArchived) {
   223                 data.write((byte)((handshakeBody.length >> 16) & 0xFF));
   472                 this.baos = null;
   224                 data.write((byte)((handshakeBody.length >> 8) & 0xFF));
   473             } else {
   225                 data.write((byte)(handshakeBody.length & 0xFF));
   474                 this.baos = new ByteArrayOutputStream();
   226                 data.write(handshakeBody, 0, handshakeBody.length);
   475             }
   227                 break;
   476         }
   228         }
   477 
   229     }
   478         @Override
   230 
   479         public void update(byte[] input, int offset, int length) {
   231     void update(byte[] b, int offset, int len) {
   480             md5.update(input, offset, length);
   232 
   481             sha.update(input, offset, length);
   233         // reload if there are reserved messages.
   482             if (baos != null) {
   234         reload();
   483                 baos.write(input, offset, length);
   235 
   484             }
   236         switch (version) {
   485         }
   237             case 1:
   486 
   238                 md5.update(b, offset, len);
   487         @Override
   239                 sha.update(b, offset, len);
   488         public byte[] digest() {
   240                 break;
   489             byte[] digest = new byte[36];
   241             default:
   490             System.arraycopy(md5.digest(), 0, digest, 0, 16);
   242                 if (finMD != null) {
   491             System.arraycopy(sha.digest(), 0, digest, 16, 20);
   243                     finMD.update(b, offset, len);
   492 
   244                 }
   493             return digest;
   245                 data.write(b, offset, len);
   494         }
   246                 break;
   495 
   247         }
   496         byte[] digest(String algorithm) {
   248     }
   497             if ("RSA".equalsIgnoreCase(algorithm)) {
   249 
   498                 return digest();
   250     /**
   499             } else {
   251      * Reset the remaining digests. Note this does *not* reset the number of
   500                 return sha.digest();
   252      * digest clones that can be obtained. Digests that have already been
   501             }
   253      * cloned and are gone remain gone.
   502         }
   254      */
   503 
   255     void reset() {
   504         @Override
   256         if (version != -1) {
   505         public byte[] archived() {
   257             throw new RuntimeException(
   506             if (baos != null) {
   258                     "reset() can be only be called before protocolDetermined");
   507                 return baos.toByteArray();
   259         }
   508             } else if (md5 instanceof NonCloneableHash) {
   260         data.reset();
   509                 return md5.archived();
   261     }
   510             } else {
   262 
   511                 return sha.archived();
   263 
   512             }
   264     void protocolDetermined(ProtocolVersion pv) {
   513         }
   265 
   514     }
   266         // Do not set again, will ignore
   515 
   267         if (version != -1) {
   516     static final class T12HandshakeHash implements TranscriptHash {
   268             return;
   517         private final TranscriptHash transcriptHash;
   269         }
   518         private final ByteArrayOutputStream baos;
   270 
   519 
   271         if (pv.maybeDTLSProtocol()) {
   520         T12HandshakeHash(CipherSuite cipherSuite) {
   272             version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1;
   521             MessageDigest md =
   273         } else {
   522                     JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
   274             version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
   523             if (md instanceof Cloneable) {
   275         }
   524                 transcriptHash = new CloneableHash(md);
   276         switch (version) {
   525                 this.baos = null;
   277             case 1:
   526             } else {
   278                 // initiate md5, sha and call update on saved array
   527                 transcriptHash = new NonCloneableHash(md);
   279                 try {
   528                 this.baos = new ByteArrayOutputStream();
   280                     md5 = CloneableDigest.getDigest("MD5", clonesNeeded);
   529             }
   281                     sha = CloneableDigest.getDigest("SHA", clonesNeeded);
   530         }
   282                 } catch (NoSuchAlgorithmException e) {
   531 
   283                     throw new RuntimeException
   532         @Override
   284                                 ("Algorithm MD5 or SHA not available", e);
   533         public void update(byte[] input, int offset, int length) {
   285                 }
   534             transcriptHash.update(input, offset, length);
   286                 byte[] bytes = data.toByteArray();
   535             if (baos != null) {
   287                 update(bytes, 0, bytes.length);
   536                 baos.write(input, offset, length);
   288                 break;
   537             }
   289             case 2:
   538         }
   290                 break;
   539 
   291         }
   540         @Override
   292     }
   541         public byte[] digest() {
   293 
   542             return transcriptHash.digest();
   294     /////////////////////////////////////////////////////////////
   543         }
   295     // Below are old methods for pre-TLS 1.1
   544 
   296     /////////////////////////////////////////////////////////////
   545         @Override
   297 
   546         public byte[] archived() {
   298     /**
   547             if (baos != null) {
   299      * Return a new MD5 digest updated with all data hashed so far.
   548                 return baos.toByteArray();
   300      */
   549             } else {
   301     MessageDigest getMD5Clone() {
   550                 return transcriptHash.archived();
   302         if (version != 1) {
   551             }
   303             throw new RuntimeException(
   552         }
   304                     "getMD5Clone() can be only be called for TLS 1.1");
   553     }
   305         }
   554 
   306         return cloneDigest(md5);
   555     static final class T13HandshakeHash implements TranscriptHash {
   307     }
   556         private final TranscriptHash transcriptHash;
   308 
   557         private final ByteArrayOutputStream baos;
   309     /**
   558 
   310      * Return a new SHA digest updated with all data hashed so far.
   559         T13HandshakeHash(CipherSuite cipherSuite) {
   311      */
   560             MessageDigest md =
   312     MessageDigest getSHAClone() {
   561                     JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
   313         if (version != 1) {
   562             if (md instanceof Cloneable) {
   314             throw new RuntimeException(
   563                 transcriptHash = new CloneableHash(md);
   315                     "getSHAClone() can be only be called for TLS 1.1");
   564                 this.baos = null;
   316         }
   565             } else {
   317         return cloneDigest(sha);
   566                 transcriptHash = new NonCloneableHash(md);
   318     }
   567                 this.baos = new ByteArrayOutputStream();
   319 
   568             }
   320     private static MessageDigest cloneDigest(MessageDigest digest) {
   569         }
   321         try {
   570 
   322             return (MessageDigest)digest.clone();
   571         @Override
   323         } catch (CloneNotSupportedException e) {
   572         public void update(byte[] input, int offset, int length) {
   324             // cannot occur for digests generated via CloneableDigest
   573             transcriptHash.update(input, offset, length);
   325             throw new RuntimeException("Could not clone digest", e);
   574             if (baos != null) {
   326         }
   575                 baos.write(input, offset, length);
   327     }
   576             }
   328 
   577         }
   329     /////////////////////////////////////////////////////////////
   578 
   330     // Below are new methods for TLS 1.2
   579         @Override
   331     /////////////////////////////////////////////////////////////
   580         public byte[] digest() {
   332 
   581             return transcriptHash.digest();
   333     private static String normalizeAlgName(String alg) {
   582         }
   334         alg = alg.toUpperCase(Locale.US);
   583 
   335         if (alg.startsWith("SHA")) {
   584         @Override
   336             if (alg.length() == 3) {
   585         public byte[] archived() {
   337                 return "SHA-1";
   586             if (baos != null) {
   338             }
   587                 return baos.toByteArray();
   339             if (alg.charAt(3) != '-') {
   588             } else {
   340                 return "SHA-" + alg.substring(3);
   589                 return transcriptHash.archived();
   341             }
   590             }
   342         }
   591 
   343         return alg;
   592             // throw new UnsupportedOperationException("Not supported yet.");
   344     }
   593         }
   345     /**
   594     }
   346      * Specifies the hash algorithm used in Finished. This should be called
   595 
   347      * based in info in ServerHello.
   596     static final class CloneableHash implements TranscriptHash {
   348      * Can be called multiple times.
   597         private final MessageDigest md;
   349      */
   598 
   350     void setFinishedAlg(String s) {
   599         CloneableHash(MessageDigest md) {
   351         if (s == null) {
   600             this.md = md;
   352             throw new RuntimeException(
   601         }
   353                     "setFinishedAlg's argument cannot be null");
   602 
   354         }
   603         @Override
   355 
   604         public void update(byte[] input, int offset, int length) {
   356         // Can be called multiple times, but only set once
   605             md.update(input, offset, length);
   357         if (finMD != null) return;
   606         }
   358 
   607 
   359         try {
   608         @Override
   360             // See comment in the contructor.
   609         public byte[] digest() {
   361             finMD = CloneableDigest.getDigest(normalizeAlgName(s), 4);
   610             try {
   362         } catch (NoSuchAlgorithmException e) {
   611                 return ((MessageDigest)md.clone()).digest();
   363             throw new Error(e);
   612             } catch (CloneNotSupportedException ex) {
   364         }
   613                 // unlikely
   365         finMD.update(data.toByteArray());
   614                 return new byte[0];
   366     }
   615             }
   367 
   616         }
   368     byte[] getAllHandshakeMessages() {
   617 
   369         return data.toByteArray();
   618         @Override
   370     }
   619         public byte[] archived() {
   371 
   620             throw new UnsupportedOperationException("Not supported yet.");
   372     /**
   621         }
   373      * Calculates the hash in Finished. Must be called after setFinishedAlg().
   622     }
   374      * This method can be called twice, for Finished messages of the server
   623 
   375      * side and client side respectively.
   624     static final class NonCloneableHash implements TranscriptHash {
   376      */
   625         private final MessageDigest md;
   377     byte[] getFinishedHash() {
   626         private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
   378         try {
   627 
   379             return cloneDigest(finMD).digest();
   628         NonCloneableHash(MessageDigest md) {
   380         } catch (Exception e) {
   629             this.md = md;
   381             throw new Error("Error during hash calculation", e);
   630         }
       
   631 
       
   632         @Override
       
   633         public void update(byte[] input, int offset, int length) {
       
   634             baos.write(input, offset, length);
       
   635         }
       
   636 
       
   637         @Override
       
   638         public byte[] digest() {
       
   639             byte[] bytes = baos.toByteArray();
       
   640             md.reset();
       
   641             return md.digest(bytes);
       
   642         }
       
   643 
       
   644         @Override
       
   645         public byte[] archived() {
       
   646             return baos.toByteArray();
   382         }
   647         }
   383     }
   648     }
   384 }
   649 }
   385 
       
   386 /**
       
   387  * A wrapper for MessageDigests that simulates cloning of non-cloneable
       
   388  * digests. It uses the standard MessageDigest API and therefore can be used
       
   389  * transparently in place of a regular digest.
       
   390  *
       
   391  * Note that we extend the MessageDigest class directly rather than
       
   392  * MessageDigestSpi. This works because MessageDigest was originally designed
       
   393  * this way in the JDK 1.1 days which allows us to avoid creating an internal
       
   394  * provider.
       
   395  *
       
   396  * It can be "cloned" a limited number of times, which is specified at
       
   397  * construction time. This is achieved by internally maintaining n digests
       
   398  * in parallel. Consequently, it is only 1/n-th times as fast as the original
       
   399  * digest.
       
   400  *
       
   401  * Example:
       
   402  *   MessageDigest md = CloneableDigest.getDigest("SHA", 2);
       
   403  *   md.update(data1);
       
   404  *   MessageDigest md2 = (MessageDigest)md.clone();
       
   405  *   md2.update(data2);
       
   406  *   byte[] d1 = md2.digest(); // digest of data1 || data2
       
   407  *   md.update(data3);
       
   408  *   byte[] d2 = md.digest();  // digest of data1 || data3
       
   409  *
       
   410  * This class is not thread safe.
       
   411  *
       
   412  */
       
   413 final class CloneableDigest extends MessageDigest implements Cloneable {
       
   414 
       
   415     /**
       
   416      * The individual MessageDigests. Initially, all elements are non-null.
       
   417      * When clone() is called, the non-null element with the maximum index is
       
   418      * returned and the array element set to null.
       
   419      *
       
   420      * All non-null element are always in the same state.
       
   421      */
       
   422     private final MessageDigest[] digests;
       
   423 
       
   424     private CloneableDigest(MessageDigest digest, int n, String algorithm)
       
   425             throws NoSuchAlgorithmException {
       
   426         super(algorithm);
       
   427         digests = new MessageDigest[n];
       
   428         digests[0] = digest;
       
   429         for (int i = 1; i < n; i++) {
       
   430             digests[i] = JsseJce.getMessageDigest(algorithm);
       
   431         }
       
   432     }
       
   433 
       
   434     /**
       
   435      * Return a MessageDigest for the given algorithm that can be cloned the
       
   436      * specified number of times. If the default implementation supports
       
   437      * cloning, it is returned. Otherwise, an instance of this class is
       
   438      * returned.
       
   439      */
       
   440     static MessageDigest getDigest(String algorithm, int n)
       
   441             throws NoSuchAlgorithmException {
       
   442         MessageDigest digest = JsseJce.getMessageDigest(algorithm);
       
   443         try {
       
   444             digest.clone();
       
   445             // already cloneable, use it
       
   446             return digest;
       
   447         } catch (CloneNotSupportedException e) {
       
   448             return new CloneableDigest(digest, n, algorithm);
       
   449         }
       
   450     }
       
   451 
       
   452     /**
       
   453      * Check if this object is still usable. If it has already been cloned the
       
   454      * maximum number of times, there are no digests left and this object can no
       
   455      * longer be used.
       
   456      */
       
   457     private void checkState() {
       
   458         // XXX handshaking currently doesn't stop updating hashes...
       
   459         // if (digests[0] == null) {
       
   460         //     throw new IllegalStateException("no digests left");
       
   461         // }
       
   462     }
       
   463 
       
   464     @Override
       
   465     protected int engineGetDigestLength() {
       
   466         checkState();
       
   467         return digests[0].getDigestLength();
       
   468     }
       
   469 
       
   470     @Override
       
   471     protected void engineUpdate(byte b) {
       
   472         checkState();
       
   473         for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
       
   474             digests[i].update(b);
       
   475         }
       
   476     }
       
   477 
       
   478     @Override
       
   479     protected void engineUpdate(byte[] b, int offset, int len) {
       
   480         checkState();
       
   481         for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
       
   482             digests[i].update(b, offset, len);
       
   483         }
       
   484     }
       
   485 
       
   486     @Override
       
   487     protected byte[] engineDigest() {
       
   488         checkState();
       
   489         byte[] digest = digests[0].digest();
       
   490         digestReset();
       
   491         return digest;
       
   492     }
       
   493 
       
   494     @Override
       
   495     protected int engineDigest(byte[] buf, int offset, int len)
       
   496             throws DigestException {
       
   497         checkState();
       
   498         int n = digests[0].digest(buf, offset, len);
       
   499         digestReset();
       
   500         return n;
       
   501     }
       
   502 
       
   503     /**
       
   504      * Reset all digests after a digest() call. digests[0] has already been
       
   505      * implicitly reset by the digest() call and does not need to be reset
       
   506      * again.
       
   507      */
       
   508     private void digestReset() {
       
   509         for (int i = 1; (i < digests.length) && (digests[i] != null); i++) {
       
   510             digests[i].reset();
       
   511         }
       
   512     }
       
   513 
       
   514     @Override
       
   515     protected void engineReset() {
       
   516         checkState();
       
   517         for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
       
   518             digests[i].reset();
       
   519         }
       
   520     }
       
   521 
       
   522     @Override
       
   523     public Object clone() {
       
   524         checkState();
       
   525         for (int i = digests.length - 1; i >= 0; i--) {
       
   526             if (digests[i] != null) {
       
   527                 MessageDigest digest = digests[i];
       
   528                 digests[i] = null;
       
   529                 return digest;
       
   530             }
       
   531         }
       
   532         // cannot occur
       
   533         throw new InternalError();
       
   534     }
       
   535 
       
   536 }