OSDN Git Service

2006-08-14 Mark Wielaard <mark@klomp.org>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / javax / crypto / mac / TMMH16.java
1 /* TMMH16.java -- 
2    Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
3
4 This file is a part of GNU Classpath.
5
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.
10
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.
15
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
20
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.
25
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.  */
37
38
39 package gnu.javax.crypto.mac;
40
41 import gnu.java.security.Registry;
42 import gnu.java.security.prng.IRandom;
43 import gnu.java.security.prng.LimitReachedException;
44
45 import java.security.InvalidKeyException;
46 import java.util.Map;
47
48 /**
49  * <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.
55  * <p>
56  * <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).
60  * <p>
61  * 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.
64  * The 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.
67  * <p>
68  * 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>.
71  * <p>
72  * References:
73  * <ol>
74  * <li><a
75  * href="http://www.ietf.org/internet-drafts/draft-mcgrew-saag-tmmh-01.txt"> The
76  * Truncated Multi-Modular Hash Function (TMMH)</a>, David A. McGrew.</li>
77  * </ol>
78  */
79 public class TMMH16
80     extends BaseMac
81     implements Cloneable
82 {
83   public static final String TAG_LENGTH = "gnu.crypto.mac.tmmh.tag.length";
84   public static final String KEYSTREAM = "gnu.crypto.mac.tmmh.keystream";
85   public static final String PREFIX = "gnu.crypto.mac.tmmh.prefix";
86   private static final int P = (1 << 16) + 1; // the TMMH/16 prime
87   /** caches the result of the correctness test, once executed. */
88   private static Boolean valid;
89   private int tagWords = 0; // the tagLength expressed in words
90   private IRandom keystream = null; // the keystream generator
91   private byte[] prefix; // mask to use when operating as an authentication f.
92   private long keyWords; // key words counter
93   private long msgLength; // in bytes
94   private long msgWords; // should be = msgLength * WORD_LENGTH
95   private int[] context; // the tmmh running context; length == TAG_WORDS
96   private int[] K0; // the first TAG_WORDS words of the keystream
97   private int[] Ki; // the sliding TAG_WORDS words of the keystream
98   private int Mi; // current message word being constructed
99
100   /** Trivial 0-arguments constructor. */
101   public TMMH16()
102   {
103     super(Registry.TMMH16);
104   }
105
106   public int macSize()
107   {
108     return tagWords * 2;
109   }
110
111   public void init(Map attributes) throws InvalidKeyException,
112       IllegalStateException
113   {
114     int wantTagLength = 0;
115     Integer tagLength = (Integer) attributes.get(TAG_LENGTH); // get tag length
116     if (tagLength == null)
117       {
118         if (tagWords == 0) // was never set
119           throw new IllegalArgumentException(TAG_LENGTH);
120         // else re-use
121       }
122     else // check if positive and is divisible by WORD_LENGTH
123       {
124         wantTagLength = tagLength.intValue();
125         if (wantTagLength < 2 || (wantTagLength % 2 != 0))
126           throw new IllegalArgumentException(TAG_LENGTH);
127         else if (wantTagLength > (512 / 8)) // 512-bits is our maximum
128           throw new IllegalArgumentException(TAG_LENGTH);
129
130         tagWords = wantTagLength / 2; // init local vars
131         K0 = new int[tagWords];
132         Ki = new int[tagWords];
133         context = new int[tagWords];
134       }
135
136     prefix = (byte[]) attributes.get(PREFIX);
137     if (prefix == null) // default to all-zeroes
138       prefix = new byte[tagWords * 2];
139     else // ensure it's as long as it should
140       {
141         if (prefix.length != tagWords * 2)
142           throw new IllegalArgumentException(PREFIX);
143       }
144
145     IRandom prng = (IRandom) attributes.get(KEYSTREAM); // get keystream
146     if (prng == null)
147       {
148         if (keystream == null)
149           throw new IllegalArgumentException(KEYSTREAM);
150         // else reuse
151       }
152     else
153       keystream = prng;
154
155     reset(); // reset context variables
156     for (int i = 0; i < tagWords; i++) // init starting key words
157       Ki[i] = K0[i] = getNextKeyWord(keystream);
158   }
159
160   // The words of the key are denoted as K[1], K[2], ..., K[KEY_WORDS], and the
161   // words of the message (after zero padding, if needed) are denoted as M[1],
162   // M[2], ..., M[MSG_WORDS], where MSG_WORDS is the smallest number such that
163   // 2 * MSG_WORDS is at least MESSAGE_LENGTH, and KEY_WORDS is KEY_LENGTH / 2.
164   //
165   // If MESSAGE_LENGTH is greater than MAX_HASH_LENGTH, then the value of
166   // TMMH/16 is undefined. Implementations MUST indicate an error if asked to
167   // hash a message with such a length. Otherwise, the hash value is defined
168   // to be the length TAG_WORDS sequence of words in which the j-th word in the
169   // sequence is defined as
170   //
171   // [ [ K[j] * MESSAGE_LENGTH +32 K[j+1] * M[1] +32 K[j+2] * M[2]
172   // +32 ... K[j+MSG_WORDS] * M[MSG_WORDS] ] modulo p ] modulo 2^16
173   //
174   // where j ranges from 1 to TAG_WORDS.
175   public void update(byte b)
176   {
177     this.update(b, keystream);
178   }
179
180   public void update(byte[] b, int offset, int len)
181   {
182     for (int i = 0; i < len; i++)
183       this.update(b[offset + i], keystream);
184   }
185
186   // For TMMH/16, KEY_LENGTH and TAG_LENGTH MUST be a multiple of two. The key,
187   // message, and hash value are treated as a sequence of unsigned sixteen bit
188   // integers in network byte order. (In this section, we call such an integer
189   // a word.) If MESSAGE_LENGTH is odd, then a zero byte is appended to the
190   // message to align it on a word boundary, though this process does not
191   // change the value of MESSAGE_LENGTH.
192   //
193   // ... Otherwise, the hash value is defined to be the length TAG_WORDS
194   // sequence of words in which the j-th word in the sequence is defined as
195   //
196   // [ [ K[j] * MESSAGE_LENGTH +32 K[j+1] * M[1] +32 K[j+2] * M[2]
197   // +32 ... K[j+MSG_WORDS] * M[MSG_WORDS] ] modulo p ] modulo 2^16
198   //
199   // where j ranges from 1 to TAG_WORDS.
200   //
201   // Here, TAG_WORDS is equal to TAG_LENGTH / 2, and p is equal to 2^16 + 1.
202   // The symbol * denotes multiplication and the symbol +32 denotes addition
203   // modulo 2^32.
204   public byte[] digest()
205   {
206     return this.digest(keystream);
207   }
208
209   public void reset()
210   {
211     msgLength = msgWords = keyWords = 0L;
212     Mi = 0;
213     for (int i = 0; i < tagWords; i++)
214       context[i] = 0;
215   }
216
217   public boolean selfTest()
218   {
219     if (valid == null)
220       {
221         // TODO: compute and test equality with one known vector
222         valid = Boolean.TRUE;
223       }
224     return valid.booleanValue();
225   }
226
227   public Object clone() throws CloneNotSupportedException
228   {
229     TMMH16 result = (TMMH16) super.clone();
230     if (this.keystream != null)
231       result.keystream = (IRandom) this.keystream.clone();
232     if (this.prefix != null)
233       result.prefix = (byte[]) this.prefix.clone();
234     if (this.context != null)
235       result.context = (int[]) this.context.clone();
236     if (this.K0 != null)
237       result.K0 = (int[]) this.K0.clone();
238     if (this.Ki != null)
239       result.Ki = (int[]) this.Ki.clone();
240     return result;
241   }
242
243   /**
244    * Similar to the same method with one argument, but uses the designated
245    * random number generator to compute needed keying material.
246    * 
247    * @param b the byte to process.
248    * @param prng the source of randomness to use.
249    */
250   public void update(byte b, IRandom prng)
251   {
252     Mi <<= 8; // update message buffer
253     Mi |= b & 0xFF;
254     msgLength++; // update message length (bytes)
255     if (msgLength % 2 == 0) // got a full word
256       {
257         msgWords++; // update message words counter
258         System.arraycopy(Ki, 1, Ki, 0, tagWords - 1); // 1. shift Ki up by 1
259         Ki[tagWords - 1] = getNextKeyWord(prng); // 2. fill last box of Ki
260         long t; // temp var to allow working in modulo 2^32
261         for (int i = 0; i < tagWords; i++) // 3. update context
262           {
263             t = context[i] & 0xFFFFFFFFL;
264             t += Ki[i] * Mi;
265             context[i] = (int) t;
266           }
267         Mi = 0; // reset message buffer
268       }
269   }
270
271   /**
272    * Similar to the same method with three arguments, but uses the designated
273    * random number generator to compute needed keying material.
274    * 
275    * @param b the byte array to process.
276    * @param offset the starting offset in <code>b</code> to start considering
277    *          the bytes to process.
278    * @param len the number of bytes in <code>b</code> starting from
279    *          <code>offset</code> to process.
280    * @param prng the source of randomness to use.
281    */
282   public void update(byte[] b, int offset, int len, IRandom prng)
283   {
284     for (int i = 0; i < len; i++)
285       this.update(b[offset + i], prng);
286   }
287
288   /**
289    * Similar to the same method with no arguments, but uses the designated
290    * random number generator to compute needed keying material.
291    * 
292    * @param prng the source of randomness to use.
293    * @return the final result of the algorithm.
294    */
295   public byte[] digest(IRandom prng)
296   {
297     doFinalRound(prng);
298     byte[] result = new byte[tagWords * 2];
299     for (int i = 0, j = 0; i < tagWords; i++)
300       {
301         result[j] = (byte)((context[i] >>> 8) ^ prefix[j]);
302         j++;
303         result[j] = (byte)(context[i] ^ prefix[j]);
304         j++;
305       }
306     reset();
307     return result;
308   }
309
310   private int getNextKeyWord(IRandom prng)
311   {
312     int result = 0;
313     try
314       {
315         result = (prng.nextByte() & 0xFF) << 8 | (prng.nextByte() & 0xFF);
316       }
317     catch (LimitReachedException x)
318       {
319         throw new RuntimeException(String.valueOf(x));
320       }
321     keyWords++; // update key words counter
322     return result;
323   }
324
325   private void doFinalRound(IRandom prng)
326   {
327     long limit = msgLength; // formula works on real message length
328     while (msgLength % 2 != 0)
329       update((byte) 0x00, prng);
330     long t;
331     for (int i = 0; i < tagWords; i++)
332       {
333         t = context[i] & 0xFFFFFFFFL;
334         t += K0[i] * limit;
335         t %= P;
336         context[i] = (int) t;
337       }
338   }
339 }