mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			419 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			419 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
/* 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;
 | 
						|
  }
 | 
						|
}
 |