jdk/src/share/classes/sun/security/ssl/MAC.java
changeset 16067 36055e4b5305
parent 16045 9d08c3b9a6a0
child 16126 aad71cf676d7
--- a/jdk/src/share/classes/sun/security/ssl/MAC.java	Tue Mar 12 10:35:44 2013 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/MAC.java	Tue Mar 12 15:31:49 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -39,15 +39,19 @@
 
 /**
  * This class computes the "Message Authentication Code" (MAC) for each
- * SSL stream and block cipher message.  This is essentially a shared-secret
- * signature, used to provide integrity protection for SSL messages.  The
- * MAC is actually one of several keyed hashes, as associated with the cipher
- * suite and protocol version. (SSL v3.0 uses one construct, TLS uses another.)
+ * SSL message.  This is essentially a shared-secret signature, used to
+ * provide integrity protection for SSL messages.  The MAC is actually
+ * one of several keyed hashes, as associated with the cipher suite and
+ * protocol version.  (SSL v3.0 uses one construct, TLS uses another.)
+ *
+ * <P>NOTE: MAC computation is the only place in the SSL protocol that the
+ * sequence number is used.  It's also reset to zero with each change of
+ * a cipher spec, so this is the only place this state is needed.
  *
  * @author David Brownell
  * @author Andreas Sterbenz
  */
-final class MAC extends Authenticator {
+final class MAC {
 
     final static MAC NULL = new MAC();
 
@@ -60,9 +64,26 @@
     // JCE Mac object
     private final Mac mac;
 
+    // byte array containing the additional information we MAC in each record
+    // (see below)
+    private final byte[] block;
+
+    // sequence number + record type + + record length
+    private static final int BLOCK_SIZE_SSL = 8 + 1 + 2;
+
+    // sequence number + record type + protocol version + record length
+    private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
+
+    // offset of record type in block
+    private static final int BLOCK_OFFSET_TYPE    = 8;
+
+    // offset of protocol version number in block (TLS only)
+    private static final int BLOCK_OFFSET_VERSION = 8 + 1;
+
     private MAC() {
         macSize = 0;
         mac = null;
+        block = null;
     }
 
     /**
@@ -70,8 +91,6 @@
      */
     MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
             throws NoSuchAlgorithmException, InvalidKeyException {
-        super(protocolVersion);
-
         this.macSize = macAlg.size;
 
         String algorithm;
@@ -91,6 +110,14 @@
 
         mac = JsseJce.getMac(algorithm);
         mac.init(key);
+
+        if (tls) {
+            block = new byte[BLOCK_SIZE_TLS];
+            block[BLOCK_OFFSET_VERSION]   = protocolVersion.major;
+            block[BLOCK_OFFSET_VERSION+1] = protocolVersion.minor;
+        } else {
+            block = new byte[BLOCK_SIZE_SSL];
+        }
     }
 
     /**
@@ -109,15 +136,7 @@
      * @param len the size of the compressed record
      */
     final byte[] compute(byte type, byte buf[], int offset, int len) {
-        if (macSize == 0) {
-            return nullMAC;
-        }
-
-        byte[] additional = acquireAuthenticationBytes(type, len);
-        mac.update(additional);
-        mac.update(buf, offset, len);
-
-        return mac.doFinal();
+        return compute(type, null, buf, offset, len);
     }
 
     /**
@@ -132,13 +151,78 @@
      *          demarcate the data to be MAC'd.
      */
     final byte[] compute(byte type, ByteBuffer bb) {
+        return compute(type, bb, null, 0, bb.remaining());
+    }
+
+    /**
+     * Check whether the sequence number is close to wrap
+     *
+     * Sequence numbers are of type uint64 and may not exceed 2^64-1.
+     * Sequence numbers do not wrap. When the sequence number is near
+     * to wrap, we need to close the connection immediately.
+     */
+    final boolean seqNumOverflow() {
+        /*
+         * Conservatively, we don't allow more records to be generated
+         * when there are only 2^8 sequence numbers left.
+         */
+        return (block != null && mac != null &&
+                block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
+                block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
+                block[6] == (byte)0xFF);
+    }
+
+    /*
+     * Check whether to renew the sequence number
+     *
+     * Sequence numbers are of type uint64 and may not exceed 2^64-1.
+     * Sequence numbers do not wrap.  If a TLS
+     * implementation would need to wrap a sequence number, it must
+     * renegotiate instead.
+     */
+    final boolean seqNumIsHuge() {
+        /*
+         * Conservatively, we should ask for renegotiation when there are
+         * only 2^48 sequence numbers left.
+         */
+        return (block != null && mac != null &&
+                block[0] == (byte)0xFF && block[1] == (byte)0xFF);
+    }
+
+    // increment the sequence number in the block array
+    // it is a 64-bit number stored in big-endian format
+    private void incrementSequenceNumber() {
+        int k = 7;
+        while ((k >= 0) && (++block[k] == 0)) {
+            k--;
+        }
+    }
+
+    /*
+     * Compute based on either buffer type, either bb.position/limit
+     * or buf/offset/len.
+     */
+    private byte[] compute(byte type, ByteBuffer bb, byte[] buf,
+            int offset, int len) {
+
         if (macSize == 0) {
             return nullMAC;
         }
 
-        byte[] additional = acquireAuthenticationBytes(type, bb.remaining());
-        mac.update(additional);
-        mac.update(bb);
+        block[BLOCK_OFFSET_TYPE] = type;
+        block[block.length - 2]  = (byte)(len >> 8);
+        block[block.length - 1]  = (byte)(len     );
+
+        mac.update(block);
+        incrementSequenceNumber();
+
+        // content
+        if (bb != null) {
+            mac.update(bb);
+        } else {
+            mac.update(buf, offset, len);
+        }
 
         return mac.doFinal();
     }