123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /* UMac32.java --
- Copyright (C) 2001, 2002, 2003, 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 gnu.java.security.util.Util;
- import gnu.javax.crypto.cipher.CipherFactory;
- import gnu.javax.crypto.cipher.IBlockCipher;
- import gnu.javax.crypto.prng.UMacGenerator;
- import java.io.UnsupportedEncodingException;
- import java.math.BigInteger;
- import java.security.InvalidKeyException;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * The implementation of the <i>UMAC</i> (Universal Message Authentication
- * Code).
- * <p>
- * The <i>UMAC</i> algorithms described are <i>parameterized</i>. This means
- * that various low-level choices, like the endian convention and the underlying
- * cryptographic primitive, have not been fixed. One must choose values for
- * these parameters before the authentication tag generated by <i>UMAC</i> (for
- * a given message, key, and nonce) becomes fully-defined. In this document we
- * provide two collections of parameter settings, and have named the sets
- * <i>UMAC16</i> and <i>UMAC32</i>. The parameter sets have been chosen based
- * on experimentation and provide good performance on a wide variety of
- * processors. <i>UMAC16</i> is designed to excel on processors which provide
- * small-scale SIMD parallelism of the type found in Intel's MMX and Motorola's
- * AltiVec instruction sets, while <i>UMAC32</i> is designed to do well on
- * processors with good 32- and 64- bit support. <i>UMAC32</i> may take
- * advantage of SIMD parallelism in future processors.
- * <p>
- * <i>UMAC</i> has been designed to allow implementations which accommodate
- * <i>on-line</i> authentication. This means that pieces of the message may be
- * presented to <i>UMAC</i> at different times (but in correct order) and an
- * on-line implementation will be able to process the message correctly without
- * the need to buffer more than a few dozen bytes of the message. For
- * simplicity, the algorithms in this specification are presented as if the
- * entire message being authenticated were available at once.
- * <p>
- * To authenticate a message, <code>Msg</code>, one first applies the
- * universal hash function, resulting in a string which is typically much
- * shorter than the original message. The pseudorandom function is applied to a
- * nonce, and the result is used in the manner of a Vernam cipher: the
- * authentication tag is the xor of the output from the hash function and the
- * output from the pseudorandom function. Thus, an authentication tag is
- * generated as
- * <pre>
- * AuthTag = f(Nonce) xor h(Msg)
- * </pre>
- * <p>
- * Here <code>f</code> is the pseudorandom function shared between the sender
- * and the receiver, and h is a universal hash function shared by the sender and
- * the receiver. In <i>UMAC</i>, a shared key is used to key the pseudorandom
- * function <code>f</code>, and then <code>f</code> is used for both tag
- * generation and internally to generate all of the bits needed by the universal
- * hash function.
- * <p>
- * The universal hash function that we use is called <code>UHASH</code>. It
- * combines several software-optimized algorithms into a multi-layered
- * structure. The algorithm is moderately complex. Some of this complexity comes
- * from extensive speed optimizations.
- * <p>
- * For the pseudorandom function we use the block cipher of the <i>Advanced
- * Encryption Standard</i> (AES).
- * <p>
- * The UMAC32 parameters, considered in this implementation are:
- * <pre>
- * UMAC32
- * ------
- * WORD-LEN 4
- * UMAC-OUTPUT-LEN 8
- * L1-KEY-LEN 1024
- * UMAC-KEY-LEN 16
- * ENDIAN-FAVORITE BIG *
- * L1-OPERATIONS-SIGN UNSIGNED
- * </pre>
- * <p>
- * Please note that this UMAC32 differs from the one described in the paper by
- * the <i>ENDIAN-FAVORITE</i> value.
- * <p>
- * References:
- * <ol>
- * <li><a href="http://www.ietf.org/internet-drafts/draft-krovetz-umac-01.txt">
- * UMAC</a>: Message Authentication Code using Universal Hashing.<br>
- * T. Krovetz, J. Black, S. Halevi, A. Hevia, H. Krawczyk, and P. Rogaway.</li>
- * </ol>
- */
- public class UMac32
- extends BaseMac
- {
- /**
- * Property name of the user-supplied <i>Nonce</i>. The value associated to
- * this property name is taken to be a byte array.
- */
- public static final String NONCE_MATERIAL = "gnu.crypto.umac.nonce.material";
- /** Known test vector. */
- // private static final String TV1 = "3E5A0E09198B0F94";
- // private static final String TV1 = "5FD764A6D3A9FD9D";
- // private static final String TV1 = "48658DE1D9A70304";
- private static final String TV1 = "455ED214A6909F20";
- private static final BigInteger MAX_NONCE_ITERATIONS = BigInteger.ONE.shiftLeft(16 * 8);
- // UMAC32 parameters
- static final int OUTPUT_LEN = 8;
- static final int L1_KEY_LEN = 1024;
- static final int KEY_LEN = 16;
- /** caches the result of the correctness test, once executed. */
- private static Boolean valid;
- private byte[] nonce;
- private UHash32 uhash32;
- private BigInteger nonceReuseCount;
- /** The authentication key for this instance. */
- private transient byte[] K;
- /** Trivial 0-arguments constructor. */
- public UMac32()
- {
- super("umac32");
- }
- /**
- * Private constructor for cloning purposes.
- *
- * @param that the instance to clone.
- */
- private UMac32(UMac32 that)
- {
- this();
- if (that.K != null)
- this.K = (byte[]) that.K.clone();
- if (that.nonce != null)
- this.nonce = (byte[]) that.nonce.clone();
- if (that.uhash32 != null)
- this.uhash32 = (UHash32) that.uhash32.clone();
- this.nonceReuseCount = that.nonceReuseCount;
- }
- public Object clone()
- {
- return new UMac32(this);
- }
- public int macSize()
- {
- return OUTPUT_LEN;
- }
- /**
- * Initialising a <i>UMAC</i> instance consists of defining values for the
- * following parameters:
- * <ol>
- * <li>Key Material: as the value of the attribute entry keyed by
- * {@link #MAC_KEY_MATERIAL}. The value is taken to be a byte array
- * containing the user-specified key material. The length of this array,
- * if/when defined SHOULD be exactly equal to {@link #KEY_LEN}.</li>
- * <li>Nonce Material: as the value of the attribute entry keyed by
- * {@link #NONCE_MATERIAL}. The value is taken to be a byte array containing
- * the user-specified nonce material. The length of this array, if/when
- * defined SHOULD be (a) greater than zero, and (b) less or equal to 16 (the
- * size of the AES block).</li>
- * </ol>
- * <p>
- * For convenience, this implementation accepts that not both parameters be
- * always specified.
- * <ul>
- * <li>If the <i>Key Material</i> is specified, but the <i>Nonce Material</i>
- * is not, then this implementation, re-uses the previously set <i>Nonce
- * Material</i> after (a) converting the bytes to an unsigned integer, (b)
- * incrementing the number by one, and (c) converting it back to 16 bytes.</li>
- * <li>If the <i>Nonce Material</i> is specified, but the <i>Key Material</i>
- * is not, then this implementation re-uses the previously set <i>Key Material</i>.
- * </li>
- * </ul>
- * <p>
- * This method throws an exception if no <i>Key Material</i> is specified in
- * the input map, and there is no previously set/defined <i>Key Material</i>
- * (from an earlier invocation of this method). If a <i>Key Material</i> can
- * be used, but no <i>Nonce Material</i> is defined or previously
- * set/defined, then a default value of all-zeroes shall be used.
- *
- * @param attributes one or both of required parameters.
- * @throws InvalidKeyException the key material specified is not of the
- * correct length.
- */
- public void init(Map attributes) throws InvalidKeyException,
- IllegalStateException
- {
- byte[] key = (byte[]) attributes.get(MAC_KEY_MATERIAL);
- byte[] n = (byte[]) attributes.get(NONCE_MATERIAL);
- boolean newKey = (key != null);
- boolean newNonce = (n != null);
- if (newKey)
- {
- if (key.length != KEY_LEN)
- throw new InvalidKeyException("Key length: "
- + String.valueOf(key.length));
- K = key;
- }
- else
- {
- if (K == null)
- throw new InvalidKeyException("Null Key");
- }
- if (newNonce)
- {
- if (n.length < 1 || n.length > 16)
- throw new IllegalArgumentException("Invalid Nonce length: "
- + String.valueOf(n.length));
- if (n.length < 16) // pad with zeroes
- {
- byte[] newN = new byte[16];
- System.arraycopy(n, 0, newN, 0, n.length);
- nonce = newN;
- }
- else
- nonce = n;
- nonceReuseCount = BigInteger.ZERO;
- }
- else if (nonce == null) // use all-0 nonce if 1st time
- {
- nonce = new byte[16];
- nonceReuseCount = BigInteger.ZERO;
- }
- else if (! newKey) // increment nonce if still below max count
- {
- nonceReuseCount = nonceReuseCount.add(BigInteger.ONE);
- if (nonceReuseCount.compareTo(MAX_NONCE_ITERATIONS) >= 0)
- {
- // limit reached. we SHOULD have a key
- throw new InvalidKeyException("Null Key and unusable old Nonce");
- }
- BigInteger N = new BigInteger(1, nonce);
- N = N.add(BigInteger.ONE).mod(MAX_NONCE_ITERATIONS);
- n = N.toByteArray();
- if (n.length == 16)
- nonce = n;
- else if (n.length < 16)
- {
- nonce = new byte[16];
- System.arraycopy(n, 0, nonce, 16 - n.length, n.length);
- }
- else
- {
- nonce = new byte[16];
- System.arraycopy(n, n.length - 16, nonce, 0, 16);
- }
- }
- else // do nothing, re-use old nonce value
- nonceReuseCount = BigInteger.ZERO;
- if (uhash32 == null)
- uhash32 = new UHash32();
- Map map = new HashMap();
- map.put(MAC_KEY_MATERIAL, K);
- uhash32.init(map);
- }
- public void update(byte b)
- {
- uhash32.update(b);
- }
- public void update(byte[] b, int offset, int len)
- {
- uhash32.update(b, offset, len);
- }
- public byte[] digest()
- {
- byte[] result = uhash32.digest();
- byte[] pad = pdf(); // pdf(K, nonce);
- for (int i = 0; i < OUTPUT_LEN; i++)
- result[i] = (byte)(result[i] ^ pad[i]);
- return result;
- }
- public void reset()
- {
- if (uhash32 != null)
- uhash32.reset();
- }
- public boolean selfTest()
- {
- if (valid == null)
- {
- byte[] key;
- try
- {
- key = "abcdefghijklmnop".getBytes("ASCII");
- }
- catch (UnsupportedEncodingException x)
- {
- throw new RuntimeException("ASCII not supported");
- }
- byte[] nonce = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
- UMac32 mac = new UMac32();
- Map attributes = new HashMap();
- attributes.put(MAC_KEY_MATERIAL, key);
- attributes.put(NONCE_MATERIAL, nonce);
- try
- {
- mac.init(attributes);
- }
- catch (InvalidKeyException x)
- {
- x.printStackTrace(System.err);
- return false;
- }
- byte[] data = new byte[128];
- data[0] = (byte) 0x80;
- mac.update(data, 0, 128);
- byte[] result = mac.digest();
- valid = Boolean.valueOf(TV1.equals(Util.toString(result)));
- }
- return valid.booleanValue();
- }
- /**
- * @return byte array of length 8 (or OUTPUT_LEN) bytes.
- */
- private byte[] pdf()
- {
- // Make Nonce 16 bytes by prepending zeroes. done (see init())
- // one AES invocation is enough for more than one PDF invocation
- // number of index bits needed = 1
- // Extract index bits and zero low bits of Nonce
- BigInteger Nonce = new BigInteger(1, nonce);
- int nlowbitsnum = Nonce.testBit(0) ? 1 : 0;
- Nonce = Nonce.clearBit(0);
- // Generate subkey, AES and extract indexed substring
- IRandom kdf = new UMacGenerator();
- Map map = new HashMap();
- map.put(IBlockCipher.KEY_MATERIAL, K);
- map.put(UMacGenerator.INDEX, Integer.valueOf(128));
- kdf.init(map);
- byte[] Kp = new byte[KEY_LEN];
- try
- {
- kdf.nextBytes(Kp, 0, KEY_LEN);
- }
- catch (IllegalStateException x)
- {
- x.printStackTrace(System.err);
- throw new RuntimeException(String.valueOf(x));
- }
- catch (LimitReachedException x)
- {
- x.printStackTrace(System.err);
- throw new RuntimeException(String.valueOf(x));
- }
- IBlockCipher aes = CipherFactory.getInstance(Registry.AES_CIPHER);
- map.put(IBlockCipher.KEY_MATERIAL, Kp);
- try
- {
- aes.init(map);
- }
- catch (InvalidKeyException x)
- {
- x.printStackTrace(System.err);
- throw new RuntimeException(String.valueOf(x));
- }
- catch (IllegalStateException x)
- {
- x.printStackTrace(System.err);
- throw new RuntimeException(String.valueOf(x));
- }
- byte[] T = new byte[16];
- aes.encryptBlock(nonce, 0, T, 0);
- byte[] result = new byte[OUTPUT_LEN];
- System.arraycopy(T, nlowbitsnum, result, 0, OUTPUT_LEN);
- return result;
- }
- }
|