mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			264 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Java
		
	
	
	
/* HMac.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.hash.IMessageDigest;
 | 
						|
import gnu.java.security.hash.MD5;
 | 
						|
import gnu.java.security.util.Util;
 | 
						|
 | 
						|
import java.security.InvalidKeyException;
 | 
						|
import java.util.HashMap;
 | 
						|
import java.util.Map;
 | 
						|
 | 
						|
/**
 | 
						|
 * The implementation of the <i>HMAC</i> (Keyed-Hash Message Authentication
 | 
						|
 * Code).
 | 
						|
 * <p>
 | 
						|
 * <i>HMAC</i> can be used in combination with any iterated cryptographic hash
 | 
						|
 * function. <i>HMAC</i> also uses a <i>secret key</i> for calculation and
 | 
						|
 * verification of the message authentication values. The main goals behind this
 | 
						|
 * construction are:
 | 
						|
 * <ul>
 | 
						|
 * <li>To use, without modifications, available hash functions. In particular,
 | 
						|
 * hash functions that perform well in software, and for which code is freely
 | 
						|
 * and widely available.</li>
 | 
						|
 * <li>To preserve the original performance of the hash function without
 | 
						|
 * incurring a significant degradation.</li>
 | 
						|
 * <li>To use and handle keys in a simple way.</li>
 | 
						|
 * <li>To have a well understood cryptographic analysis of the strength of the
 | 
						|
 * authentication mechanism based on reasonable assumptions on the underlying
 | 
						|
 * hash function.</li>
 | 
						|
 * <li>To allow for easy replaceability of the underlying hash function in case
 | 
						|
 * that faster or more secure hash functions are found or required.</li>
 | 
						|
 * </ul>
 | 
						|
 * <p>
 | 
						|
 * References:
 | 
						|
 * <ol>
 | 
						|
 * <li><a href="http://www.ietf.org/rfc/rfc-2104.txt">RFC 2104</a>HMAC:
 | 
						|
 * Keyed-Hashing for Message Authentication.<br>
 | 
						|
 * H. Krawczyk, M. Bellare, and R. Canetti.</li>
 | 
						|
 * </ol>
 | 
						|
 */
 | 
						|
