jdk/src/share/classes/sun/security/ssl/MAC.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/ssl/MAC.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1996-2007 Sun Microsystems, Inc.  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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+
+package sun.security.ssl;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import java.nio.ByteBuffer;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+
+import sun.security.ssl.CipherSuite.MacAlg;
+import static sun.security.ssl.CipherSuite.*;
+
+/**
+ * This class computes the "Message Authentication Code" (MAC) for each
+ * 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 {
+
+    final static MAC NULL = new MAC();
+
+    // Value of the null MAC is fixed
+    private static final byte nullMAC[] = new byte[0];
+
+    // internal identifier for the MAC algorithm
+    private final MacAlg        macAlg;
+
+    // stuff defined by the kind of MAC algorithm
+    private final int           macSize;
+
+    // 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;
+        macAlg = M_NULL;
+        mac = null;
+        block = null;
+    }
+
+    /**
+     * Set up, configured for the given SSL/TLS MAC type and version.
+     */
+    MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
+            throws NoSuchAlgorithmException, InvalidKeyException {
+        this.macAlg = macAlg;
+        this.macSize = macAlg.size;
+
+        String algorithm;
+        boolean tls = (protocolVersion.v >= ProtocolVersion.TLS10.v);
+
+        if (macAlg == M_MD5) {
+            algorithm = tls ? "HmacMD5" : "SslMacMD5";
+        } else if (macAlg == M_SHA) {
+            algorithm = tls ? "HmacSHA1" : "SslMacSHA1";
+        } else {
+            throw new RuntimeException("Unknown Mac " + macAlg);
+        }
+
+        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];
+        }
+    }
+
+    /**
+     * Returns the length of the MAC.
+     */
+    int MAClen() {
+        return macSize;
+    }
+
+    /**
+     * Computes and returns the MAC for the data in this byte array.
+     *
+     * @param type record type
+     * @param buf compressed record on which the MAC is computed
+     * @param offset start of compressed record data
+     * @param len the size of the compressed record
+     */
+    final byte[] compute(byte type, byte buf[], int offset, int len) {
+        return compute(type, null, buf, offset, len);
+    }
+
+    /**
+     * Compute and returns the MAC for the remaining data
+     * in this ByteBuffer.
+     *
+     * On return, the bb position == limit, and limit will
+     * have not changed.
+     *
+     * @param type record type
+     * @param bb a ByteBuffer in which the position and limit
+     *          demarcate the data to be MAC'd.
+     */
+    final byte[] compute(byte type, ByteBuffer bb) {
+        return compute(type, bb, null, 0, bb.remaining());
+    }
+
+    // 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;
+        }
+
+        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();
+    }
+
+}