// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS.utils;
import java.util.Arrays;
import java.security.*;
/**
* An implementation of the HMAC message authentication code.
*
* @author Brian Wellington
*/
public class HMAC {
private MessageDigest digest;
private int blockLength;
private byte [] ipad, opad;
private static final byte IPAD = 0x36;
private static final byte OPAD = 0x5c;
private void
init(byte [] key) {
int i;
if (key.length > blockLength) {
key = digest.digest(key);
digest.reset();
}
ipad = new byte[blockLength];
opad = new byte[blockLength];
for (i = 0; i < key.length; i++) {
ipad[i] = (byte) (key[i] ^ IPAD);
opad[i] = (byte) (key[i] ^ OPAD);
}
for (; i < blockLength; i++) {
ipad[i] = IPAD;
opad[i] = OPAD;
}
digest.update(ipad);
}
/**
* Creates a new HMAC instance
* @param digest The message digest object.
* @param blockLength The block length of the message digest.
* @param key The secret key
*/
public
HMAC(MessageDigest digest, int blockLength, byte [] key) {
digest.reset();
this.digest = digest;
this.blockLength = blockLength;
init(key);
}
/**
* Creates a new HMAC instance
* @param digestName The name of the message digest function.
* @param blockLength The block length of the message digest.
* @param key The secret key.
*/
public
HMAC(String digestName, int blockLength, byte [] key) {
try {
digest = MessageDigest.getInstance(digestName);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("unknown digest algorithm "
+ digestName);
}
this.blockLength = blockLength;
init(key);
}
/**
* Creates a new HMAC instance
* @param digest The message digest object.
* @param key The secret key
* @deprecated won't work with digests using a padding length other than 64;
* use {@code HMAC(MessageDigest digest, int blockLength,
* byte [] key)} instead.
* @see HMAC#HMAC(MessageDigest digest, int blockLength, byte [] key)
*/
public
HMAC(MessageDigest digest, byte [] key) {
this(digest, 64, key);
}
/**
* Creates a new HMAC instance
* @param digestName The name of the message digest function.
* @param key The secret key.
* @deprecated won't work with digests using a padding length other than 64;
* use {@code HMAC(String digestName, int blockLength, byte [] key)}
* instead
* @see HMAC#HMAC(String digestName, int blockLength, byte [] key)
*/
public
HMAC(String digestName, byte [] key) {
this(digestName, 64, key);
}
/**
* Adds data to the current hash
* @param b The data
* @param offset The index at which to start adding to the hash
* @param length The number of bytes to hash
*/
public void
update(byte [] b, int offset, int length) {
digest.update(b, offset, length);
}
/**
* Adds data to the current hash
* @param b The data
*/
public void
update(byte [] b) {
digest.update(b);
}
/**
* Signs the data (computes the secure hash)
* @return An array with the signature
*/
public byte []
sign() {
byte [] output = digest.digest();
digest.reset();
digest.update(opad);
return digest.digest(output);
}
/**
* Verifies the data (computes the secure hash and compares it to the input)
* @param signature The signature to compare against
* @return true if the signature matches, false otherwise
*/
public boolean
verify(byte [] signature) {
return verify(signature, false);
}
/**
* Verifies the data (computes the secure hash and compares it to the input)
* @param signature The signature to compare against
* @param truncation_ok If true, the signature may be truncated; only the
* number of bytes in the provided signature are compared.
* @return true if the signature matches, false otherwise
*/
public boolean
verify(byte [] signature, boolean truncation_ok) {
byte [] expected = sign();
if (truncation_ok && signature.length < expected.length) {
byte [] truncated = new byte[signature.length];
System.arraycopy(expected, 0, truncated, 0, truncated.length);
expected = truncated;
}
return Arrays.equals(signature, expected);
}
/**
* Resets the HMAC object for further use
*/
public void
clear() {
digest.reset();
digest.update(ipad);
}
/**
* Returns the length of the digest.
*/
public int
digestLength() {
return digest.getDigestLength();
}
}