123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- /* 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;
- }
- }
- }
|