package org.bouncycastle.crypto.macs; import java.util.Hashtable; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Memoable; /** * HMAC implementation based on RFC2104 * * H(K XOR opad, H(K XOR ipad, text)) */ public class HMac implements Mac { private final static byte IPAD = (byte)0x36; private final static byte OPAD = (byte)0x5C; private Digest digest; private int digestSize; private int blockLength; private Memoable ipadState; private Memoable opadState; private byte[] inputPad; private byte[] outputBuf; private static Hashtable blockLengths; static { blockLengths = new Hashtable(); blockLengths.put("GOST3411", Integers.valueOf(32)); blockLengths.put("MD2", Integers.valueOf(16)); blockLengths.put("MD4", Integers.valueOf(64)); blockLengths.put("MD5", Integers.valueOf(64)); blockLengths.put("RIPEMD128", Integers.valueOf(64)); blockLengths.put("RIPEMD160", Integers.valueOf(64)); blockLengths.put("SHA-1", Integers.valueOf(64)); blockLengths.put("SHA-224", Integers.valueOf(64)); blockLengths.put("SHA-256", Integers.valueOf(64)); blockLengths.put("SHA-384", Integers.valueOf(128)); blockLengths.put("SHA-512", Integers.valueOf(128)); blockLengths.put("Tiger", Integers.valueOf(64)); blockLengths.put("Whirlpool", Integers.valueOf(64)); } private static int getByteLength( Digest digest) { if (digest instanceof ExtendedDigest) { return ((ExtendedDigest)digest).getByteLength(); } Integer b = (Integer)blockLengths.get(digest.getAlgorithmName()); if (b == null) { throw new IllegalArgumentException("unknown digest passed: " + digest.getAlgorithmName()); } return b.intValue(); } /** * Base constructor for one of the standard digest algorithms that the * byteLength of the algorithm is know for. * * @param digest the digest. */ public HMac( Digest digest) { this(digest, getByteLength(digest)); } private HMac( Digest digest, int byteLength) { this.digest = digest; this.digestSize = digest.getDigestSize(); this.blockLength = byteLength; this.inputPad = new byte[blockLength]; this.outputBuf = new byte[blockLength + digestSize]; } public String getAlgorithmName() { return digest.getAlgorithmName() + "/HMAC"; } public Digest getUnderlyingDigest() { return digest; } public void init( CipherParameters params) { digest.reset(); byte[] key = ((KeyParameter)params).getKey(); int keyLength = key.length; if (keyLength > blockLength) { digest.update(key, 0, keyLength); digest.doFinal(inputPad, 0); keyLength = digestSize; } else { System.arraycopy(key, 0, inputPad, 0, keyLength); } for (int i = keyLength; i < inputPad.length; i++) { inputPad[i] = 0; } System.arraycopy(inputPad, 0, outputBuf, 0, blockLength); xorPad(inputPad, blockLength, IPAD); xorPad(outputBuf, blockLength, OPAD); if (digest instanceof Memoable) { opadState = ((Memoable)digest).copy(); ((Digest)opadState).update(outputBuf, 0, blockLength); } digest.update(inputPad, 0, inputPad.length); if (digest instanceof Memoable) { ipadState = ((Memoable)digest).copy(); } } public int getMacSize() { return digestSize; } public void update( byte in) { digest.update(in); } public void update( byte[] in, int inOff, int len) { digest.update(in, inOff, len); } public int doFinal( byte[] out, int outOff) { digest.doFinal(outputBuf, blockLength); if (opadState != null) { ((Memoable)digest).reset(opadState); digest.update(outputBuf, blockLength, digest.getDigestSize()); } else { digest.update(outputBuf, 0, outputBuf.length); } int len = digest.doFinal(out, outOff); for (int i = blockLength; i < outputBuf.length; i++) { outputBuf[i] = 0; } if (ipadState != null) { ((Memoable)digest).reset(ipadState); } else { digest.update(inputPad, 0, inputPad.length); } return len; } /** * Reset the mac generator. */ public void reset() { /* * reset the underlying digest. */ digest.reset(); /* * reinitialize the digest. */ digest.update(inputPad, 0, inputPad.length); } private static void xorPad(byte[] pad, int len, byte n) { for (int i = 0; i < len; ++i) { pad[i] ^= n; } } }