summaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/javax/crypto/mac
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/gnu/javax/crypto/mac')
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/BaseMac.java127
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/HMac.java263
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/HMacFactory.java111
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/IMac.java181
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/MacFactory.java130
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/MacInputStream.java124
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/MacOutputStream.java123
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/OMAC.java303
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/TMMH16.java339
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/UHash32.java758
-rw-r--r--libjava/classpath/gnu/javax/crypto/mac/UMac32.java418
11 files changed, 2877 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/crypto/mac/BaseMac.java b/libjava/classpath/gnu/javax/crypto/mac/BaseMac.java
new file mode 100644
index 000000000..4c524e905
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/BaseMac.java
@@ -0,0 +1,127 @@
+/* BaseMac.java --
+ Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.hash.IMessageDigest;
+
+import java.security.InvalidKeyException;
+import java.util.Map;
+
+/**
+ * A base abstract class to facilitate <i>MAC</i> (Message Authentication Code)
+ * implementations.
+ */
+public abstract class BaseMac
+ implements IMac
+{
+ /** The canonical name prefix of the <i>MAC</i>. */
+ protected String name;
+ /** Reference to the underlying hash algorithm instance. */
+ protected IMessageDigest underlyingHash;
+ /** The length of the truncated output in bytes. */
+ protected int truncatedSize;
+
+ /**
+ * Trivial constructor for use by concrete subclasses.
+ *
+ * @param name the canonical name of this instance.
+ */
+ protected BaseMac(String name)
+ {
+ super();
+
+ this.name = name;
+ }
+
+ /**
+ * Trivial constructor for use by concrete subclasses.
+ *
+ * @param name the canonical name of this instance.
+ * @param underlyingHash the underlying message digest algorithm instance.
+ */
+ protected BaseMac(String name, IMessageDigest underlyingHash)
+ {
+ this(name);
+
+ if (underlyingHash != null)
+ truncatedSize = underlyingHash.hashSize();
+ this.underlyingHash = underlyingHash;
+ }
+
+ public String name()
+ {
+ return name;
+ }
+
+ public int macSize()
+ {
+ return truncatedSize;
+ }
+
+ public void update(byte b)
+ {
+ underlyingHash.update(b);
+ }
+
+ public void update(byte[] b, int offset, int len)
+ {
+ underlyingHash.update(b, offset, len);
+ }
+
+ public void reset()
+ {
+ underlyingHash.reset();
+ }
+
+ public Object clone() throws CloneNotSupportedException
+ {
+ BaseMac result = (BaseMac) super.clone();
+ if (this.underlyingHash != null)
+ result.underlyingHash = (IMessageDigest) this.underlyingHash.clone();
+
+ return result;
+ }
+
+ public abstract void init(Map attributes) throws InvalidKeyException,
+ IllegalStateException;
+
+ public abstract byte[] digest();
+
+ public abstract boolean selfTest();
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/HMac.java b/libjava/classpath/gnu/javax/crypto/mac/HMac.java
new file mode 100644
index 000000000..ae2cd3ce2
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/HMac.java
@@ -0,0 +1,263 @@
+/* HMac.java --
+ Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.Registry;
+import gnu.java.security.hash.IMessageDigest;
+import gnu.java.security.hash.MD5;
+import gnu.java.security.util.Util;
+
+import java.security.InvalidKeyException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The implementation of the <i>HMAC</i> (Keyed-Hash Message Authentication
+ * Code).
+ * <p>
+ * <i>HMAC</i> can be used in combination with any iterated cryptographic hash
+ * function. <i>HMAC</i> also uses a <i>secret key</i> for calculation and
+ * verification of the message authentication values. The main goals behind this
+ * construction are:
+ * <ul>
+ * <li>To use, without modifications, available hash functions. In particular,
+ * hash functions that perform well in software, and for which code is freely
+ * and widely available.</li>
+ * <li>To preserve the original performance of the hash function without
+ * incurring a significant degradation.</li>
+ * <li>To use and handle keys in a simple way.</li>
+ * <li>To have a well understood cryptographic analysis of the strength of the
+ * authentication mechanism based on reasonable assumptions on the underlying
+ * hash function.</li>
+ * <li>To allow for easy replaceability of the underlying hash function in case
+ * that faster or more secure hash functions are found or required.</li>
+ * </ul>
+ * <p>
+ * References:
+ * <ol>
+ * <li><a href="http://www.ietf.org/rfc/rfc-2104.txt">RFC 2104</a>HMAC:
+ * Keyed-Hashing for Message Authentication.<br>
+ * H. Krawczyk, M. Bellare, and R. Canetti.</li>
+ * </ol>
+ */
+public class HMac
+ extends BaseMac
+ implements Cloneable
+{
+ public static final String USE_WITH_PKCS5_V2 = "gnu.crypto.hmac.pkcs5";
+ private static final byte IPAD_BYTE = 0x36;
+ private static final byte OPAD_BYTE = 0x5C;
+ /** caches the result of the correctness test, once executed. */
+ private static Boolean valid;
+ protected int macSize;
+ protected int blockSize;
+ protected IMessageDigest ipadHash;
+ protected IMessageDigest opadHash;
+ protected byte[] ipad;
+
+ /**
+ * Trivial constructor for use by concrete subclasses.
+ *
+ * @param underlyingHash the underlying hash algorithm instance.
+ */
+ protected HMac(IMessageDigest underlyingHash)
+ {
+ super(Registry.HMAC_NAME_PREFIX + underlyingHash.name(), underlyingHash);
+
+ this.blockSize = underlyingHash.blockSize();
+ this.macSize = underlyingHash.hashSize();
+ ipadHash = opadHash = null;
+ }
+
+ public Object clone() throws CloneNotSupportedException
+ {
+ HMac result = (HMac) super.clone();
+ if (this.ipadHash != null)
+ result.ipadHash = (IMessageDigest) this.ipadHash.clone();
+ if (this.opadHash != null)
+ result.opadHash = (IMessageDigest) this.opadHash.clone();
+ if (this.ipad != null)
+ result.ipad = (byte[]) this.ipad.clone();
+
+ return result;
+ }
+
+ public void init(Map attributes) throws InvalidKeyException,
+ IllegalStateException
+ {
+ Integer ts = (Integer) attributes.get(TRUNCATED_SIZE);
+ truncatedSize = (ts == null ? macSize : ts.intValue());
+ if (truncatedSize < (macSize / 2))
+ throw new IllegalArgumentException("Truncated size too small");
+ else if (truncatedSize < 10)
+ throw new IllegalArgumentException("Truncated size less than 80 bits");
+
+ // we dont use/save the key outside this method
+ byte[] K = (byte[]) attributes.get(MAC_KEY_MATERIAL);
+ if (K == null)
+ { // take it as an indication to re-use previous key if set
+ if (ipadHash == null)
+ throw new InvalidKeyException("Null key");
+ // we already went through the motions; ie. up to step #4. re-use
+ underlyingHash = (IMessageDigest) ipadHash.clone();
+ return;
+ }
+
+ // for HMACs used in key-derivation functions (e.g. PBKDF2) the key material
+ // need not be >= the (output) block size of the underlying algorithm
+ Boolean pkcs5 = (Boolean) attributes.get(USE_WITH_PKCS5_V2);
+ if (pkcs5 == null)
+ pkcs5 = Boolean.FALSE;
+ if (K.length < macSize && ! pkcs5.booleanValue())
+ throw new InvalidKeyException("Key too short");
+
+ if (K.length > blockSize)
+ {
+ // (0) replace K with HASH(K) if K is larger than the hash's block size.
+ // Then pad with zeros until it is the correct size (the next `if').
+ underlyingHash.update(K, 0, K.length);
+ K = underlyingHash.digest();
+ }
+ if (K.length < blockSize)
+ {
+ // (1) append zeros to the end of K to create a B byte string (e.g., if
+ // K is of length 20 bytes and B=64, then K will be appended with 44
+ // zero bytes 0x00)
+ int limit = (K.length > blockSize) ? blockSize : K.length;
+ byte[] newK = new byte[blockSize];
+ System.arraycopy(K, 0, newK, 0, limit);
+ K = newK;
+ }
+ underlyingHash.reset();
+ opadHash = (IMessageDigest) underlyingHash.clone();
+ if (ipad == null)
+ ipad = new byte[blockSize];
+ // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1)
+ // with ipad
+ // (3) append the stream of data 'text' to the B byte string resulting from
+ // step (2)
+ // (4) apply H to the stream generated in step (3)
+ for (int i = 0; i < blockSize; i++)
+ ipad[i] = (byte)(K[i] ^ IPAD_BYTE);
+ for (int i = 0; i < blockSize; i++)
+ opadHash.update((byte)(K[i] ^ OPAD_BYTE));
+ underlyingHash.update(ipad, 0, blockSize);
+ ipadHash = (IMessageDigest) underlyingHash.clone();
+ K = null;
+ }
+
+ public void reset()
+ {
+ super.reset();
+ if (ipad != null)
+ {
+ underlyingHash.update(ipad, 0, blockSize);
+ ipadHash = (IMessageDigest) underlyingHash.clone();
+ }
+ }
+
+ public byte[] digest()
+ {
+ if (ipadHash == null)
+ throw new IllegalStateException("HMAC not initialised");
+ byte[] out = underlyingHash.digest();
+ // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1)
+ // with opad
+ underlyingHash = (IMessageDigest) opadHash.clone();
+ // (6) append the H result from step (4) to the B byte string resulting from
+ // step (5)
+ underlyingHash.update(out, 0, macSize);
+ // (7) apply H to the stream generated in step (6) and output the result
+ out = underlyingHash.digest(); // which also resets the underlying hash
+ // truncate and return
+ if (truncatedSize == macSize)
+ return out;
+ byte[] result = new byte[truncatedSize];
+ System.arraycopy(out, 0, result, 0, truncatedSize);
+ return result;
+ }
+
+ public boolean selfTest()
+ {
+ if (valid == null)
+ {
+ try
+ {
+ IMac mac = new HMac(new MD5()); // use rfc-2104 test vectors
+ String tv1 = "9294727A3638BB1C13F48EF8158BFC9D";
+ String tv3 = "56BE34521D144C88DBB8C733F0E8B3F6";
+ byte[] k1 = new byte[] {
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B };
+ byte[] k3 = new byte[] {
+ (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
+ (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
+ (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
+ (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA };
+ byte[] data = new byte[50];
+ for (int i = 0; i < 50;)
+ data[i++] = (byte) 0xDD;
+
+ HashMap map = new HashMap();
+ // test vector #1
+ map.put(MAC_KEY_MATERIAL, k1);
+ mac.init(map);
+ mac.update("Hi There".getBytes("ASCII"), 0, 8);
+ if (! tv1.equals(Util.toString(mac.digest())))
+ valid = Boolean.FALSE;
+
+ // test #2 is not used since it causes a "Key too short" exception
+
+ // test vector #3
+ map.put(MAC_KEY_MATERIAL, k3);
+ mac.init(map);
+ mac.update(data, 0, 50);
+ if (! tv3.equals(Util.toString(mac.digest())))
+ valid = Boolean.FALSE;
+ valid = Boolean.TRUE;
+ }
+ catch (Exception x)
+ {
+ x.printStackTrace(System.err);
+ valid = Boolean.FALSE;
+ }
+ }
+ return valid.booleanValue();
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/HMacFactory.java b/libjava/classpath/gnu/javax/crypto/mac/HMacFactory.java
new file mode 100644
index 000000000..0afd8c6ac
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/HMacFactory.java
@@ -0,0 +1,111 @@
+/* HMacFactory.java --
+ Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.Registry;
+import gnu.java.security.hash.HashFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A <i>Factory</i> to instantiate Keyed-Hash Message Authentication Code
+ * (HMAC) algorithm instances.
+ */
+public class HMacFactory
+ implements Registry
+{
+ /** Trivial constructor to enforce <i>Singleton</i> pattern. */
+ private HMacFactory()
+ {
+ super();
+ }
+
+ /**
+ * Return an instance of a <i>HMAC</i> algorithm given the name of its
+ * underlying hash function, prefixed with the literal defined in
+ * {@link Registry#HMAC_NAME_PREFIX}.
+ *
+ * @param name the fully qualified name of the underlying algorithm: composed
+ * as the concatenation of a literal prefix (see
+ * {@link Registry#HMAC_NAME_PREFIX}) and the name of the underlying
+ * hash algorithm.
+ * @return an instance of the <i>HMAC</i> algorithm, or <code>null</code>
+ * if none can be constructed.
+ * @exception InternalError if the implementation does not pass its self-test.
+ */
+ public static IMac getInstance(String name)
+ {
+ if (name == null)
+ return null;
+
+ name = name.trim();
+ name = name.toLowerCase();
+ if (! name.startsWith(HMAC_NAME_PREFIX))
+ return null;
+
+ // strip the prefix
+ name = name.substring(HMAC_NAME_PREFIX.length()).trim();
+ IMac result = new HMac(HashFactory.getInstance(name));
+ if (result != null && ! result.selfTest())
+ throw new InternalError(result.name());
+
+ return result;
+ }
+
+ /**
+ * <p>
+ * Returns a {@link java.util.Set} of names of <i>HMAC</i> algorithms
+ * supported by this <i>Factory</i>.
+ * </p>
+ *
+ * @return a {@link java.util.Set} of HMAC algorithm names (Strings).
+ */
+ public static final Set getNames()
+ {
+ Set hashNames = HashFactory.getNames();
+ HashSet hs = new HashSet();
+ for (Iterator it = hashNames.iterator(); it.hasNext();)
+ hs.add(HMAC_NAME_PREFIX + ((String) it.next()));
+
+ return Collections.unmodifiableSet(hs);
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/IMac.java b/libjava/classpath/gnu/javax/crypto/mac/IMac.java
new file mode 100644
index 000000000..a9582564d
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/IMac.java
@@ -0,0 +1,181 @@
+/* IMac.java --
+ Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import java.security.InvalidKeyException;
+import java.util.Map;
+
+/**
+ * The basic visible methods of any MAC (Message Authentication Code) algorithm.
+ * <p>
+ * A <i>MAC</i> provides a way to check the integrity of information
+ * transmitted over, or stored in, an unreliable medium, based on a secret key.
+ * Typically, <i>MAC</i>s are used between two parties, that share a common
+ * secret key, in order to validate information transmitted between them.
+ * <p>
+ * When a <i>MAC</i> algorithm is based on a cryptographic hash function, it is
+ * then called to a <i>HMAC</i> (Hashed Message Authentication Code) --see <a
+ * href="http://www.ietf.org/rfc/rfc-2104.txt">RFC-2104</a>.
+ * <p>
+ * Another type of <i>MAC</i> algorithms exist: UMAC or <i>Universal Message
+ * Authentication Code</i>, described in <a
+ * href="http://www.ietf.org/internet-drafts/draft-krovetz-umac-01.txt">
+ * draft-krovetz-umac-01.txt</a>.
+ * <p>
+ * With <i>UMAC</i>s, the sender and receiver share a common secret key (the
+ * <i>MAC</i> key) which determines:
+ * <ul>
+ * <li>The key for a <i>universal hash function</i>. This hash function is
+ * <i>non-cryptographic</i>, in the sense that it does not need to have any
+ * cryptographic <i>hardness</i> property. Rather, it needs to satisfy some
+ * combinatorial property, which can be proven to hold without relying on
+ * unproven hardness assumptions.</li>
+ * <li>The key for a <i>pseudorandom function</i>. This is where one needs a
+ * cryptographic hardness assumption. The pseudorandom function may be obtained
+ * from a <i>block cipher</i> or a <i>cryptographic hash function</i>. </li>
+ * </ul>
+ * <p>
+ * References:
+ * <ol>
+ * <li><a href="http://www.ietf.org/rfc/rfc-2104.txt">RFC 2104</a>HMAC:
+ * Keyed-Hashing for Message Authentication.<br>
+ * H. Krawczyk, M. Bellare, and R. Canetti.</li>
+ * <li><a href="http://www.ietf.org/internet-drafts/draft-krovetz-umac-01.txt">
+ * UMAC</a>: Message Authentication Code using Universal Hashing.<br>
+ * T. Krovetz, J. Black, S. Halevi, A. Hevia, H. Krawczyk, and P. Rogaway.</li>
+ * </ol>
+ */
+public interface IMac
+{
+ /**
+ * Property name of the user-supplied key material. The value associated to
+ * this property name is taken to be a byte array.
+ */
+ String MAC_KEY_MATERIAL = "gnu.crypto.mac.key.material";
+ /**
+ * Property name of the desired truncated output size in bytes. The value
+ * associated to this property name is taken to be an integer. If no value is
+ * specified in the attributes map at initialisation time, then all bytes of
+ * the underlying hash algorithm's output are emitted.
+ * <p>
+ * This implementation, follows the recommendation of the <i>RFC 2104</i>
+ * authors; specifically:
+ * <pre>
+ * We recommend that the output length t be not less than half the
+ * length of the hash output (to match the birthday attack bound)
+ * and not less than 80 bits (a suitable lower bound on the number
+ * of bits that need to be predicted by an attacker).
+ * </pre>
+ */
+ String TRUNCATED_SIZE = "gnu.crypto.mac.truncated.size";
+
+ /**
+ * Returns the canonical name of this algorithm.
+ *
+ * @return the canonical name of this algorithm.
+ */
+ String name();
+
+ /**
+ * Returns the output length in bytes of this <i>MAC</i> algorithm.
+ *
+ * @return the output length in bytes of this <i>MAC</i> algorithm.
+ */
+ int macSize();
+
+ /**
+ * Initialises the algorithm with designated attributes. Permissible names and
+ * values are described in the class documentation above.
+ *
+ * @param attributes a set of name-value pairs that describe the desired
+ * future instance behaviour.
+ * @exception InvalidKeyException if the key data is invalid.
+ * @exception IllegalStateException if the instance is already initialised.
+ * @see #MAC_KEY_MATERIAL
+ */
+ void init(Map attributes) throws InvalidKeyException, IllegalStateException;
+
+ /**
+ * Continues a <i>MAC</i> operation using the input byte.
+ *
+ * @param b the input byte to digest.
+ */
+ void update(byte b);
+
+ /**
+ * Continues a <i>MAC</i> operation, by filling the buffer, processing data
+ * in the algorithm's MAC_SIZE-bit block(s), updating the context and count,
+ * and buffering the remaining bytes in buffer for the next operation.
+ *
+ * @param in the input block.
+ * @param offset start of meaningful bytes in input block.
+ * @param length number of bytes, in input block, to consider.
+ */
+ void update(byte[] in, int offset, int length);
+
+ /**
+ * Completes the <i>MAC</i> by performing final operations such as padding
+ * and resetting the instance.
+ *
+ * @return the array of bytes representing the <i>MAC</i> value.
+ */
+ byte[] digest();
+
+ /**
+ * Resets the algorithm instance for re-initialisation and use with other
+ * characteristics. This method always succeeds.
+ */
+ void reset();
+
+ /**
+ * A basic test. Ensures that the MAC of a pre-determined message is equal to
+ * a known pre-computed value.
+ *
+ * @return <code>true</code> if the implementation passes a basic self-test.
+ * Returns <code>false</code> otherwise.
+ */
+ boolean selfTest();
+
+ /**
+ * Returns a clone copy of this instance.
+ *
+ * @return a clone copy of this instance.
+ */
+ Object clone() throws CloneNotSupportedException;
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/MacFactory.java b/libjava/classpath/gnu/javax/crypto/mac/MacFactory.java
new file mode 100644
index 000000000..5e3b50f7f
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/MacFactory.java
@@ -0,0 +1,130 @@
+/* MacFactory.java --
+ Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.Registry;
+import gnu.javax.crypto.cipher.CipherFactory;
+import gnu.javax.crypto.cipher.IBlockCipher;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A <i>Factory</i> that instantiates instances of every supported Message
+ * Authentication Code algorithms, including all <i>HMAC</i> algorithms.
+ */
+public class MacFactory
+ implements Registry
+{
+ private static Set names;
+
+ /** Trivial constructor to enforce <i>Singleton</i> pattern. */
+ private MacFactory()
+ {
+ super();
+ }
+
+ /**
+ * Returns an instance of a <i>MAC</i> algorithm given its name.
+ *
+ * @param name the name of the MAC algorithm.
+ * @return an instance of the <i>MAC</i> algorithm, or <code>null</code> if
+ * none can be constructed.
+ * @exception InternalError if the implementation does not pass its self-test.
+ */
+ public static IMac getInstance(String name)
+ {
+ if (name == null)
+ return null;
+
+ name = name.trim();
+ name = name.toLowerCase();
+ if (name.startsWith(HMAC_NAME_PREFIX))
+ return HMacFactory.getInstance(name);
+
+ if (name.startsWith(OMAC_PREFIX))
+ {
+ name = name.substring(OMAC_PREFIX.length());
+ IBlockCipher cipher = CipherFactory.getInstance(name);
+ if (cipher == null)
+ return null;
+ return new OMAC(cipher);
+ }
+ IMac result = null;
+ if (name.equalsIgnoreCase(UHASH32))
+ result = new UHash32();
+ else if (name.equalsIgnoreCase(UMAC32))
+ result = new UMac32();
+ else if (name.equalsIgnoreCase(TMMH16))
+ result = new TMMH16();
+
+ if (result != null && ! result.selfTest())
+ throw new InternalError(result.name());
+
+ return result;
+ }
+
+ /**
+ * Returns a {@link Set} of names of <i>MAC</i> algorithms supported by this
+ * <i>Factory</i>.
+ *
+ * @return a {@link Set} of MAC names (Strings).
+ */
+ public static final Set getNames()
+ {
+ synchronized (MacFactory.class)
+ {
+ if (names == null)
+ {
+ HashSet hs = new HashSet();
+ hs.addAll(HMacFactory.getNames());
+ hs.add(UHASH32);
+ hs.add(UMAC32);
+ hs.add(TMMH16);
+ for (Iterator it = CipherFactory.getNames().iterator(); it.hasNext();)
+ hs.add(OMAC_PREFIX + it.next());
+
+ names = Collections.unmodifiableSet(hs);
+ }
+ }
+ return names;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/MacInputStream.java b/libjava/classpath/gnu/javax/crypto/mac/MacInputStream.java
new file mode 100644
index 000000000..7ea808aa9
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/MacInputStream.java
@@ -0,0 +1,124 @@
+/* MacInputStream.java --
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * A filtering input stream that computes a MAC (message authentication code)
+ * over all data read from the stream.
+ */
+public class MacInputStream
+ extends FilterInputStream
+{
+ /** The digesting state. The MAC is updated only if this flag is true. */
+ private boolean digesting;
+ /** The MAC being updated. */
+ private IMac mac;
+
+ /**
+ * Creates a new MacInputStream. The stream is initially set to digest data
+ * written, the <i>mac</i> argument must have already been initialized, and
+ * the <i>mac</i> argument is <b>not</b> cloned.
+ *
+ * @param in The underlying input stream.
+ * @param mac The mac instance to use.
+ */
+ public MacInputStream(InputStream in, IMac mac)
+ {
+ super(in);
+ if (mac == null)
+ throw new NullPointerException();
+ this.mac = mac;
+ digesting = true;
+ }
+
+ /**
+ * Returns the MAC this stream is updating.
+ *
+ * @return The MAC.
+ */
+ public IMac getMac()
+ {
+ return mac;
+ }
+
+ /**
+ * Sets the MAC this stream is updating, which must have already been
+ * initialized. The argument is not cloned by this method.
+ *
+ * @param mac The new MAC.
+ * @throws NullPointerException If the argument is null.
+ */
+ public void setMac(IMac mac)
+ {
+ if (mac == null)
+ throw new NullPointerException();
+ this.mac = mac;
+ }
+
+ /**
+ * Turns the digesting state on or off. When off, the MAC will not be updated
+ * when data is written to the stream.
+ *
+ * @param flag The new digesting state.
+ */
+ public void on(boolean flag)
+ {
+ digesting = flag;
+ }
+
+ public int read() throws IOException
+ {
+ int i = in.read();
+ if (digesting && i != -1)
+ mac.update((byte) i);
+ return i;
+ }
+
+ public int read(byte[] buf, int off, int len) throws IOException
+ {
+ int i = in.read(buf, off, len);
+ if (digesting && i != -1)
+ mac.update(buf, off, i);
+ return i;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/MacOutputStream.java b/libjava/classpath/gnu/javax/crypto/mac/MacOutputStream.java
new file mode 100644
index 000000000..2aa352d75
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/MacOutputStream.java
@@ -0,0 +1,123 @@
+/* MacOutputStream.java --
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A filtering output stream that computes a MAC (message authentication code)
+ * over all data written to the stream.
+ */
+public class MacOutputStream
+ extends FilterOutputStream
+{
+ /** The digesting state. The MAC is updated only if this flag is true. */
+ private boolean digesting;
+ /** The MAC being updated. */
+ private IMac mac;
+
+ /**
+ * Creates a new <code>MacOutputStream</code>. The stream is initially set
+ * to digest data written, the <code>mac</code> argument must have already
+ * been initialized, and the <code>mac</code> argument is <b>not</b>
+ * cloned.
+ *
+ * @param out The underlying output stream.
+ * @param mac The mac instance to use.
+ */
+ public MacOutputStream(OutputStream out, IMac mac)
+ {
+ super(out);
+ if (mac == null)
+ throw new NullPointerException();
+ this.mac = mac;
+ digesting = true;
+ }
+
+ /**
+ * Returns the MAC this stream is updating.
+ *
+ * @return The MAC.
+ */
+ public IMac getMac()
+ {
+ return mac;
+ }
+
+ /**
+ * Sets the MAC this stream is updating, which must have already been
+ * initialized. The argument is not cloned by this method.
+ *
+ * @param mac The non-null new MAC.
+ * @throws NullPointerException If the argument is null.
+ */
+ public void setMac(IMac mac)
+ {
+ if (mac == null)
+ throw new NullPointerException();
+ this.mac = mac;
+ }
+
+ /**
+ * Turns the digesting state on or off. When off, the MAC will not be updated
+ * when data is written to the stream.
+ *
+ * @param flag The new digesting state.
+ */
+ public void on(boolean flag)
+ {
+ digesting = flag;
+ }
+
+ public void write(int b) throws IOException
+ {
+ if (digesting)
+ mac.update((byte) b);
+ out.write(b);
+ }
+
+ public void write(byte[] buf, int off, int len) throws IOException
+ {
+ if (digesting)
+ mac.update(buf, off, len);
+ out.write(buf, off, len);
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/OMAC.java b/libjava/classpath/gnu/javax/crypto/mac/OMAC.java
new file mode 100644
index 000000000..6758b314f
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/OMAC.java
@@ -0,0 +1,303 @@
+/* OMAC.java --
+ Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.Configuration;
+import gnu.java.security.Registry;
+import gnu.java.security.util.Util;
+import gnu.javax.crypto.cipher.CipherFactory;
+import gnu.javax.crypto.cipher.IBlockCipher;
+import gnu.javax.crypto.mode.IMode;
+
+import java.security.InvalidKeyException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * The One-Key CBC MAC, OMAC. This message authentication code is based on a
+ * block cipher in CBC mode.
+ * <p>
+ * References:
+ * <ol>
+ * <li>Tetsu Iwata and Kaoru Kurosawa, <i><a
+ * href="http://crypt.cis.ibaraki.ac.jp/omac/docs/omac.pdf">OMAC: One-Key CBC
+ * MAC</a></i>.</li>
+ * </ol>
+ */
+public class OMAC
+ implements IMac
+{
+ private static final Logger log = Logger.getLogger(OMAC.class.getName());
+ private static final byte C1 = (byte) 0x87;
+ private static final byte C2 = 0x1b;
+ // Test key for OMAC-AES-128
+ private static final byte[] KEY0 =
+ Util.toBytesFromString("2b7e151628aed2a6abf7158809cf4f3c");
+ // Test MAC for zero-length input.
+ private static final byte[] DIGEST0 =
+ Util.toBytesFromString("bb1d6929e95937287fa37d129b756746");
+ private static Boolean valid;
+ private final IBlockCipher cipher;
+ private final String name;
+ private IMode mode;
+ private int blockSize;
+ private int outputSize;
+ private byte[] Lu, Lu2;
+ private byte[] M;
+ private byte[] Y;
+ private boolean init;
+ private int index;
+
+ public OMAC(IBlockCipher cipher)
+ {
+ this.cipher = cipher;
+ this.name = "OMAC-" + cipher.name();
+ }
+
+ public Object clone()
+ {
+ return new OMAC(cipher);
+ }
+
+ public String name()
+ {
+ return name;
+ }
+
+ public int macSize()
+ {
+ return outputSize;
+ }
+
+ public void init(Map attrib) throws InvalidKeyException
+ {
+ HashMap attrib2 = new HashMap();
+ attrib2.put(IBlockCipher.KEY_MATERIAL, attrib.get(MAC_KEY_MATERIAL));
+ cipher.reset();
+ cipher.init(attrib2);
+ blockSize = cipher.currentBlockSize();
+ Integer os = (Integer) attrib.get(TRUNCATED_SIZE);
+ if (os != null)
+ {
+ outputSize = os.intValue();
+ if (outputSize < 0 || outputSize > blockSize)
+ throw new IllegalArgumentException("truncated size out of range");
+ }
+ else
+ outputSize = blockSize;
+
+ byte[] L = new byte[blockSize];
+ cipher.encryptBlock(L, 0, L, 0);
+ if (Configuration.DEBUG)
+ log.fine("L = " + Util.toString(L).toLowerCase());
+ if (Lu != null)
+ {
+ Arrays.fill(Lu, (byte) 0);
+ if (Lu.length != blockSize)
+ Lu = new byte[blockSize];
+ }
+ else
+ Lu = new byte[blockSize];
+ if (Lu2 != null)
+ {
+ Arrays.fill(Lu2, (byte) 0);
+ if (Lu2.length != blockSize)
+ Lu2 = new byte[blockSize];
+ }
+ else
+ Lu2 = new byte[blockSize];
+
+ boolean msb = (L[0] & 0x80) != 0;
+ for (int i = 0; i < blockSize; i++)
+ {
+ Lu[i] = (byte)(L[i] << 1 & 0xFF);
+ if (i + 1 < blockSize)
+ Lu[i] |= (byte)((L[i + 1] & 0x80) >> 7);
+ }
+ if (msb)
+ {
+ if (blockSize == 16)
+ Lu[Lu.length - 1] ^= C1;
+ else if (blockSize == 8)
+ Lu[Lu.length - 1] ^= C2;
+ else
+ throw new IllegalArgumentException("unsupported cipher block size: "
+ + blockSize);
+ }
+ if (Configuration.DEBUG)
+ log.fine("Lu = " + Util.toString(Lu).toLowerCase());
+ msb = (Lu[0] & 0x80) != 0;
+ for (int i = 0; i < blockSize; i++)
+ {
+ Lu2[i] = (byte)(Lu[i] << 1 & 0xFF);
+ if (i + 1 < blockSize)
+ Lu2[i] |= (byte)((Lu[i + 1] & 0x80) >> 7);
+ }
+ if (msb)
+ {
+ if (blockSize == 16)
+ Lu2[Lu2.length - 1] ^= C1;
+ else
+ Lu2[Lu2.length - 1] ^= C2;
+ }
+ if (Configuration.DEBUG)
+ log.fine("Lu2 = " + Util.toString(Lu2).toLowerCase());
+ if (M != null)
+ {
+ Arrays.fill(M, (byte) 0);
+ if (M.length != blockSize)
+ M = new byte[blockSize];
+ }
+ else
+ M = new byte[blockSize];
+ if (Y != null)
+ {
+ Arrays.fill(Y, (byte) 0);
+ if (Y.length != blockSize)
+ Y = new byte[blockSize];
+ }
+ else
+ Y = new byte[blockSize];
+
+ index = 0;
+ init = true;
+ }
+
+ public void update(byte b)
+ {
+ if (! init)
+ throw new IllegalStateException("not initialized");
+ if (index == M.length)
+ {
+ process();
+ index = 0;
+ }
+ M[index++] = b;
+ }
+
+ public void update(byte[] buf, int off, int len)
+ {
+ if (! init)
+ throw new IllegalStateException("not initialized");
+ if (off < 0 || len < 0 || off + len > buf.length)
+ throw new IndexOutOfBoundsException("size=" + buf.length + "; off=" + off
+ + "; len=" + len);
+ for (int i = 0; i < len;)
+ {
+ if (index == blockSize)
+ {
+ process();
+ index = 0;
+ }
+ int count = Math.min(blockSize - index, len - i);
+ System.arraycopy(buf, off + i, M, index, count);
+ index += count;
+ i += count;
+ }
+ }
+
+ public byte[] digest()
+ {
+ byte[] b = new byte[outputSize];
+ digest(b, 0);
+ return b;
+ }
+
+ public void digest(byte[] out, int off)
+ {
+ if (! init)
+ throw new IllegalStateException("not initialized");
+ if (off < 0 || off + outputSize > out.length)
+ throw new IndexOutOfBoundsException("size=" + out.length + "; off=" + off
+ + "; len=" + outputSize);
+ byte[] T = new byte[blockSize];
+ byte[] L = Lu;
+ if (index < blockSize)
+ {
+ M[index++] = (byte) 0x80;
+ while (index < blockSize)
+ M[index++] = 0;
+ L = Lu2;
+ }
+ for (int i = 0; i < blockSize; i++)
+ T[i] = (byte)(M[i] ^ Y[i] ^ L[i]);
+ cipher.encryptBlock(T, 0, T, 0);
+ System.arraycopy(T, 0, out, off, outputSize);
+ reset();
+ }
+
+ public void reset()
+ {
+ index = 0;
+ if (Y != null)
+ Arrays.fill(Y, (byte) 0);
+ if (M != null)
+ Arrays.fill(M, (byte) 0);
+ }
+
+ public boolean selfTest()
+ {
+ OMAC mac = new OMAC(CipherFactory.getInstance(Registry.AES_CIPHER));
+ mac.reset();
+ Map attr = new HashMap();
+ attr.put(MAC_KEY_MATERIAL, KEY0);
+ byte[] digest = null;
+ try
+ {
+ mac.init(attr);
+ digest = mac.digest();
+ }
+ catch (Exception x)
+ {
+ return false;
+ }
+ if (digest == null)
+ return false;
+ return Arrays.equals(DIGEST0, digest);
+ }
+
+ private void process()
+ {
+ for (int i = 0; i < blockSize; i++)
+ M[i] = (byte)(M[i] ^ Y[i]);
+ cipher.encryptBlock(M, 0, Y, 0);
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/TMMH16.java b/libjava/classpath/gnu/javax/crypto/mac/TMMH16.java
new file mode 100644
index 000000000..3427317ab
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/TMMH16.java
@@ -0,0 +1,339 @@
+/* TMMH16.java --
+ Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.Registry;
+import gnu.java.security.prng.IRandom;
+import gnu.java.security.prng.LimitReachedException;
+
+import java.security.InvalidKeyException;
+import java.util.Map;
+
+/**
+ * <i>TMMH</i> is a <i>universal</i> hash function suitable for message
+ * authentication in the Wegman-Carter paradigm, as in the Stream Cipher
+ * Security Transform. It is simple, quick, and especially appropriate for
+ * Digital Signal Processors and other processors with a fast multiply
+ * operation, though a straightforward implementation requires storage equal in
+ * length to the largest message to be hashed.
+ * <p>
+ * <i>TMMH</i> is a simple hash function which maps a key and a message to a
+ * hash value. There are two versions of TMMH: TMMH/16 and TMMH/32. <i>TMMH</i>
+ * can be used as a message authentication code, as described in Section 5 (see
+ * References).
+ * <p>
+ * The key, message, and hash value are all octet strings, and the lengths of
+ * these quantities are denoted as <code>KEY_LENGTH</code>,
+ * <code>MESSAGE_LENGTH</code>, and <code>TAG_LENGTH</code>, respectively.
+ * The values of <code>KEY_LENGTH</code> and <code>TAG_LENGTH</code>
+ * <bold>MUST</bold> be fixed for any particular fixed value of the key, and
+ * must obey the alignment restrictions described below.
+ * <p>
+ * The parameter <code>MAX_HASH_LENGTH</code>, which denotes the maximum
+ * value which <code>MESSAGE_LENGTH</code> may take, is equal to
+ * <code>KEY_LENGTH - TAG_LENGTH</code>.
+ * <p>
+ * References:
+ * <ol>
+ * <li><a
+ * href="http://www.ietf.org/internet-drafts/draft-mcgrew-saag-tmmh-01.txt"> The
+ * Truncated Multi-Modular Hash Function (TMMH)</a>, David A. McGrew.</li>
+ * </ol>
+ */
+public class TMMH16
+ extends BaseMac
+ implements Cloneable
+{
+ public static final String TAG_LENGTH = "gnu.crypto.mac.tmmh.tag.length";
+ public static final String KEYSTREAM = "gnu.crypto.mac.tmmh.keystream";
+ public static final String PREFIX = "gnu.crypto.mac.tmmh.prefix";
+ private static final int P = (1 << 16) + 1; // the TMMH/16 prime
+ /** caches the result of the correctness test, once executed. */
+ private static Boolean valid;
+ private int tagWords = 0; // the tagLength expressed in words
+ private IRandom keystream = null; // the keystream generator
+ private byte[] prefix; // mask to use when operating as an authentication f.
+ private long keyWords; // key words counter
+ private long msgLength; // in bytes
+ private long msgWords; // should be = msgLength * WORD_LENGTH
+ private int[] context; // the tmmh running context; length == TAG_WORDS
+ private int[] K0; // the first TAG_WORDS words of the keystream
+ private int[] Ki; // the sliding TAG_WORDS words of the keystream
+ private int Mi; // current message word being constructed
+
+ /** Trivial 0-arguments constructor. */
+ public TMMH16()
+ {
+ super(Registry.TMMH16);
+ }
+
+ public int macSize()
+ {
+ return tagWords * 2;
+ }
+
+ public void init(Map attributes) throws InvalidKeyException,
+ IllegalStateException
+ {
+ int wantTagLength = 0;
+ Integer tagLength = (Integer) attributes.get(TAG_LENGTH); // get tag length
+ if (tagLength == null)
+ {
+ if (tagWords == 0) // was never set
+ throw new IllegalArgumentException(TAG_LENGTH);
+ // else re-use
+ }
+ else // check if positive and is divisible by WORD_LENGTH
+ {
+ wantTagLength = tagLength.intValue();
+ if (wantTagLength < 2 || (wantTagLength % 2 != 0))
+ throw new IllegalArgumentException(TAG_LENGTH);
+ else if (wantTagLength > (512 / 8)) // 512-bits is our maximum
+ throw new IllegalArgumentException(TAG_LENGTH);
+
+ tagWords = wantTagLength / 2; // init local vars
+ K0 = new int[tagWords];
+ Ki = new int[tagWords];
+ context = new int[tagWords];
+ }
+
+ prefix = (byte[]) attributes.get(PREFIX);
+ if (prefix == null) // default to all-zeroes
+ prefix = new byte[tagWords * 2];
+ else // ensure it's as long as it should
+ {
+ if (prefix.length != tagWords * 2)
+ throw new IllegalArgumentException(PREFIX);
+ }
+
+ IRandom prng = (IRandom) attributes.get(KEYSTREAM); // get keystream
+ if (prng == null)
+ {
+ if (keystream == null)
+ throw new IllegalArgumentException(KEYSTREAM);
+ // else reuse
+ }
+ else
+ keystream = prng;
+
+ reset(); // reset context variables
+ for (int i = 0; i < tagWords; i++) // init starting key words
+ Ki[i] = K0[i] = getNextKeyWord(keystream);
+ }
+
+ // The words of the key are denoted as K[1], K[2], ..., K[KEY_WORDS], and the
+ // words of the message (after zero padding, if needed) are denoted as M[1],
+ // M[2], ..., M[MSG_WORDS], where MSG_WORDS is the smallest number such that
+ // 2 * MSG_WORDS is at least MESSAGE_LENGTH, and KEY_WORDS is KEY_LENGTH / 2.
+ //
+ // If MESSAGE_LENGTH is greater than MAX_HASH_LENGTH, then the value of
+ // TMMH/16 is undefined. Implementations MUST indicate an error if asked to
+ // hash a message with such a length. Otherwise, the hash value is defined
+ // to be the length TAG_WORDS sequence of words in which the j-th word in the
+ // sequence is defined as
+ //
+ // [ [ K[j] * MESSAGE_LENGTH +32 K[j+1] * M[1] +32 K[j+2] * M[2]
+ // +32 ... K[j+MSG_WORDS] * M[MSG_WORDS] ] modulo p ] modulo 2^16
+ //
+ // where j ranges from 1 to TAG_WORDS.
+ public void update(byte b)
+ {
+ this.update(b, keystream);
+ }
+
+ public void update(byte[] b, int offset, int len)
+ {
+ for (int i = 0; i < len; i++)
+ this.update(b[offset + i], keystream);
+ }
+
+ // For TMMH/16, KEY_LENGTH and TAG_LENGTH MUST be a multiple of two. The key,
+ // message, and hash value are treated as a sequence of unsigned sixteen bit
+ // integers in network byte order. (In this section, we call such an integer
+ // a word.) If MESSAGE_LENGTH is odd, then a zero byte is appended to the
+ // message to align it on a word boundary, though this process does not
+ // change the value of MESSAGE_LENGTH.
+ //
+ // ... Otherwise, the hash value is defined to be the length TAG_WORDS
+ // sequence of words in which the j-th word in the sequence is defined as
+ //
+ // [ [ K[j] * MESSAGE_LENGTH +32 K[j+1] * M[1] +32 K[j+2] * M[2]
+ // +32 ... K[j+MSG_WORDS] * M[MSG_WORDS] ] modulo p ] modulo 2^16
+ //
+ // where j ranges from 1 to TAG_WORDS.
+ //
+ // Here, TAG_WORDS is equal to TAG_LENGTH / 2, and p is equal to 2^16 + 1.
+ // The symbol * denotes multiplication and the symbol +32 denotes addition
+ // modulo 2^32.
+ public byte[] digest()
+ {
+ return this.digest(keystream);
+ }
+
+ public void reset()
+ {
+ msgLength = msgWords = keyWords = 0L;
+ Mi = 0;
+ for (int i = 0; i < tagWords; i++)
+ context[i] = 0;
+ }
+
+ public boolean selfTest()
+ {
+ if (valid == null)
+ {
+ // TODO: compute and test equality with one known vector
+ valid = Boolean.TRUE;
+ }
+ return valid.booleanValue();
+ }
+
+ public Object clone() throws CloneNotSupportedException
+ {
+ TMMH16 result = (TMMH16) super.clone();
+ if (this.keystream != null)
+ result.keystream = (IRandom) this.keystream.clone();
+ if (this.prefix != null)
+ result.prefix = (byte[]) this.prefix.clone();
+ if (this.context != null)
+ result.context = (int[]) this.context.clone();
+ if (this.K0 != null)
+ result.K0 = (int[]) this.K0.clone();
+ if (this.Ki != null)
+ result.Ki = (int[]) this.Ki.clone();
+ return result;
+ }
+
+ /**
+ * Similar to the same method with one argument, but uses the designated
+ * random number generator to compute needed keying material.
+ *
+ * @param b the byte to process.
+ * @param prng the source of randomness to use.
+ */
+ public void update(byte b, IRandom prng)
+ {
+ Mi <<= 8; // update message buffer
+ Mi |= b & 0xFF;
+ msgLength++; // update message length (bytes)
+ if (msgLength % 2 == 0) // got a full word
+ {
+ msgWords++; // update message words counter
+ System.arraycopy(Ki, 1, Ki, 0, tagWords - 1); // 1. shift Ki up by 1
+ Ki[tagWords - 1] = getNextKeyWord(prng); // 2. fill last box of Ki
+ long t; // temp var to allow working in modulo 2^32
+ for (int i = 0; i < tagWords; i++) // 3. update context
+ {
+ t = context[i] & 0xFFFFFFFFL;
+ t += Ki[i] * Mi;
+ context[i] = (int) t;
+ }
+ Mi = 0; // reset message buffer
+ }
+ }
+
+ /**
+ * Similar to the same method with three arguments, but uses the designated
+ * random number generator to compute needed keying material.
+ *
+ * @param b the byte array to process.
+ * @param offset the starting offset in <code>b</code> to start considering
+ * the bytes to process.
+ * @param len the number of bytes in <code>b</code> starting from
+ * <code>offset</code> to process.
+ * @param prng the source of randomness to use.
+ */
+ public void update(byte[] b, int offset, int len, IRandom prng)
+ {
+ for (int i = 0; i < len; i++)
+ this.update(b[offset + i], prng);
+ }
+
+ /**
+ * Similar to the same method with no arguments, but uses the designated
+ * random number generator to compute needed keying material.
+ *
+ * @param prng the source of randomness to use.
+ * @return the final result of the algorithm.
+ */
+ public byte[] digest(IRandom prng)
+ {
+ doFinalRound(prng);
+ byte[] result = new byte[tagWords * 2];
+ for (int i = 0, j = 0; i < tagWords; i++)
+ {
+ result[j] = (byte)((context[i] >>> 8) ^ prefix[j]);
+ j++;
+ result[j] = (byte)(context[i] ^ prefix[j]);
+ j++;
+ }
+ reset();
+ return result;
+ }
+
+ private int getNextKeyWord(IRandom prng)
+ {
+ int result = 0;
+ try
+ {
+ result = (prng.nextByte() & 0xFF) << 8 | (prng.nextByte() & 0xFF);
+ }
+ catch (LimitReachedException x)
+ {
+ throw new RuntimeException(String.valueOf(x));
+ }
+ keyWords++; // update key words counter
+ return result;
+ }
+
+ private void doFinalRound(IRandom prng)
+ {
+ long limit = msgLength; // formula works on real message length
+ while (msgLength % 2 != 0)
+ update((byte) 0x00, prng);
+ long t;
+ for (int i = 0; i < tagWords; i++)
+ {
+ t = context[i] & 0xFFFFFFFFL;
+ t += K0[i] * limit;
+ t %= P;
+ context[i] = (int) t;
+ }
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/UHash32.java b/libjava/classpath/gnu/javax/crypto/mac/UHash32.java
new file mode 100644
index 000000000..53513eda9
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/UHash32.java
@@ -0,0 +1,758 @@
+/* UHash32.java --
+ Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.prng.IRandom;
+import gnu.java.security.prng.LimitReachedException;
+import gnu.javax.crypto.cipher.IBlockCipher;
+import gnu.javax.crypto.prng.UMacGenerator;
+
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <i>UHASH</i> is a keyed hash function, which takes as input a string of
+ * arbitrary length, and produces as output a string of fixed length (such as 8
+ * bytes). The actual output length depends on the parameter UMAC-OUTPUT-LEN.
+ * <p>
+ * <i>UHASH</i> has been shown to be <i>epsilon-ASU</i> ("Almost Strongly
+ * Universal"), where epsilon is a small (parameter-dependent) real number.
+ * Informally, saying that a keyed hash function is <i>epsilon-ASU</i> means
+ * that for any two distinct fixed input strings, the two outputs of the hash
+ * function with a random key "look almost like a pair of random strings". The
+ * number epsilon measures how non-random the output strings may be.
+ * <p>
+ * <i>UHASH</i> has been designed to be fast by exploiting several
+ * architectural features of modern commodity processors. It was specifically
+ * designed for use in <i>UMAC</i>. But <i>UHASH</i> is useful beyond that
+ * domain, and can be easily adopted for other purposes.
+ * <p>
+ * <i>UHASH</i> does its work in three layers. First, a hash function called
+ * <code>NH</code> is used to compress input messages into strings which are
+ * typically many times smaller than the input message. Second, the compressed
+ * message is hashed with an optimized <i>polynomial hash function</i> into a
+ * fixed-length 16-byte string. Finally, the 16-byte string is hashed using an
+ * <i>inner-product hash</i> into a string of length WORD-LEN bytes. These
+ * three layers are repeated (with a modified key) until the outputs total
+ * UMAC-OUTPUT-LEN bytes.
+ * <p>
+ * References:
+ * <ol>
+ * <li><a href="http://www.ietf.org/internet-drafts/draft-krovetz-umac-01.txt">
+ * UMAC</a>: Message Authentication Code using Universal Hashing.<br>
+ * T. Krovetz, J. Black, S. Halevi, A. Hevia, H. Krawczyk, and P. Rogaway.</li>
+ * </ol>
+ */
+public class UHash32
+ extends BaseMac
+{
+ // UMAC prime values
+ private static final BigInteger PRIME_19 = BigInteger.valueOf(0x7FFFFL);
+ private static final BigInteger PRIME_32 = BigInteger.valueOf(0xFFFFFFFBL);
+ private static final BigInteger PRIME_36 = BigInteger.valueOf(0xFFFFFFFFBL);
+ private static final BigInteger PRIME_64 = new BigInteger(1, new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC5 });
+ private static final BigInteger PRIME_128 = new BigInteger(1, new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x61 });
+ static final BigInteger TWO = BigInteger.valueOf(2L);
+ static final long BOUNDARY = TWO.shiftLeft(17).longValue();
+ // 2**64 - 2**32
+ static final BigInteger LOWER_RANGE = TWO.pow(64).subtract(TWO.pow(32));
+ // 2**128 - 2**96
+ static final BigInteger UPPER_RANGE = TWO.pow(128).subtract(TWO.pow(96));
+ static final byte[] ALL_ZEROES = new byte[32];
+ int streams;
+ L1Hash32[] l1hash;
+
+ /** Trivial 0-arguments constructor. */
+ public UHash32()
+ {
+ super("uhash32");
+ }
+
+ /**
+ * Private constructor for cloning purposes.
+ *
+ * @param that the instance to clone.
+ */
+ private UHash32(UHash32 that)
+ {
+ this();
+
+ this.streams = that.streams;
+ if (that.l1hash != null)
+ {
+ this.l1hash = new L1Hash32[that.streams];
+ for (int i = 0; i < that.streams; i++)
+ if (that.l1hash[i] != null)
+ this.l1hash[i] = (L1Hash32) that.l1hash[i].clone();
+ }
+ }
+
+ /**
+ * The prime numbers used in UMAC are:
+ * <pre>
+ * +-----+--------------------+---------------------------------------+
+ * | x | prime(x) [Decimal] | prime(x) [Hexadecimal] |
+ * +-----+--------------------+---------------------------------------+
+ * | 19 | 2^19 - 1 | 0x0007FFFF |
+ * | 32 | 2^32 - 5 | 0xFFFFFFFB |
+ * | 36 | 2^36 - 5 | 0x0000000F FFFFFFFB |
+ * | 64 | 2^64 - 59 | 0xFFFFFFFF FFFFFFC5 |
+ * | 128 | 2^128 - 159 | 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFF61 |
+ * +-----+--------------------+---------------------------------------+
+ *</pre>
+ *
+ * @param n a number of bits.
+ * @return the largest prime number less than 2**n.
+ */
+ static final BigInteger prime(int n)
+ {
+ switch (n)
+ {
+ case 19:
+ return PRIME_19;
+ case 32:
+ return PRIME_32;
+ case 36:
+ return PRIME_36;
+ case 64:
+ return PRIME_64;
+ case 128:
+ return PRIME_128;
+ default:
+ throw new IllegalArgumentException("Undefined prime("
+ + String.valueOf(n) + ")");
+ }
+ }
+
+ public Object clone()
+ {
+ return new UHash32(this);
+ }
+
+ public int macSize()
+ {
+ return UMac32.OUTPUT_LEN;
+ }
+
+ public void init(Map attributes) throws InvalidKeyException,
+ IllegalStateException
+ {
+ byte[] K = (byte[]) attributes.get(MAC_KEY_MATERIAL);
+ if (K == null)
+ throw new InvalidKeyException("Null Key");
+ if (K.length != UMac32.KEY_LEN)
+ throw new InvalidKeyException("Invalid Key length: "
+ + String.valueOf(K.length));
+ // Calculate iterations needed to make UMAC-OUTPUT-LEN bytes
+ streams = (UMac32.OUTPUT_LEN + 3) / 4;
+ // Define total key needed for all iterations using UMacGenerator.
+ // L1Key and L3Key1 both reuse most key between iterations.
+ IRandom kdf1 = new UMacGenerator();
+ IRandom kdf2 = new UMacGenerator();
+ IRandom kdf3 = new UMacGenerator();
+ IRandom kdf4 = new UMacGenerator();
+ Map map = new HashMap();
+ map.put(IBlockCipher.KEY_MATERIAL, K);
+ map.put(UMacGenerator.INDEX, Integer.valueOf(0));
+ kdf1.init(map);
+ map.put(UMacGenerator.INDEX, Integer.valueOf(1));
+ kdf2.init(map);
+ map.put(UMacGenerator.INDEX, Integer.valueOf(2));
+ kdf3.init(map);
+ map.put(UMacGenerator.INDEX, Integer.valueOf(3));
+ kdf4.init(map);
+ // need to generate all bytes for use later in a Toepliz construction
+ byte[] L1Key = new byte[UMac32.L1_KEY_LEN + (streams - 1) * 16];
+ try
+ {
+ kdf1.nextBytes(L1Key, 0, L1Key.length);
+ }
+ catch (LimitReachedException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException("KDF for L1Key reached limit");
+ }
+
+ l1hash = new L1Hash32[streams];
+ for (int i = 0; i < streams; i++)
+ {
+ byte[] k1 = new byte[UMac32.L1_KEY_LEN];
+ System.arraycopy(L1Key, i * 16, k1, 0, UMac32.L1_KEY_LEN);
+ byte[] k2 = new byte[24];
+ try
+ {
+ kdf2.nextBytes(k2, 0, 24);
+ }
+ catch (LimitReachedException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException("KDF for L2Key reached limit");
+ }
+ byte[] k31 = new byte[64];
+ try
+ {
+ kdf3.nextBytes(k31, 0, 64);
+ }
+ catch (LimitReachedException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException("KDF for L3Key1 reached limit");
+ }
+ byte[] k32 = new byte[4];
+ try
+ {
+ kdf4.nextBytes(k32, 0, 4);
+ }
+ catch (LimitReachedException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException("KDF for L3Key2 reached limit");
+ }
+ L1Hash32 mac = new L1Hash32();
+ mac.init(k1, k2, k31, k32);
+ l1hash[i] = mac;
+ }
+ }
+
+ public void update(byte b)
+ {
+ for (int i = 0; i < streams; i++)
+ l1hash[i].update(b);
+ }
+
+ public void update(byte[] b, int offset, int len)
+ {
+ for (int i = 0; i < len; i++)
+ this.update(b[offset + i]);
+ }
+
+ public byte[] digest()
+ {
+ byte[] result = new byte[UMac32.OUTPUT_LEN];
+ for (int i = 0; i < streams; i++)
+ {
+ byte[] partialResult = l1hash[i].digest();
+ System.arraycopy(partialResult, 0, result, 4 * i, 4);
+ }
+ reset();
+ return result;
+ }
+
+ public void reset()
+ {
+ for (int i = 0; i < streams; i++)
+ l1hash[i].reset();
+ }
+
+ public boolean selfTest()
+ {
+ return true;
+ }
+
+ /**
+ * First hash stage of the UHash32 algorithm.
+ */
+ class L1Hash32
+ implements Cloneable
+ {
+ private int[] key; // key material as an array of 32-bit ints
+ private byte[] buffer; // work buffer L1_KEY_LEN long
+ private int count; // meaningful bytes in buffer
+ private ByteArrayOutputStream Y;
+ private long totalCount;
+ private L2Hash32 l2hash;
+ private L3Hash32 l3hash;
+
+ /** Trivial 0-arguments constructor. */
+ L1Hash32()
+ {
+ super();
+
+ key = new int[UMac32.L1_KEY_LEN / 4];
+ buffer = new byte[UMac32.L1_KEY_LEN];
+ count = 0;
+ Y = new ByteArrayOutputStream();
+ totalCount = 0L;
+ }
+
+ /**
+ * Private constructor for cloning purposes.
+ *
+ * @param that the instance to clone.
+ */
+ private L1Hash32(L1Hash32 that)
+ {
+ this();
+
+ System.arraycopy(that.key, 0, this.key, 0, that.key.length);
+ System.arraycopy(that.buffer, 0, this.buffer, 0, that.count);
+ this.count = that.count;
+ byte[] otherY = that.Y.toByteArray();
+ this.Y.write(otherY, 0, otherY.length);
+ this.totalCount = that.totalCount;
+ if (that.l2hash != null)
+ this.l2hash = (L2Hash32) that.l2hash.clone();
+ if (that.l3hash != null)
+ this.l3hash = (L3Hash32) that.l3hash.clone();
+ }
+
+ public Object clone()
+ {
+ return new L1Hash32(this);
+ }
+
+ public void init(byte[] k1, byte[] k2, byte[] k31, byte[] k32)
+ {
+ for (int i = 0, j = 0; i < (UMac32.L1_KEY_LEN / 4); i++)
+ key[i] = k1[j++] << 24
+ | (k1[j++] & 0xFF) << 16
+ | (k1[j++] & 0xFF) << 8
+ | (k1[j++] & 0xFF);
+ l2hash = new L2Hash32(k2);
+ l3hash = new L3Hash32(k31, k32);
+ }
+
+ public void update(byte b)
+ {
+ // Break M into L1_KEY_LEN byte chunks (final chunk may be shorter)
+
+ // Let M_1, M_2, ..., M_t be strings so that M = M_1 || M_2 || .. ||
+ // M_t, and length(M_i) = L1_KEY_LEN for all 0 < i < t.
+
+ // For each chunk, except the last: endian-adjust, NH hash
+ // and add bit-length. Use results to build Y.
+ buffer[count] = b;
+ count++;
+ totalCount++;
+ if (count >= UMac32.L1_KEY_LEN)
+ {
+ byte[] y = nh32(UMac32.L1_KEY_LEN);
+ Y.write(y, 0, 8);
+
+ count = 0;
+
+ // For each iteration, extract key and three-layer hash.
+ // If length(M) <= L1_KEY_LEN, then skip L2-HASH.
+ if (Y.size() == 16) // we already hashed twice L1_KEY_LEN
+ {
+ byte[] A = Y.toByteArray();
+ Y.reset();
+ l2hash.update(A, 0, 16);
+ }
+ }
+ }
+
+ public byte[] digest()
+ {
+ // For the last chunk: pad to 32-byte boundary, endian-adjust,
+ // NH hash and add bit-length. Concatenate the result to Y.
+ if (count != 0)
+ {
+ if (count % 32 != 0)
+ {
+ int limit = 32 * ((count + 31) / 32);
+ System.arraycopy(ALL_ZEROES, 0, buffer, count, limit - count);
+ count += limit - count;
+ }
+ byte[] y = nh32(count);
+ Y.write(y, 0, 8);
+ }
+ byte[] A = Y.toByteArray();
+ Y.reset();
+ byte[] B;
+ if (totalCount <= UMac32.L1_KEY_LEN)
+ {
+ // we might have 'update'd the bytes already. check
+ if (A.length == 0) // we did
+ B = l2hash.digest();
+ else // did not
+ {
+ B = new byte[16];
+ System.arraycopy(A, 0, B, 8, 8);
+ }
+ }
+ else
+ {
+ if (A.length != 0)
+ l2hash.update(A, 0, A.length);
+ B = l2hash.digest();
+ }
+ byte[] result = l3hash.digest(B);
+ reset();
+ return result;
+ }
+
+ public void reset()
+ {
+ count = 0;
+ Y.reset();
+ totalCount = 0L;
+ if (l2hash != null)
+ l2hash.reset();
+ }
+
+ /**
+ * 5.1 NH-32: NH hashing with a 32-bit word size.
+ *
+ * @param len count of bytes, divisible by 32, in buffer to process
+ * @return Y, string of length 8 bytes.
+ */
+ private byte[] nh32(int len)
+ {
+ // Break M and K into 4-byte chunks
+ int t = len / 4;
+ // Let M_1, M_2, ..., M_t be 4-byte strings
+ // so that M = M_1 || M_2 || .. || M_t.
+ // Let K_1, K_2, ..., K_t be 4-byte strings
+ // so that K_1 || K_2 || .. || K_t is a prefix of K.
+ int[] m = new int[t];
+ int i;
+ int j = 0;
+ for (i = 0, j = 0; i < t; i++)
+ m[i] = buffer[j++] << 24
+ | (buffer[j++] & 0xFF) << 16
+ | (buffer[j++] & 0xFF) << 8
+ | (buffer[j++] & 0xFF);
+ // Perform NH hash on the chunks, pairing words for multiplication
+ // which are 4 apart to accommodate vector-parallelism.
+ long result = len * 8L;
+ for (i = 0; i < t; i += 8)
+ {
+ result += ((m[i + 0] + key[i + 0]) & 0xFFFFFFFFL)
+ * ((m[i + 4] + key[i + 4]) & 0xFFFFFFFFL);
+ result += ((m[i + 1] + key[i + 1]) & 0xFFFFFFFFL)
+ * ((m[i + 5] + key[i + 5]) & 0xFFFFFFFFL);
+ result += ((m[i + 2] + key[i + 2]) & 0xFFFFFFFFL)
+ * ((m[i + 6] + key[i + 6]) & 0xFFFFFFFFL);
+ result += ((m[i + 3] + key[i + 3]) & 0xFFFFFFFFL)
+ * ((m[i + 7] + key[i + 7]) & 0xFFFFFFFFL);
+ }
+ return new byte[] {
+ (byte)(result >>> 56), (byte)(result >>> 48),
+ (byte)(result >>> 40), (byte)(result >>> 32),
+ (byte)(result >>> 24), (byte)(result >>> 16),
+ (byte)(result >>> 8), (byte) result };
+ }
+ }
+
+ /**
+ * Second hash stage of the UHash32 algorithm.
+ * <p>
+ * 5.4 L2-HASH-32: Second-layer hash.
+ * <ul>
+ * <li>Input:<br>
+ * K string of length 24 bytes.<br>
+ * M string of length less than 2^64 bytes.</li>
+ * <li>Returns:<br>
+ * Y, string of length 16 bytes.</li>
+ * </ul>
+ */
+ class L2Hash32
+ implements Cloneable
+ {
+ private BigInteger k64, k128;
+ private BigInteger y;
+ private boolean highBound;
+ private long bytesSoFar;
+ private ByteArrayOutputStream buffer;
+
+ L2Hash32(byte[] K)
+ {
+ super();
+
+ if (K.length != 24)
+ throw new ExceptionInInitializerError("K length is not 24");
+ // Extract keys and restrict to special key-sets
+ // Mask64 = uint2str(0x01FFFFFF01FFFFFF, 8);
+ // Mask128 = uint2str(0x01FFFFFF01FFFFFF01FFFFFF01FFFFFF, 16);
+ // k64 = str2uint(K[1..8] and Mask64);
+ // k128 = str2uint(K[9..24] and Mask128);
+ int i = 0;
+ k64 = new BigInteger(1, new byte[] {
+ (byte)(K[i++] & 0x01), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0xFF), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0x01), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0xFF), (byte)(K[i++] & 0xFF) });
+ k128 = new BigInteger(1, new byte[] {
+ (byte)(K[i++] & 0x01), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0xFF), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0x01), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0xFF), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0x01), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0xFF), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0x01), (byte)(K[i++] & 0xFF),
+ (byte)(K[i++] & 0xFF), (byte)(K[i++] & 0xFF) });
+ y = BigInteger.ONE;
+ highBound = false;
+ bytesSoFar = 0L;
+ }
+
+ private L2Hash32(L2Hash32 that)
+ {
+ super();
+
+ this.k64 = that.k64;
+ this.k128 = that.k128;
+ this.y = that.y;
+ this.highBound = that.highBound;
+ this.bytesSoFar = that.bytesSoFar;
+ if (that.buffer != null)
+ {
+ byte[] thatbuffer = that.buffer.toByteArray();
+ this.buffer = new ByteArrayOutputStream();
+ this.buffer.write(thatbuffer, 0, thatbuffer.length);
+ }
+ }
+
+ public Object clone()
+ {
+ return new L2Hash32(this);
+ }
+
+ // this is called with either 8-bytes or 16-bytes
+ void update(byte[] b, int offset, int len)
+ {
+ if (len == 0)
+ return;
+
+ if (! highBound) // do the first (only?) 8-bytes
+ {
+ poly(64, LOWER_RANGE, k64, b, offset, 8);
+ bytesSoFar += 8L;
+ highBound = (bytesSoFar > BOUNDARY);
+ if (highBound) // if we just crossed the limit then process y
+ {
+ poly(128, UPPER_RANGE, k128, yTo16bytes(), 0, 16);
+ buffer = new ByteArrayOutputStream();
+ }
+ // do the rest if any
+ update(b, offset + 8, len - 8);
+ }
+ else
+ { // we're already beyond the 2**17 bytes size limit
+ // process in chuncks of 16
+ buffer.write(b, offset, len);
+ if (buffer.size() > 16)
+ {
+ byte[] bb = buffer.toByteArray();
+ poly(128, UPPER_RANGE, k128, bb, 0, 16);
+ if (bb.length > 16)
+ buffer.write(bb, 16, bb.length - 16);
+ }
+ }
+ }
+
+ byte[] digest()
+ {
+ // If M no more than 2^17 bytes, hash under 64-bit prime,
+ // otherwise, hash first 2^17 bytes under 64-bit prime and
+ // remainder under 128-bit prime.
+ if (! highBound) // y is up-to-date
+ {
+ // do nothing
+ }
+ else // we may have some bytes in buffer
+ {
+ byte[] bb = buffer.toByteArray();
+ byte[] lastBlock = new byte[16];
+ System.arraycopy(bb, 0, lastBlock, 0, bb.length);
+ lastBlock[bb.length] = (byte) 0x80;
+ poly(128, UPPER_RANGE, k128, lastBlock, 0, 16);
+ }
+ byte[] result = yTo16bytes();
+ reset();
+ return result;
+ }
+
+ void reset()
+ {
+ y = BigInteger.ONE;
+ highBound = false;
+ bytesSoFar = 0L;
+ if (buffer != null)
+ buffer.reset();
+ }
+
+ private byte[] yTo16bytes()
+ {
+ byte[] yy = y.toByteArray();
+ byte[] result = new byte[16];
+ if (yy.length > 16)
+ System.arraycopy(yy, yy.length - 16, result, 0, 16);
+ else
+ System.arraycopy(yy, 0, result, 16 - yy.length, yy.length);
+
+ return result;
+ }
+
+ /**
+ * 5.3 POLY: Polynomial hash Function Name: POLY
+ *
+ * @param wordbits positive integer divisible by 8: called with 64 or 128.
+ * @param maxwordrange positive integer less than 2**wordbits.
+ * @param k integer in the range 0 .. prime(wordbits) - 1.
+ * @param M string with length divisible by (wordbits / 8) bytes. return y,
+ * integer in the range 0 .. prime(wordbits) - 1.
+ */
+ private void poly(int wordbits, BigInteger maxwordrange, BigInteger k,
+ byte[] M, int off, int len)
+ {
+ byte[] mag = new byte[len];
+ System.arraycopy(M, off, mag, 0, len);
+ // Define constants used for fixing out-of-range words
+ BigInteger p = prime(wordbits);
+ BigInteger offset = TWO.pow(wordbits).subtract(p); // 2^wordbits - p;
+ BigInteger marker = p.subtract(BigInteger.ONE);
+ // Break M into chunks of length wordbytes bytes
+ // long n = M.length / wordbytes;
+ // Let M_1, M_2, ..., M_n be strings of length wordbytes bytes
+ // so that M = M_1 || M_2 || .. || M_n
+
+ // For each input word, compare it with maxwordrange. If larger
+ // then hash the words 'marker' and (m - offset), both in range.
+ // for (int i = 0; i < n; i++) {
+ BigInteger m = new BigInteger(1, mag);
+ if (m.compareTo(maxwordrange) >= 0) // m >= maxwordrange
+ {
+ y = y.multiply(k).add(marker).mod(p); // (k * y + marker) % p;
+ y = y.multiply(k).add(m.subtract(offset)).mod(p); // (k * y + (m - offset)) % p;
+ }
+ else
+ y = y.multiply(k).add(m).mod(p); // (k * y + m) % p;
+ }
+ }
+
+ /**
+ * Third hash stage of the UHash32 algorithm.
+ * <ul>
+ * <li>Input:<br/>
+ * K1 string of length 64 bytes.<br/>
+ * K2 string of length 4 bytes.<br/>
+ * M string of length 16 bytes.</li>
+ * <li>Returns:<br/>
+ * Y, string of length 4 bytes.</li>
+ * </ul>
+ */
+ class L3Hash32
+ implements Cloneable
+ {
+ private static final long PRIME_36 = 0x0000000FFFFFFFFBL;
+ private int[] k = new int[9];
+
+ /**
+ * @param K1 string of length 64 bytes.
+ * @param K2 string of length 4 bytes.
+ */
+ L3Hash32(byte[] K1, byte[] K2)
+ {
+ super();
+
+ // pre-conditions
+ if (K1.length != 64)
+ throw new ExceptionInInitializerError("K1 length is not 64");
+ if (K2.length != 4)
+ throw new ExceptionInInitializerError("K2 length is not 4");
+ // Break K1 into 8 chunks and convert to integers
+ for (int i = 0, j = 0; i < 8; i++)
+ {
+ long kk = (K1[j++] & 0xFFL) << 56
+ | (K1[j++] & 0xFFL) << 48
+ | (K1[j++] & 0xFFL) << 40
+ | (K1[j++] & 0xFFL) << 32
+ | (K1[j++] & 0xFFL) << 24
+ | (K1[j++] & 0xFFL) << 16
+ | (K1[j++] & 0xFFL) << 8
+ | (K1[j++] & 0xFFL);
+ k[i] = (int)(kk % PRIME_36);
+ }
+ k[8] = K2[0] << 24
+ | (K2[1] & 0xFF) << 16
+ | (K2[2] & 0xFF) << 8
+ | (K2[3] & 0xFF);
+ }
+
+ private L3Hash32(int[] k)
+ {
+ super();
+
+ this.k = k;
+ }
+
+ public Object clone()
+ {
+ return new L3Hash32((int[]) k.clone());
+ }
+
+ /**
+ * @param M string of length 16 bytes.
+ * @return Y, string of length 4 bytes.
+ */
+ byte[] digest(byte[] M)
+ {
+ if (M.length != 16)
+ throw new IllegalArgumentException("M length is not 16");
+
+ long m, y = 0L;
+ for (int i = 0, j = 0; i < 8; i++)
+ {
+ // Break M into 8 chunks and convert to integers
+ m = (M[j++] & 0xFFL) << 8 | (M[j++] & 0xFFL);
+ // Inner-product hash, extract last 32 bits and affine-translate
+ // y = (m_1 * k_1 + ... + m_8 * k_8) mod prime(36);
+ // y = y mod 2^32;
+ y += (m * (k[i] & 0xFFFFFFFFL)) % PRIME_36;
+ }
+ int Y = ((int) y) ^ k[8];
+ return new byte[] {
+ (byte)(Y >>> 24),
+ (byte)(Y >>> 16),
+ (byte)(Y >>> 8),
+ (byte) Y };
+ }
+ }
+}
diff --git a/libjava/classpath/gnu/javax/crypto/mac/UMac32.java b/libjava/classpath/gnu/javax/crypto/mac/UMac32.java
new file mode 100644
index 000000000..6f53424ea
--- /dev/null
+++ b/libjava/classpath/gnu/javax/crypto/mac/UMac32.java
@@ -0,0 +1,418 @@
+/* UMac32.java --
+ Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
+
+This file is a part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+USA
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.crypto.mac;
+
+import gnu.java.security.Registry;
+import gnu.java.security.prng.IRandom;
+import gnu.java.security.prng.LimitReachedException;
+import gnu.java.security.util.Util;
+import gnu.javax.crypto.cipher.CipherFactory;
+import gnu.javax.crypto.cipher.IBlockCipher;
+import gnu.javax.crypto.prng.UMacGenerator;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The implementation of the <i>UMAC</i> (Universal Message Authentication
+ * Code).
+ * <p>
+ * The <i>UMAC</i> algorithms described are <i>parameterized</i>. This means
+ * that various low-level choices, like the endian convention and the underlying
+ * cryptographic primitive, have not been fixed. One must choose values for
+ * these parameters before the authentication tag generated by <i>UMAC</i> (for
+ * a given message, key, and nonce) becomes fully-defined. In this document we
+ * provide two collections of parameter settings, and have named the sets
+ * <i>UMAC16</i> and <i>UMAC32</i>. The parameter sets have been chosen based
+ * on experimentation and provide good performance on a wide variety of
+ * processors. <i>UMAC16</i> is designed to excel on processors which provide
+ * small-scale SIMD parallelism of the type found in Intel's MMX and Motorola's
+ * AltiVec instruction sets, while <i>UMAC32</i> is designed to do well on
+ * processors with good 32- and 64- bit support. <i>UMAC32</i> may take
+ * advantage of SIMD parallelism in future processors.
+ * <p>
+ * <i>UMAC</i> has been designed to allow implementations which accommodate
+ * <i>on-line</i> authentication. This means that pieces of the message may be
+ * presented to <i>UMAC</i> at different times (but in correct order) and an
+ * on-line implementation will be able to process the message correctly without
+ * the need to buffer more than a few dozen bytes of the message. For
+ * simplicity, the algorithms in this specification are presented as if the
+ * entire message being authenticated were available at once.
+ * <p>
+ * To authenticate a message, <code>Msg</code>, one first applies the
+ * universal hash function, resulting in a string which is typically much
+ * shorter than the original message. The pseudorandom function is applied to a
+ * nonce, and the result is used in the manner of a Vernam cipher: the
+ * authentication tag is the xor of the output from the hash function and the
+ * output from the pseudorandom function. Thus, an authentication tag is
+ * generated as
+ * <pre>
+ * AuthTag = f(Nonce) xor h(Msg)
+ * </pre>
+ * <p>
+ * Here <code>f</code> is the pseudorandom function shared between the sender
+ * and the receiver, and h is a universal hash function shared by the sender and
+ * the receiver. In <i>UMAC</i>, a shared key is used to key the pseudorandom
+ * function <code>f</code>, and then <code>f</code> is used for both tag
+ * generation and internally to generate all of the bits needed by the universal
+ * hash function.
+ * <p>
+ * The universal hash function that we use is called <code>UHASH</code>. It
+ * combines several software-optimized algorithms into a multi-layered
+ * structure. The algorithm is moderately complex. Some of this complexity comes
+ * from extensive speed optimizations.
+ * <p>
+ * For the pseudorandom function we use the block cipher of the <i>Advanced
+ * Encryption Standard</i> (AES).
+ * <p>
+ * The UMAC32 parameters, considered in this implementation are:
+ * <pre>
+ * UMAC32
+ * ------
+ * WORD-LEN 4
+ * UMAC-OUTPUT-LEN 8
+ * L1-KEY-LEN 1024
+ * UMAC-KEY-LEN 16
+ * ENDIAN-FAVORITE BIG *
+ * L1-OPERATIONS-SIGN UNSIGNED
+ * </pre>
+ * <p>
+ * Please note that this UMAC32 differs from the one described in the paper by
+ * the <i>ENDIAN-FAVORITE</i> value.
+ * <p>
+ * References:
+ * <ol>
+ * <li><a href="http://www.ietf.org/internet-drafts/draft-krovetz-umac-01.txt">
+ * UMAC</a>: Message Authentication Code using Universal Hashing.<br>
+ * T. Krovetz, J. Black, S. Halevi, A. Hevia, H. Krawczyk, and P. Rogaway.</li>
+ * </ol>
+ */
+public class UMac32
+ extends BaseMac
+{
+ /**
+ * Property name of the user-supplied <i>Nonce</i>. The value associated to
+ * this property name is taken to be a byte array.
+ */
+ public static final String NONCE_MATERIAL = "gnu.crypto.umac.nonce.material";
+ /** Known test vector. */
+ // private static final String TV1 = "3E5A0E09198B0F94";
+ // private static final String TV1 = "5FD764A6D3A9FD9D";
+ // private static final String TV1 = "48658DE1D9A70304";
+ private static final String TV1 = "455ED214A6909F20";
+ private static final BigInteger MAX_NONCE_ITERATIONS = BigInteger.ONE.shiftLeft(16 * 8);
+ // UMAC32 parameters
+ static final int OUTPUT_LEN = 8;
+ static final int L1_KEY_LEN = 1024;
+ static final int KEY_LEN = 16;
+ /** caches the result of the correctness test, once executed. */
+ private static Boolean valid;
+ private byte[] nonce;
+ private UHash32 uhash32;
+ private BigInteger nonceReuseCount;
+ /** The authentication key for this instance. */
+ private transient byte[] K;
+
+ /** Trivial 0-arguments constructor. */
+ public UMac32()
+ {
+ super("umac32");
+ }
+
+ /**
+ * Private constructor for cloning purposes.
+ *
+ * @param that the instance to clone.
+ */
+ private UMac32(UMac32 that)
+ {
+ this();
+
+ if (that.K != null)
+ this.K = (byte[]) that.K.clone();
+ if (that.nonce != null)
+ this.nonce = (byte[]) that.nonce.clone();
+ if (that.uhash32 != null)
+ this.uhash32 = (UHash32) that.uhash32.clone();
+ this.nonceReuseCount = that.nonceReuseCount;
+ }
+
+ public Object clone()
+ {
+ return new UMac32(this);
+ }
+
+ public int macSize()
+ {
+ return OUTPUT_LEN;
+ }
+
+ /**
+ * Initialising a <i>UMAC</i> instance consists of defining values for the
+ * following parameters:
+ * <ol>
+ * <li>Key Material: as the value of the attribute entry keyed by
+ * {@link #MAC_KEY_MATERIAL}. The value is taken to be a byte array
+ * containing the user-specified key material. The length of this array,
+ * if/when defined SHOULD be exactly equal to {@link #KEY_LEN}.</li>
+ * <li>Nonce Material: as the value of the attribute entry keyed by
+ * {@link #NONCE_MATERIAL}. The value is taken to be a byte array containing
+ * the user-specified nonce material. The length of this array, if/when
+ * defined SHOULD be (a) greater than zero, and (b) less or equal to 16 (the
+ * size of the AES block).</li>
+ * </ol>
+ * <p>
+ * For convenience, this implementation accepts that not both parameters be
+ * always specified.
+ * <ul>
+ * <li>If the <i>Key Material</i> is specified, but the <i>Nonce Material</i>
+ * is not, then this implementation, re-uses the previously set <i>Nonce
+ * Material</i> after (a) converting the bytes to an unsigned integer, (b)
+ * incrementing the number by one, and (c) converting it back to 16 bytes.</li>
+ * <li>If the <i>Nonce Material</i> is specified, but the <i>Key Material</i>
+ * is not, then this implementation re-uses the previously set <i>Key Material</i>.
+ * </li>
+ * </ul>
+ * <p>
+ * This method throws an exception if no <i>Key Material</i> is specified in
+ * the input map, and there is no previously set/defined <i>Key Material</i>
+ * (from an earlier invocation of this method). If a <i>Key Material</i> can
+ * be used, but no <i>Nonce Material</i> is defined or previously
+ * set/defined, then a default value of all-zeroes shall be used.
+ *
+ * @param attributes one or both of required parameters.
+ * @throws InvalidKeyException the key material specified is not of the
+ * correct length.
+ */
+ public void init(Map attributes) throws InvalidKeyException,
+ IllegalStateException
+ {
+ byte[] key = (byte[]) attributes.get(MAC_KEY_MATERIAL);
+ byte[] n = (byte[]) attributes.get(NONCE_MATERIAL);
+ boolean newKey = (key != null);
+ boolean newNonce = (n != null);
+ if (newKey)
+ {
+ if (key.length != KEY_LEN)
+ throw new InvalidKeyException("Key length: "
+ + String.valueOf(key.length));
+ K = key;
+ }
+ else
+ {
+ if (K == null)
+ throw new InvalidKeyException("Null Key");
+ }
+ if (newNonce)
+ {
+ if (n.length < 1 || n.length > 16)
+ throw new IllegalArgumentException("Invalid Nonce length: "
+ + String.valueOf(n.length));
+ if (n.length < 16) // pad with zeroes
+ {
+ byte[] newN = new byte[16];
+ System.arraycopy(n, 0, newN, 0, n.length);
+ nonce = newN;
+ }
+ else
+ nonce = n;
+
+ nonceReuseCount = BigInteger.ZERO;
+ }
+ else if (nonce == null) // use all-0 nonce if 1st time
+ {
+ nonce = new byte[16];
+ nonceReuseCount = BigInteger.ZERO;
+ }
+ else if (! newKey) // increment nonce if still below max count
+ {
+ nonceReuseCount = nonceReuseCount.add(BigInteger.ONE);
+ if (nonceReuseCount.compareTo(MAX_NONCE_ITERATIONS) >= 0)
+ {
+ // limit reached. we SHOULD have a key
+ throw new InvalidKeyException("Null Key and unusable old Nonce");
+ }
+ BigInteger N = new BigInteger(1, nonce);
+ N = N.add(BigInteger.ONE).mod(MAX_NONCE_ITERATIONS);
+ n = N.toByteArray();
+ if (n.length == 16)
+ nonce = n;
+ else if (n.length < 16)
+ {
+ nonce = new byte[16];
+ System.arraycopy(n, 0, nonce, 16 - n.length, n.length);
+ }
+ else
+ {
+ nonce = new byte[16];
+ System.arraycopy(n, n.length - 16, nonce, 0, 16);
+ }
+ }
+ else // do nothing, re-use old nonce value
+ nonceReuseCount = BigInteger.ZERO;
+
+ if (uhash32 == null)
+ uhash32 = new UHash32();
+
+ Map map = new HashMap();
+ map.put(MAC_KEY_MATERIAL, K);
+ uhash32.init(map);
+ }
+
+ public void update(byte b)
+ {
+ uhash32.update(b);
+ }
+
+ public void update(byte[] b, int offset, int len)
+ {
+ uhash32.update(b, offset, len);
+ }
+
+ public byte[] digest()
+ {
+ byte[] result = uhash32.digest();
+ byte[] pad = pdf(); // pdf(K, nonce);
+ for (int i = 0; i < OUTPUT_LEN; i++)
+ result[i] = (byte)(result[i] ^ pad[i]);
+
+ return result;
+ }
+
+ public void reset()
+ {
+ if (uhash32 != null)
+ uhash32.reset();
+ }
+
+ public boolean selfTest()
+ {
+ if (valid == null)
+ {
+ byte[] key;
+ try
+ {
+ key = "abcdefghijklmnop".getBytes("ASCII");
+ }
+ catch (UnsupportedEncodingException x)
+ {
+ throw new RuntimeException("ASCII not supported");
+ }
+ byte[] nonce = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
+ UMac32 mac = new UMac32();
+ Map attributes = new HashMap();
+ attributes.put(MAC_KEY_MATERIAL, key);
+ attributes.put(NONCE_MATERIAL, nonce);
+ try
+ {
+ mac.init(attributes);
+ }
+ catch (InvalidKeyException x)
+ {
+ x.printStackTrace(System.err);
+ return false;
+ }
+ byte[] data = new byte[128];
+ data[0] = (byte) 0x80;
+ mac.update(data, 0, 128);
+ byte[] result = mac.digest();
+ valid = Boolean.valueOf(TV1.equals(Util.toString(result)));
+ }
+ return valid.booleanValue();
+ }
+
+ /**
+ * @return byte array of length 8 (or OUTPUT_LEN) bytes.
+ */
+ private byte[] pdf()
+ {
+ // Make Nonce 16 bytes by prepending zeroes. done (see init())
+ // one AES invocation is enough for more than one PDF invocation
+ // number of index bits needed = 1
+ // Extract index bits and zero low bits of Nonce
+ BigInteger Nonce = new BigInteger(1, nonce);
+ int nlowbitsnum = Nonce.testBit(0) ? 1 : 0;
+ Nonce = Nonce.clearBit(0);
+ // Generate subkey, AES and extract indexed substring
+ IRandom kdf = new UMacGenerator();
+ Map map = new HashMap();
+ map.put(IBlockCipher.KEY_MATERIAL, K);
+ map.put(UMacGenerator.INDEX, Integer.valueOf(128));
+ kdf.init(map);
+ byte[] Kp = new byte[KEY_LEN];
+ try
+ {
+ kdf.nextBytes(Kp, 0, KEY_LEN);
+ }
+ catch (IllegalStateException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException(String.valueOf(x));
+ }
+ catch (LimitReachedException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException(String.valueOf(x));
+ }
+ IBlockCipher aes = CipherFactory.getInstance(Registry.AES_CIPHER);
+ map.put(IBlockCipher.KEY_MATERIAL, Kp);
+ try
+ {
+ aes.init(map);
+ }
+ catch (InvalidKeyException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException(String.valueOf(x));
+ }
+ catch (IllegalStateException x)
+ {
+ x.printStackTrace(System.err);
+ throw new RuntimeException(String.valueOf(x));
+ }
+ byte[] T = new byte[16];
+ aes.encryptBlock(nonce, 0, T, 0);
+ byte[] result = new byte[OUTPUT_LEN];
+ System.arraycopy(T, nlowbitsnum, result, 0, OUTPUT_LEN);
+ return result;
+ }
+}