/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.crypt;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import freenet.support.HexUtil;
/**
* Implements the HMAC Keyed Message Authentication function, as described
* in the draft FIPS standard.
*/
public class HMAC_legacy {
protected static final int B = 64;
protected static byte[] ipad = new byte[B];
protected static byte[] opad = new byte[B];
static {
for(int i = 0; i < B; i++) {
ipad[i] = (byte) 0x36;
opad[i] = (byte) 0x5c;
}
}
protected MessageDigest d;
public HMAC_legacy(MessageDigest md) {
this.d = md;
}
public boolean verify(byte[] K, byte[] text, byte[] mac) {
byte[] mac2 = mac(K, text, mac.length);
// this is constant-time; DO NOT 'optimize'
return MessageDigest.isEqual(mac, mac2);
}
public byte[] mac(byte[] K, byte[] text, int macbytes) {
byte[] K0 = null;
if(K.length == B) // Step 1
K0 = K;
else {
// Step 2
if(K.length > B)
K0 = K = Util.hashBytes(d, K);
if(K.length < B) { // Step 3
K0 = new byte[B];
System.arraycopy(K, 0, K0, 0, K.length);
}
}
// Step 4
byte[] IS1 = Util.xor(K0, ipad);
// Step 5/6
d.update(IS1);
d.update(text);
IS1 = d.digest();
// Step 7
byte[] IS2 = Util.xor(K0, opad);
// Step 8/9
d.update(IS2);
d.update(IS1);
IS1 = d.digest();
// Step 10
if(macbytes == IS1.length)
return IS1;
else {
byte[] rv = new byte[macbytes];
System.arraycopy(IS1, 0, rv, 0, Math.min(rv.length, IS1.length));
return rv;
}
}
public static void main(String[] args) throws UnsupportedEncodingException {
HMAC_legacy s = null;
try {
s = new HMAC_legacy(MessageDigest.getInstance("SHA1"));
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] key = new byte[20];
System.err.println("20x0b, 'Hi There':");
byte[] text;
text = "Hi There".getBytes("UTF-8");
for(int i = 0; i < key.length; i++)
key[i] = (byte) 0x0b;
byte[] mv = s.mac(key, text, 20);
System.out.println(HexUtil.bytesToHex(mv, 0, mv.length));
System.err.println("20xaa, 50xdd:");
for(int i = 0; i < key.length; i++)
key[i] = (byte) 0xaa;
text = new byte[50];
for(int i = 0; i < text.length; i++)
text[i] = (byte) 0xdd;
mv = s.mac(key, text, 20);
System.out.println(HexUtil.bytesToHex(mv, 0, mv.length));
key = new byte[25];
System.err.println("25x[i+1], 50xcd:");
for(int i = 0; i < key.length; i++)
key[i] = (byte) (i + 1);
for(int i = 0; i < text.length; i++)
text[i] = (byte) 0xcd;
mv = s.mac(key, text, 20);
System.out.println(HexUtil.bytesToHex(mv, 0, mv.length));
key = new byte[20];
System.err.println("20x0c, 'Test With Truncation':");
for(int i = 0; i < key.length; i++)
key[i] = (byte) 0x0c;
text = "Test With Truncation".getBytes("UTF-8");
mv = s.mac(key, text, 20);
System.out.println(HexUtil.bytesToHex(mv, 0, mv.length));
mv = s.mac(key, text, 12);
System.out.println(HexUtil.bytesToHex(mv, 0, mv.length));
}
public static byte[] macWithSHA256(byte[] K, byte[] text, int macbytes) {
MessageDigest sha256 = null;
try {
sha256 = SHA256.getMessageDigest();
HMAC_legacy hash = new HMAC_legacy(sha256);
return hash.mac(K, text, macbytes);
} finally {
if(sha256 != null)
SHA256.returnMessageDigest(sha256);
}
}
public static boolean verifyWithSHA256(byte[] K, byte[] text, byte[] mac) {
MessageDigest sha256 = null;
try {
sha256 = SHA256.getMessageDigest();
HMAC_legacy hash = new HMAC_legacy(sha256);
return hash.verify(K, text, mac);
} finally {
if(sha256 != null)
SHA256.returnMessageDigest(sha256);
}
}
}