Frames | No Frames |
1: /* UMac32.java -- 2: Copyright (C) 2001, 2002, 2003, 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: import gnu.java.security.util.Util; 45: import gnu.javax.crypto.cipher.CipherFactory; 46: import gnu.javax.crypto.cipher.IBlockCipher; 47: import gnu.javax.crypto.prng.UMacGenerator; 48: 49: import java.io.UnsupportedEncodingException; 50: import java.math.BigInteger; 51: import java.security.InvalidKeyException; 52: import java.util.HashMap; 53: import java.util.Map; 54: 55: /** 56: * The implementation of the <i>UMAC</i> (Universal Message Authentication 57: * Code). 58: * <p> 59: * The <i>UMAC</i> algorithms described are <i>parameterized</i>. This means 60: * that various low-level choices, like the endian convention and the underlying 61: * cryptographic primitive, have not been fixed. One must choose values for 62: * these parameters before the authentication tag generated by <i>UMAC</i> (for 63: * a given message, key, and nonce) becomes fully-defined. In this document we 64: * provide two collections of parameter settings, and have named the sets 65: * <i>UMAC16</i> and <i>UMAC32</i>. The parameter sets have been chosen based 66: * on experimentation and provide good performance on a wide variety of 67: * processors. <i>UMAC16</i> is designed to excel on processors which provide 68: * small-scale SIMD parallelism of the type found in Intel's MMX and Motorola's 69: * AltiVec instruction sets, while <i>UMAC32</i> is designed to do well on 70: * processors with good 32- and 64- bit support. <i>UMAC32</i> may take 71: * advantage of SIMD parallelism in future processors. 72: * <p> 73: * <i>UMAC</i> has been designed to allow implementations which accommodate 74: * <i>on-line</i> authentication. This means that pieces of the message may be 75: * presented to <i>UMAC</i> at different times (but in correct order) and an 76: * on-line implementation will be able to process the message correctly without 77: * the need to buffer more than a few dozen bytes of the message. For 78: * simplicity, the algorithms in this specification are presented as if the 79: * entire message being authenticated were available at once. 80: * <p> 81: * To authenticate a message, <code>Msg</code>, one first applies the 82: * universal hash function, resulting in a string which is typically much 83: * shorter than the original message. The pseudorandom function is applied to a 84: * nonce, and the result is used in the manner of a Vernam cipher: the 85: * authentication tag is the xor of the output from the hash function and the 86: * output from the pseudorandom function. Thus, an authentication tag is 87: * generated as 88: * <pre> 89: * AuthTag = f(Nonce) xor h(Msg) 90: * </pre> 91: * <p> 92: * Here <code>f</code> is the pseudorandom function shared between the sender 93: * and the receiver, and h is a universal hash function shared by the sender and 94: * the receiver. In <i>UMAC</i>, a shared key is used to key the pseudorandom 95: * function <code>f</code>, and then <code>f</code> is used for both tag 96: * generation and internally to generate all of the bits needed by the universal 97: * hash function. 98: * <p> 99: * The universal hash function that we use is called <code>UHASH</code>. It 100: * combines several software-optimized algorithms into a multi-layered 101: * structure. The algorithm is moderately complex. Some of this complexity comes 102: * from extensive speed optimizations. 103: * <p> 104: * For the pseudorandom function we use the block cipher of the <i>Advanced 105: * Encryption Standard</i> (AES). 106: * <p> 107: * The UMAC32 parameters, considered in this implementation are: 108: * <pre> 109: * UMAC32 110: * ------ 111: * WORD-LEN 4 112: * UMAC-OUTPUT-LEN 8 113: * L1-KEY-LEN 1024 114: * UMAC-KEY-LEN 16 115: * ENDIAN-FAVORITE BIG * 116: * L1-OPERATIONS-SIGN UNSIGNED 117: * </pre> 118: * <p> 119: * Please note that this UMAC32 differs from the one described in the paper by 120: * the <i>ENDIAN-FAVORITE</i> value. 121: * <p> 122: * References: 123: * <ol> 124: * <li><a href="http://www.ietf.org/internet-drafts/draft-krovetz-umac-01.txt"> 125: * UMAC</a>: Message Authentication Code using Universal Hashing.<br> 126: * T. Krovetz, J. Black, S. Halevi, A. Hevia, H. Krawczyk, and P. Rogaway.</li> 127: * </ol> 128: */ 129: public class UMac32 130: extends BaseMac 131: { 132: /** 133: * Property name of the user-supplied <i>Nonce</i>. The value associated to 134: * this property name is taken to be a byte array. 135: */ 136: public static final String NONCE_MATERIAL = "gnu.crypto.umac.nonce.material"; 137: /** Known test vector. */ 138: // private static final String TV1 = "3E5A0E09198B0F94"; 139: // private static final String TV1 = "5FD764A6D3A9FD9D"; 140: // private static final String TV1 = "48658DE1D9A70304"; 141: private static final String TV1 = "455ED214A6909F20"; 142: private static final BigInteger MAX_NONCE_ITERATIONS = BigInteger.ONE.shiftLeft(16 * 8); 143: // UMAC32 parameters 144: static final int OUTPUT_LEN = 8; 145: static final int L1_KEY_LEN = 1024; 146: static final int KEY_LEN = 16; 147: /** caches the result of the correctness test, once executed. */ 148: private static Boolean valid; 149: private byte[] nonce; 150: private UHash32 uhash32; 151: private BigInteger nonceReuseCount; 152: /** The authentication key for this instance. */ 153: private transient byte[] K; 154: 155: /** Trivial 0-arguments constructor. */ 156: public UMac32() 157: { 158: super("umac32"); 159: } 160: 161: /** 162: * Private constructor for cloning purposes. 163: * 164: * @param that the instance to clone. 165: */ 166: private UMac32(UMac32 that) 167: { 168: this(); 169: 170: if (that.K != null) 171: this.K = (byte[]) that.K.clone(); 172: if (that.nonce != null) 173: this.nonce = (byte[]) that.nonce.clone(); 174: if (that.uhash32 != null) 175: this.uhash32 = (UHash32) that.uhash32.clone(); 176: this.nonceReuseCount = that.nonceReuseCount; 177: } 178: 179: public Object clone() 180: { 181: return new UMac32(this); 182: } 183: 184: public int macSize() 185: { 186: return OUTPUT_LEN; 187: } 188: 189: /** 190: * Initialising a <i>UMAC</i> instance consists of defining values for the 191: * following parameters: 192: * <ol> 193: * <li>Key Material: as the value of the attribute entry keyed by 194: * {@link #MAC_KEY_MATERIAL}. The value is taken to be a byte array 195: * containing the user-specified key material. The length of this array, 196: * if/when defined SHOULD be exactly equal to {@link #KEY_LEN}.</li> 197: * <li>Nonce Material: as the value of the attribute entry keyed by 198: * {@link #NONCE_MATERIAL}. The value is taken to be a byte array containing 199: * the user-specified nonce material. The length of this array, if/when 200: * defined SHOULD be (a) greater than zero, and (b) less or equal to 16 (the 201: * size of the AES block).</li> 202: * </ol> 203: * <p> 204: * For convenience, this implementation accepts that not both parameters be 205: * always specified. 206: * <ul> 207: * <li>If the <i>Key Material</i> is specified, but the <i>Nonce Material</i> 208: * is not, then this implementation, re-uses the previously set <i>Nonce 209: * Material</i> after (a) converting the bytes to an unsigned integer, (b) 210: * incrementing the number by one, and (c) converting it back to 16 bytes.</li> 211: * <li>If the <i>Nonce Material</i> is specified, but the <i>Key Material</i> 212: * is not, then this implementation re-uses the previously set <i>Key Material</i>. 213: * </li> 214: * </ul> 215: * <p> 216: * This method throws an exception if no <i>Key Material</i> is specified in 217: * the input map, and there is no previously set/defined <i>Key Material</i> 218: * (from an earlier invocation of this method). If a <i>Key Material</i> can 219: * be used, but no <i>Nonce Material</i> is defined or previously 220: * set/defined, then a default value of all-zeroes shall be used. 221: * 222: * @param attributes one or both of required parameters. 223: * @throws InvalidKeyException the key material specified is not of the 224: * correct length. 225: */ 226: public void init(Map attributes) throws InvalidKeyException, 227: IllegalStateException 228: { 229: byte[] key = (byte[]) attributes.get(MAC_KEY_MATERIAL); 230: byte[] n = (byte[]) attributes.get(NONCE_MATERIAL); 231: boolean newKey = (key != null); 232: boolean newNonce = (n != null); 233: if (newKey) 234: { 235: if (key.length != KEY_LEN) 236: throw new InvalidKeyException("Key length: " 237: + String.valueOf(key.length)); 238: K = key; 239: } 240: else 241: { 242: if (K == null) 243: throw new InvalidKeyException("Null Key"); 244: } 245: if (newNonce) 246: { 247: if (n.length < 1 || n.length > 16) 248: throw new IllegalArgumentException("Invalid Nonce length: " 249: + String.valueOf(n.length)); 250: if (n.length < 16) // pad with zeroes 251: { 252: byte[] newN = new byte[16]; 253: System.arraycopy(n, 0, newN, 0, n.length); 254: nonce = newN; 255: } 256: else 257: nonce = n; 258: 259: nonceReuseCount = BigInteger.ZERO; 260: } 261: else if (nonce == null) // use all-0 nonce if 1st time 262: { 263: nonce = new byte[16]; 264: nonceReuseCount = BigInteger.ZERO; 265: } 266: else if (! newKey) // increment nonce if still below max count 267: { 268: nonceReuseCount = nonceReuseCount.add(BigInteger.ONE); 269: if (nonceReuseCount.compareTo(MAX_NONCE_ITERATIONS) >= 0) 270: { 271: // limit reached. we SHOULD have a key 272: throw new InvalidKeyException("Null Key and unusable old Nonce"); 273: } 274: BigInteger N = new BigInteger(1, nonce); 275: N = N.add(BigInteger.ONE).mod(MAX_NONCE_ITERATIONS); 276: n = N.toByteArray(); 277: if (n.length == 16) 278: nonce = n; 279: else if (n.length < 16) 280: { 281: nonce = new byte[16]; 282: System.arraycopy(n, 0, nonce, 16 - n.length, n.length); 283: } 284: else 285: { 286: nonce = new byte[16]; 287: System.arraycopy(n, n.length - 16, nonce, 0, 16); 288: } 289: } 290: else // do nothing, re-use old nonce value 291: nonceReuseCount = BigInteger.ZERO; 292: 293: if (uhash32 == null) 294: uhash32 = new UHash32(); 295: 296: Map map = new HashMap(); 297: map.put(MAC_KEY_MATERIAL, K); 298: uhash32.init(map); 299: } 300: 301: public void update(byte b) 302: { 303: uhash32.update(b); 304: } 305: 306: public void update(byte[] b, int offset, int len) 307: { 308: uhash32.update(b, offset, len); 309: } 310: 311: public byte[] digest() 312: { 313: byte[] result = uhash32.digest(); 314: byte[] pad = pdf(); // pdf(K, nonce); 315: for (int i = 0; i < OUTPUT_LEN; i++) 316: result[i] = (byte)(result[i] ^ pad[i]); 317: 318: return result; 319: } 320: 321: public void reset() 322: { 323: if (uhash32 != null) 324: uhash32.reset(); 325: } 326: 327: public boolean selfTest() 328: { 329: if (valid == null) 330: { 331: byte[] key; 332: try 333: { 334: key = "abcdefghijklmnop".getBytes("ASCII"); 335: } 336: catch (UnsupportedEncodingException x) 337: { 338: throw new RuntimeException("ASCII not supported"); 339: } 340: byte[] nonce = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; 341: UMac32 mac = new UMac32(); 342: Map attributes = new HashMap(); 343: attributes.put(MAC_KEY_MATERIAL, key); 344: attributes.put(NONCE_MATERIAL, nonce); 345: try 346: { 347: mac.init(attributes); 348: } 349: catch (InvalidKeyException x) 350: { 351: x.printStackTrace(System.err); 352: return false; 353: } 354: byte[] data = new byte[128]; 355: data[0] = (byte) 0x80; 356: mac.update(data, 0, 128); 357: byte[] result = mac.digest(); 358: valid = Boolean.valueOf(TV1.equals(Util.toString(result))); 359: } 360: return valid.booleanValue(); 361: } 362: 363: /** 364: * @return byte array of length 8 (or OUTPUT_LEN) bytes. 365: */ 366: private byte[] pdf() 367: { 368: // Make Nonce 16 bytes by prepending zeroes. done (see init()) 369: // one AES invocation is enough for more than one PDF invocation 370: // number of index bits needed = 1 371: // Extract index bits and zero low bits of Nonce 372: BigInteger Nonce = new BigInteger(1, nonce); 373: int nlowbitsnum = Nonce.testBit(0) ? 1 : 0; 374: Nonce = Nonce.clearBit(0); 375: // Generate subkey, AES and extract indexed substring 376: IRandom kdf = new UMacGenerator(); 377: Map map = new HashMap(); 378: map.put(IBlockCipher.KEY_MATERIAL, K); 379: map.put(UMacGenerator.INDEX, Integer.valueOf(128)); 380: kdf.init(map); 381: byte[] Kp = new byte[KEY_LEN]; 382: try 383: { 384: kdf.nextBytes(Kp, 0, KEY_LEN); 385: } 386: catch (IllegalStateException x) 387: { 388: x.printStackTrace(System.err); 389: throw new RuntimeException(String.valueOf(x)); 390: } 391: catch (LimitReachedException x) 392: { 393: x.printStackTrace(System.err); 394: throw new RuntimeException(String.valueOf(x)); 395: } 396: IBlockCipher aes = CipherFactory.getInstance(Registry.AES_CIPHER); 397: map.put(IBlockCipher.KEY_MATERIAL, Kp); 398: try 399: { 400: aes.init(map); 401: } 402: catch (InvalidKeyException x) 403: { 404: x.printStackTrace(System.err); 405: throw new RuntimeException(String.valueOf(x)); 406: } 407: catch (IllegalStateException x) 408: { 409: x.printStackTrace(System.err); 410: throw new RuntimeException(String.valueOf(x)); 411: } 412: byte[] T = new byte[16]; 413: aes.encryptBlock(nonce, 0, T, 0); 414: byte[] result = new byte[OUTPUT_LEN]; 415: System.arraycopy(T, nlowbitsnum, result, 0, OUTPUT_LEN); 416: return result; 417: } 418: }