123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /* 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();
- }
- }
|