Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / javax / crypto / mac / TMMH16.java
blobaf6e78fcf87eca32e1c9ebe665a26e0d88567e2a
1 /* TMMH16.java --
2 Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
4 This file is a part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.javax.crypto.mac;
41 import gnu.java.security.Registry;
42 import gnu.java.security.prng.IRandom;
43 import gnu.java.security.prng.LimitReachedException;
45 import java.security.InvalidKeyException;
46 import java.util.Map;
48 /**
49 * <p><i>TMMH</i> is a <i>universal</i> hash function suitable for message
50 * authentication in the Wegman-Carter paradigm, as in the Stream Cipher
51 * Security Transform. It is simple, quick, and especially appropriate for
52 * Digital Signal Processors and other processors with a fast multiply
53 * operation, though a straightforward implementation requires storage equal in
54 * length to the largest message to be hashed.</p>
56 * <p><i>TMMH</i> is a simple hash function which maps a key and a message to a
57 * hash value. There are two versions of TMMH: TMMH/16 and TMMH/32. <i>TMMH</i>
58 * can be used as a message authentication code, as described in Section 5 (see
59 * References).</p>
61 * <p>The key, message, and hash value are all octet strings, and the lengths of
62 * these quantities are denoted as <code>KEY_LENGTH</code>,
63 * <code>MESSAGE_LENGTH</code>, and <code>TAG_LENGTH</code>, respectively. The
64 * values of <code>KEY_LENGTH</code> and <code>TAG_LENGTH</code>
65 * <bold>MUST</bold> be fixed for any particular fixed value of the key, and
66 * must obey the alignment restrictions described below.</p>
68 * <p>The parameter <code>MAX_HASH_LENGTH</code>, which denotes the maximum
69 * value which <code>MESSAGE_LENGTH</code> may take, is equal to
70 * <code>KEY_LENGTH - TAG_LENGTH</code>.</p>
72 * <p>References:</p>
74 * <ol>
75 * <li><a
76 href="http://www.ietf.org/internet-drafts/draft-mcgrew-saag-tmmh-01.txt">
77 * The Truncated Multi-Modular Hash Function (TMMH)</a>, David A. McGrew.</li>
78 * </ol>
80 public class TMMH16 extends BaseMac implements Cloneable
83 // Constants and variables
84 // -------------------------------------------------------------------------
86 public static final String TAG_LENGTH = "gnu.crypto.mac.tmmh.tag.length";
88 public static final String KEYSTREAM = "gnu.crypto.mac.tmmh.keystream";
90 public static final String PREFIX = "gnu.crypto.mac.tmmh.prefix";
92 private static final int P = (1 << 16) + 1; // the TMMH/16 prime
94 /** caches the result of the correctness test, once executed. */
95 private static Boolean valid;
97 private int tagWords = 0; // the tagLength expressed in words
99 private IRandom keystream = null; // the keystream generator
101 private byte[] prefix; // mask to use when operating as an authentication f.
103 private long keyWords; // key words counter
105 private long msgLength; // in bytes
107 private long msgWords; // should be = msgLength * WORD_LENGTH
109 private int[] context; // the tmmh running context; length == TAG_WORDS
111 private int[] K0; // the first TAG_WORDS words of the keystream
113 private int[] Ki; // the sliding TAG_WORDS words of the keystream
115 private int Mi; // current message word being constructed
117 // Constructor(s)
118 // -------------------------------------------------------------------------
120 /** Trivial 0-arguments constructor. */
121 public TMMH16()
123 super(Registry.TMMH16);
126 // Class methods
127 // -------------------------------------------------------------------------
129 // Instance methods
130 // -------------------------------------------------------------------------
132 // gnu.crypto.mac.IMac interface implementation ----------------------------
134 public int macSize()
136 return tagWords * 2;
139 public void init(Map attributes) throws InvalidKeyException,
140 IllegalStateException
142 int wantTagLength = 0;
143 Integer tagLength = (Integer) attributes.get(TAG_LENGTH); // get tag length
144 if (tagLength == null)
146 if (tagWords == 0)
147 { // was never set
148 throw new IllegalArgumentException(TAG_LENGTH);
149 } // else re-use
151 else
152 { // check if positive and is divisible by WORD_LENGTH
153 wantTagLength = tagLength.intValue();
154 if (wantTagLength < 2 || (wantTagLength % 2 != 0))
156 throw new IllegalArgumentException(TAG_LENGTH);
158 else if (wantTagLength > (512 / 8))
159 { // 512-bits is our maximum
160 throw new IllegalArgumentException(TAG_LENGTH);
163 tagWords = wantTagLength / 2; // init local vars
164 K0 = new int[tagWords];
165 Ki = new int[tagWords];
166 context = new int[tagWords];
169 prefix = (byte[]) attributes.get(PREFIX);
170 if (prefix == null)
171 { // default to all-zeroes
172 prefix = new byte[tagWords * 2];
174 else
175 { // ensure it's as long as it should
176 if (prefix.length != tagWords * 2)
178 throw new IllegalArgumentException(PREFIX);
182 IRandom prng = (IRandom) attributes.get(KEYSTREAM); // get keystream
183 if (prng == null)
185 if (keystream == null)
187 throw new IllegalArgumentException(KEYSTREAM);
188 } // else reuse
190 else
192 keystream = prng;
195 reset(); // reset context variables
196 for (int i = 0; i < tagWords; i++)
197 { // init starting key words
198 Ki[i] = K0[i] = getNextKeyWord(keystream);
202 // The words of the key are denoted as K[1], K[2], ..., K[KEY_WORDS], and the
203 // words of the message (after zero padding, if needed) are denoted as M[1],
204 // M[2], ..., M[MSG_WORDS], where MSG_WORDS is the smallest number such that
205 // 2 * MSG_WORDS is at least MESSAGE_LENGTH, and KEY_WORDS is KEY_LENGTH / 2.
207 // If MESSAGE_LENGTH is greater than MAX_HASH_LENGTH, then the value of
208 // TMMH/16 is undefined. Implementations MUST indicate an error if asked to
209 // hash a message with such a length. Otherwise, the hash value is defined
210 // to be the length TAG_WORDS sequence of words in which the j-th word in the
211 // sequence is defined as
213 // [ [ K[j] * MESSAGE_LENGTH +32 K[j+1] * M[1] +32 K[j+2] * M[2]
214 // +32 ... K[j+MSG_WORDS] * M[MSG_WORDS] ] modulo p ] modulo 2^16
216 // where j ranges from 1 to TAG_WORDS.
217 public void update(byte b)
219 this.update(b, keystream);
222 public void update(byte[] b, int offset, int len)
224 for (int i = 0; i < len; i++)
226 this.update(b[offset + i], keystream);
230 // For TMMH/16, KEY_LENGTH and TAG_LENGTH MUST be a multiple of two. The key,
231 // message, and hash value are treated as a sequence of unsigned sixteen bit
232 // integers in network byte order. (In this section, we call such an integer
233 // a word.) If MESSAGE_LENGTH is odd, then a zero byte is appended to the
234 // message to align it on a word boundary, though this process does not
235 // change the value of MESSAGE_LENGTH.
237 // ... Otherwise, the hash value is defined to be the length TAG_WORDS
238 // sequence of words in which the j-th word in the sequence is defined as
240 // [ [ K[j] * MESSAGE_LENGTH +32 K[j+1] * M[1] +32 K[j+2] * M[2]
241 // +32 ... K[j+MSG_WORDS] * M[MSG_WORDS] ] modulo p ] modulo 2^16
243 // where j ranges from 1 to TAG_WORDS.
245 // Here, TAG_WORDS is equal to TAG_LENGTH / 2, and p is equal to 2^16 + 1.
246 // The symbol * denotes multiplication and the symbol +32 denotes addition
247 // modulo 2^32.
248 public byte[] digest()
250 return this.digest(keystream);
253 public void reset()
255 msgLength = msgWords = keyWords = 0L;
256 Mi = 0;
257 for (int i = 0; i < tagWords; i++)
259 context[i] = 0;
263 public boolean selfTest()
265 if (valid == null)
267 // TODO: compute and test equality with one known vector
269 valid = Boolean.TRUE;
271 return valid.booleanValue();
274 // Cloneable interface implementation ---------------------------------------
276 public Object clone() throws CloneNotSupportedException
278 TMMH16 result = (TMMH16) super.clone();
280 if (this.keystream != null)
281 result.keystream = (IRandom) this.keystream.clone();
283 if (this.prefix != null)
284 result.prefix = (byte[]) this.prefix.clone();
286 if (this.context != null)
287 result.context = (int[]) this.context.clone();
289 if (this.K0 != null)
290 result.K0 = (int[]) this.K0.clone();
292 if (this.Ki != null)
293 result.Ki = (int[]) this.Ki.clone();
295 return result;
298 // own methods -------------------------------------------------------------
301 * <p>Similar to the same method with one argument, but uses the designated
302 * random number generator to compute needed keying material.</p>
304 * @param b the byte to process.
305 * @param prng the source of randomness to use.
307 public void update(byte b, IRandom prng)
309 Mi <<= 8; // update message buffer
310 Mi |= b & 0xFF;
311 msgLength++; // update message length (bytes)
312 if (msgLength % 2 == 0)
313 { // got a full word
314 msgWords++; // update message words counter
315 System.arraycopy(Ki, 1, Ki, 0, tagWords - 1); // 1. shift Ki up by 1
316 Ki[tagWords - 1] = getNextKeyWord(prng); // 2. fill last box of Ki
317 long t; // temp var to allow working in modulo 2^32
318 for (int i = 0; i < tagWords; i++)
319 { // 3. update context
320 t = context[i] & 0xFFFFFFFFL;
321 t += Ki[i] * Mi;
322 context[i] = (int) t;
324 Mi = 0; // reset message buffer
329 * <p>Similar to the same method with three arguments, but uses the
330 * designated random number generator to compute needed keying material.</p>
332 * @param b the byte array to process.
333 * @param offset the starting offset in <code>b</code> to start considering
334 * the bytes to process.
335 * @param len the number of bytes in <code>b</code> starting from
336 * <code>offset</code> to process.
337 * @param prng the source of randomness to use.
339 public void update(byte[] b, int offset, int len, IRandom prng)
341 for (int i = 0; i < len; i++)
343 this.update(b[offset + i], prng);
348 * <p>Similar to the same method with no arguments, but uses the designated
349 * random number generator to compute needed keying material.</p>
351 * @param prng the source of randomness to use.
352 * @return the final result of the algorithm.
354 public byte[] digest(IRandom prng)
356 doFinalRound(prng);
357 byte[] result = new byte[tagWords * 2];
358 for (int i = 0, j = 0; i < tagWords; i++)
360 result[j] = (byte) ((context[i] >>> 8) ^ prefix[j]);
361 j++;
362 result[j] = (byte) (context[i] ^ prefix[j]);
363 j++;
366 reset();
367 return result;
370 private int getNextKeyWord(IRandom prng)
372 int result = 0;
375 result = (prng.nextByte() & 0xFF) << 8 | (prng.nextByte() & 0xFF);
377 catch (LimitReachedException x)
379 throw new RuntimeException(String.valueOf(x));
382 keyWords++; // update key words counter
383 return result;
386 private void doFinalRound(IRandom prng)
388 long limit = msgLength; // formula works on real message length
389 while (msgLength % 2 != 0)
391 update((byte) 0x00, prng);
393 long t;
394 for (int i = 0; i < tagWords; i++)
396 t = context[i] & 0xFFFFFFFFL;
397 t += K0[i] * limit;
398 t %= P;
399 context[i] = (int) t;