// $Id: HMAC.java 81 2007-02-25 12:45:17Z tp $ package fr.cryptohash; /** * <p>This class implements the HMAC message authentication algorithm, * under the <code>Digest</code> API, using the * <code>DigestEngine</code> class. HMAC is defined in RFC 2104 (also * FIPS 198a). This implementation uses an underlying digest algorithm, * provided as parameter to the constructor.</p> * * <pre> * ==========================(LICENSE BEGIN)============================ * * Copyright (c) 2007 Projet RNRT SAPHIR * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * ===========================(LICENSE END)============================= * </pre> * * @version $Revision: 81 $ * @author Thomas Pornin <thomas.pornin@cryptolog.com> */ public class HMAC extends DigestEngine { /** * Build the object. The provided digest algorithm will be used * internally; it MUST NOT be directly accessed afterwards. The * <code>key</code> array holds the MAC key; the key is copied * internally, which means that the caller may modify the * <code>key</code> array afterwards. * * @param dig the underlying hash function * @param key the MAC key */ public HMAC(Digest dig, byte[] key) { dig.reset(); this.dig = dig; int B = dig.getBlockLength(); byte[] keyB = new byte[B]; int len = key.length; if (len > B) { key = dig.digest(key); len = key.length; if (len > B) len = B; } System.arraycopy(key, 0, keyB, 0, key.length); /* * Newly created arrays are guaranteed filled with zeroes, * hence the key padding is already done. */ processKey(keyB); outputLength = -1; tmpOut = new byte[dig.getDigestLength()]; reset(); } /** * Build the object. The provided digest algorithm will be used * internally; it MUST NOT be directly accessed afterwards. The * <code>key</code> array holds the MAC key; the key is copied * internally, which means that the caller may modify the * <code>key</code> array afterwards. The provided output length * is the maximum HMAC output length, in bytes: the digest * output will be truncated, if needed, to respect that limit. * * @param dig the underlying hash function * @param key the MAC key * @param outputLength the HMAC output length (in bytes) */ public HMAC(Digest dig, byte[] key, int outputLength) { this(dig, key); if (outputLength < dig.getDigestLength()) this.outputLength = outputLength; } /** * Internal constructor, used for cloning. The key is referenced, * not copied. * * @param dig the digest * @param kipad the (internal) ipad key * @param kopad the (internal) opad key * @param outputLength the output length, or -1 */ private HMAC(Digest dig, byte[] kipad, byte[] kopad, int outputLength) { this.dig = dig; this.kipad = kipad; this.kopad = kopad; this.outputLength = outputLength; tmpOut = new byte[dig.getDigestLength()]; } private Digest dig; private byte[] kipad, kopad; private int outputLength; private byte[] tmpOut; private void processKey(byte[] keyB) { int B = keyB.length; kipad = new byte[B]; kopad = new byte[B]; for (int i = 0; i < B; i ++) { int x = keyB[i]; kipad[i] = (byte)(x ^ 0x36); kopad[i] = (byte)(x ^ 0x5C); } } /** @see Digest */ public Digest copy() { HMAC h = new HMAC(dig.copy(), kipad, kopad, outputLength); return copyState(h); } /** @see Digest */ public int getDigestLength() { /* * At construction time, outputLength is first set to 0, * which means that this method will return 0, which is * appropriate since at that time "dig" has not yet been * set. */ return outputLength < 0 ? dig.getDigestLength() : outputLength; } /** @see Digest */ public int getBlockLength() { /* * Internal block length is not defined for HMAC, which * is not, stricto-sensu, an iterated hash function. * The value 64 should provide correct buffering. Do NOT * change this value without checking doPadding(). */ return 64; } /** @see DigestEngine */ protected void engineReset() { dig.reset(); dig.update(kipad); } private int onlyThis = 0; private static final byte[] zeroPad = new byte[64]; /** @see DigestEngine */ protected void processBlock(byte[] data) { if (onlyThis > 0) { dig.update(data, 0, onlyThis); onlyThis = 0; } else { dig.update(data); } } /** @see DigestEngine */ protected void doPadding(byte[] output, int outputOffset) { /* * This is slightly ugly... we need to get the still * buffered data, but the only way to get it from * DigestEngine is to input some more bytes and wait * for the processBlock() call. We set a variable * with the count of actual data bytes, so that * processBlock() knows what to do. */ onlyThis = flush(); if (onlyThis > 0) update(zeroPad, 0, 64 - onlyThis); int olen = tmpOut.length; dig.digest(tmpOut, 0, olen); dig.update(kopad); dig.update(tmpOut); dig.digest(tmpOut, 0, olen); if (outputLength >= 0) olen = outputLength; System.arraycopy(tmpOut, 0, output, outputOffset, olen); } /** @see DigestEngine */ protected void doInit() { /* * Empty: we do not want to do anything here because * it would prevent correct cloning. The initialization * job is done in the constructor. */ } }