| 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; |
| |
| /** |
| * 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 byte[] inputPad; |
| private byte[] outputPad; |
| |
| private static Hashtable blockLengths; |
| |
| static |
| { |
| blockLengths = new Hashtable(); |
| |
| blockLengths.put("GOST3411", new Integer(32)); |
| |
| blockLengths.put("MD2", new Integer(16)); |
| blockLengths.put("MD4", new Integer(64)); |
| blockLengths.put("MD5", new Integer(64)); |
| |
| blockLengths.put("RIPEMD128", new Integer(64)); |
| blockLengths.put("RIPEMD160", new Integer(64)); |
| |
| blockLengths.put("SHA-1", new Integer(64)); |
| blockLengths.put("SHA-224", new Integer(64)); |
| blockLengths.put("SHA-256", new Integer(64)); |
| blockLengths.put("SHA-384", new Integer(128)); |
| blockLengths.put("SHA-512", new Integer(128)); |
| |
| blockLengths.put("Tiger", new Integer(64)); |
| blockLengths.put("Whirlpool", new Integer(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; |
| digestSize = digest.getDigestSize(); |
| |
| this.blockLength = byteLength; |
| |
| inputPad = new byte[blockLength]; |
| outputPad = new byte[blockLength]; |
| } |
| |
| public String getAlgorithmName() |
| { |
| return digest.getAlgorithmName() + "/HMAC"; |
| } |
| |
| public Digest getUnderlyingDigest() |
| { |
| return digest; |
| } |
| |
| public void init( |
| CipherParameters params) |
| { |
| digest.reset(); |
| |
| byte[] key = ((KeyParameter)params).getKey(); |
| |
| if (key.length > blockLength) |
| { |
| digest.update(key, 0, key.length); |
| digest.doFinal(inputPad, 0); |
| for (int i = digestSize; i < inputPad.length; i++) |
| { |
| inputPad[i] = 0; |
| } |
| } |
| else |
| { |
| System.arraycopy(key, 0, inputPad, 0, key.length); |
| for (int i = key.length; i < inputPad.length; i++) |
| { |
| inputPad[i] = 0; |
| } |
| } |
| |
| outputPad = new byte[inputPad.length]; |
| System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length); |
| |
| for (int i = 0; i < inputPad.length; i++) |
| { |
| inputPad[i] ^= IPAD; |
| } |
| |
| for (int i = 0; i < outputPad.length; i++) |
| { |
| outputPad[i] ^= OPAD; |
| } |
| |
| digest.update(inputPad, 0, inputPad.length); |
| } |
| |
| 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) |
| { |
| byte[] tmp = new byte[digestSize]; |
| digest.doFinal(tmp, 0); |
| |
| digest.update(outputPad, 0, outputPad.length); |
| digest.update(tmp, 0, tmp.length); |
| |
| int len = digest.doFinal(out, outOff); |
| |
| reset(); |
| |
| return len; |
| } |
| |
| /** |
| * Reset the mac generator. |
| */ |
| public void reset() |
| { |
| /* |
| * reset the underlying digest. |
| */ |
| digest.reset(); |
| |
| /* |
| * reinitialize the digest. |
| */ |
| digest.update(inputPad, 0, inputPad.length); |
| } |
| } |