Frames | No Frames |
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: }