diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/gnu/javax/crypto/mac | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/gnu/javax/crypto/mac')
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/BaseMac.java | 127 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/HMac.java | 263 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/HMacFactory.java | 111 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/IMac.java | 181 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/MacFactory.java | 130 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/MacInputStream.java | 124 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/MacOutputStream.java | 123 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/OMAC.java | 303 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/TMMH16.java | 339 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/UHash32.java | 758 | ||||
-rw-r--r-- | libjava/classpath/gnu/javax/crypto/mac/UMac32.java | 418 |
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; + } +} |