public class HMac
 | 
						|
    extends BaseMac
 | 
						|
    implements Cloneable
 | 
						|
{
 | 
						|
  public static final String USE_WITH_PKCS5_V2 = "gnu.crypto.hmac.pkcs5";
 | 
						|
  private static final byte IPAD_BYTE = 0x36;
 | 
						|
  private static final byte OPAD_BYTE = 0x5C;
 | 
						|
  /** caches the result of the correctness test, once executed. */
 | 
						|
  private static Boolean valid;
 | 
						|
  protected int macSize;
 | 
						|
  protected int blockSize;
 | 
						|
  protected IMessageDigest ipadHash;
 | 
						|
  protected IMessageDigest opadHash;
 | 
						|
  protected byte[] ipad;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Trivial constructor for use by concrete subclasses.
 | 
						|
   *
 | 
						|
   * @param underlyingHash the underlying hash algorithm instance.
 | 
						|
   */
 | 
						|
  protected HMac(IMessageDigest underlyingHash)
 | 
						|
  {
 | 
						|
    super(Registry.HMAC_NAME_PREFIX + underlyingHash.name(), underlyingHash);
 | 
						|
 | 
						|
    this.blockSize = underlyingHash.blockSize();
 | 
						|
    this.macSize = underlyingHash.hashSize();
 | 
						|
    ipadHash = opadHash = null;
 | 
						|
  }
 | 
						|
 | 
						|
  public Object clone() throws CloneNotSupportedException
 | 
						|
  {
 | 
						|
    HMac result = (HMac) super.clone();
 | 
						|
    if (this.ipadHash != null)
 | 
						|
      result.ipadHash = (IMessageDigest) this.ipadHash.clone();
 | 
						|
    if (this.opadHash != null)
 | 
						|
      result.opadHash = (IMessageDigest) this.opadHash.clone();
 | 
						|
    if (this.ipad != null)
 | 
						|
      result.ipad = (byte[]) this.ipad.clone();
 | 
						|
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  public void init(Map attributes) throws InvalidKeyException,
 | 
						|
      IllegalStateException
 | 
						|
  {
 | 
						|
    Integer ts = (Integer) attributes.get(TRUNCATED_SIZE);
 | 
						|
    truncatedSize = (ts == null ? macSize : ts.intValue());
 | 
						|
    if (truncatedSize < (macSize / 2))
 | 
						|
      throw new IllegalArgumentException("Truncated size too small");
 | 
						|
    else if (truncatedSize < 10)
 | 
						|
      throw new IllegalArgumentException("Truncated size less than 80 bits");
 | 
						|
 | 
						|
    // we dont use/save the key outside this method
 | 
						|
    byte[] K = (byte[]) attributes.get(MAC_KEY_MATERIAL);
 | 
						|
    if (K == null)
 | 
						|
      { // take it as an indication to re-use previous key if set
 | 
						|
        if (ipadHash == null)
 | 
						|
          throw new InvalidKeyException("Null key");
 | 
						|
        // we already went through the motions; ie. up to step #4.  re-use
 | 
						|
        underlyingHash = (IMessageDigest) ipadHash.clone();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
    // for HMACs used in key-derivation functions (e.g. PBKDF2) the key material
 | 
						|
    // need not be >= the (output) block size of the underlying algorithm
 | 
						|
    Boolean pkcs5 = (Boolean) attributes.get(USE_WITH_PKCS5_V2);
 | 
						|
    if (pkcs5 == null)
 | 
						|
      pkcs5 = Boolean.FALSE;
 | 
						|
    if (K.length < macSize && ! pkcs5.booleanValue())
 | 
						|
      throw new InvalidKeyException("Key too short");
 | 
						|
 | 
						|
    if (K.length > blockSize)
 | 
						|
      {
 | 
						|
        // (0) replace K with HASH(K) if K is larger than the hash's block size.
 | 
						|
        //     Then pad with zeros until it is the correct size (the next `if').
 | 
						|
        underlyingHash.update(K, 0, K.length);
 | 
						|
        K = underlyingHash.digest();
 | 
						|
      }
 | 
						|
    if (K.length < blockSize)
 | 
						|
      {
 | 
						|
        // (1) append zeros to the end of K to create a B byte string (e.g., if
 | 
						|
        //     K is of length 20 bytes and B=64, then K will be appended with 44
 | 
						|
        //     zero bytes 0x00)
 | 
						|
        int limit = (K.length > blockSize) ? blockSize : K.length;
 | 
						|
        byte[] newK = new byte[blockSize];
 | 
						|
        System.arraycopy(K, 0, newK, 0, limit);
 | 
						|
        K = newK;
 | 
						|
      }
 | 
						|
    underlyingHash.reset();
 | 
						|
    opadHash = (IMessageDigest) underlyingHash.clone();
 | 
						|
    if (ipad == null)
 | 
						|
      ipad = new byte[blockSize];
 | 
						|
    // (2) XOR (bitwise exclusive-OR) the B byte string computed in step (1)
 | 
						|
    //     with ipad
 | 
						|
    // (3) append the stream of data 'text' to the B byte string resulting from
 | 
						|
    //     step (2)
 | 
						|
    // (4) apply H to the stream generated in step (3)
 | 
						|
    for (int i = 0; i < blockSize; i++)
 | 
						|
      ipad[i] = (byte)(K[i] ^ IPAD_BYTE);
 | 
						|
    for (int i = 0; i < blockSize; i++)
 | 
						|
      opadHash.update((byte)(K[i] ^ OPAD_BYTE));
 | 
						|
    underlyingHash.update(ipad, 0, blockSize);
 | 
						|
    ipadHash = (IMessageDigest) underlyingHash.clone();
 | 
						|
    K = null;
 | 
						|
  }
 | 
						|
 | 
						|
  public void reset()
 | 
						|
  {
 | 
						|
    super.reset();
 | 
						|
    if (ipad != null)
 | 
						|
      {
 | 
						|
        underlyingHash.update(ipad, 0, blockSize);
 | 
						|
        ipadHash = (IMessageDigest) underlyingHash.clone();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  public byte[] digest()
 | 
						|
  {
 | 
						|
    if (ipadHash == null)
 | 
						|
      throw new IllegalStateException("HMAC not initialised");
 | 
						|
    byte[] out = underlyingHash.digest();
 | 
						|
    // (5) XOR (bitwise exclusive-OR) the B byte string computed in step (1)
 | 
						|
    //     with opad
 | 
						|
    underlyingHash = (IMessageDigest) opadHash.clone();
 | 
						|
    // (6) append the H result from step (4) to the B byte string resulting from
 | 
						|
    //     step (5)
 | 
						|
    underlyingHash.update(out, 0, macSize);
 | 
						|
    // (7) apply H to the stream generated in step (6) and output the result
 | 
						|
    out = underlyingHash.digest(); // which also resets the underlying hash
 | 
						|
    // truncate and return
 | 
						|
    if (truncatedSize == macSize)
 | 
						|
      return out;
 | 
						|
    byte[] result = new byte[truncatedSize];
 | 
						|
    System.arraycopy(out, 0, result, 0, truncatedSize);
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean selfTest()
 | 
						|
  {
 | 
						|
    if (valid == null)
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            IMac mac = new HMac(new MD5()); // use rfc-2104 test vectors
 | 
						|
            String tv1 = "9294727A3638BB1C13F48EF8158BFC9D";
 | 
						|
            String tv3 = "56BE34521D144C88DBB8C733F0E8B3F6";
 | 
						|
            byte[] k1 = new byte[] {
 | 
						|
                0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
 | 
						|
                0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B };
 | 
						|
            byte[] k3 = new byte[] {
 | 
						|
                (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
 | 
						|
                (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
 | 
						|
                (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
 | 
						|
                (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA };
 | 
						|
            byte[] data = new byte[50];
 | 
						|
            for (int i = 0; i < 50;)
 | 
						|
              data[i++] = (byte) 0xDD;
 | 
						|
 | 
						|
            HashMap map = new HashMap();
 | 
						|
            // test vector #1
 | 
						|
            map.put(MAC_KEY_MATERIAL, k1);
 | 
						|
            mac.init(map);
 | 
						|
            mac.update("Hi There".getBytes("ASCII"), 0, 8);
 | 
						|
            if (! tv1.equals(Util.toString(mac.digest())))
 | 
						|
              valid = Boolean.FALSE;
 | 
						|
 | 
						|
            // test #2 is not used since it causes a "Key too short" exception
 | 
						|
 | 
						|
            // test vector #3
 | 
						|
            map.put(MAC_KEY_MATERIAL, k3);
 | 
						|
            mac.init(map);
 | 
						|
            mac.update(data, 0, 50);
 | 
						|
            if (! tv3.equals(Util.toString(mac.digest())))
 | 
						|
              valid = Boolean.FALSE;
 | 
						|
            valid = Boolean.TRUE;
 | 
						|
          }
 | 
						|
        catch (Exception x)
 | 
						|
          {
 | 
						|
            x.printStackTrace(System.err);
 | 
						|
            valid = Boolean.FALSE;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return valid.booleanValue();
 | 
						|
  }
 | 
						|
}
 |