From b9537f49750dd09bdb2e4ef5b679d1a7229fc190 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 1 Jan 2016 16:41:23 +0100 Subject: [PATCH] Add a lousy benchmark to show that it's not slower --- test/freenet/crypt/HMACTest.java | 65 +++++++++++- test/freenet/crypt/HMAC_legacy.java | 154 ++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 test/freenet/crypt/HMAC_legacy.java diff --git a/test/freenet/crypt/HMACTest.java b/test/freenet/crypt/HMACTest.java index 5704c5e70c3..d959ccaa923 100644 --- a/test/freenet/crypt/HMACTest.java +++ b/test/freenet/crypt/HMACTest.java @@ -2,19 +2,29 @@ import junit.framework.TestCase; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Hex; import java.security.Security; import java.util.Random; +import freenet.support.TestProperty; +import freenet.support.TimeUtil; + public class HMACTest extends TestCase { Random random; // RFC4868 2.7.2.1 SHA256 Authentication Test Vector - static byte[] plaintext = "Hi There".getBytes(); - static byte[] knownKey = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); - static byte[] knownSHA256 = Hex.decode("198a607eb44bfbc69903a0f1cf2bbdc5ba0aa3f3d9ae3c1c7a3b1696a0b68cf7"); + static byte[] plaintext = "Hi There".getBytes(); + static byte[] + knownKey = + Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); + static byte[] + knownSHA256 = + Hex.decode("198a607eb44bfbc69903a0f1cf2bbdc5ba0aa3f3d9ae3c1c7a3b1696a0b68cf7"); protected void setUp() throws Exception { super.setUp(); @@ -57,7 +67,52 @@ public void testWrongKeySize() { } public void testKnownVectors() { - byte[] hmac = HMAC.macWithSHA256(knownKey, plaintext); - assertEquals(Hex.toHexString(hmac),Hex.toHexString(knownSHA256)); + byte[] hmac = HMAC.macWithSHA256(knownKey, plaintext); + assertEquals(Hex.toHexString(hmac), Hex.toHexString(knownSHA256)); + } + + // ant -Dtest.skip=false -Dtest.class=freenet.crypt.HMACTest -Dtest.benchmark=true unit + public void testBenchmark() { + if (!TestProperty.BENCHMARK) { + return; + } + + int count = 0; + int ITERATIONS = 10000000; + System.out.println("We're getting ready to benchmark HMACs"); + long t1 = System.currentTimeMillis(); + for (int i = 0; i < ITERATIONS; i++) { + byte[] r1 = HMAC_legacy.macWithSHA256(knownKey, plaintext, 32); + for (int j = 0; j < r1.length; j++) { + count += r1[j]; + } + } + long legacyLength = System.currentTimeMillis() - t1; + + t1 = System.currentTimeMillis(); + for (int i = 0; i < ITERATIONS; i++) { + byte[] r1 = HMAC.macWithSHA256(knownKey, plaintext); + for (int j = 0; j < r1.length; j++) { + count += r1[j]; + } + } + long currentLength = System.currentTimeMillis() - t1; + + t1 = System.currentTimeMillis(); + for (int i = 0; i < ITERATIONS; i++) { + byte[] r1 = new byte[32]; + HMac hmac = new HMac(new SHA256Digest()); + KeyParameter kp = new KeyParameter(knownKey); + hmac.init(kp); + hmac.update(plaintext, 0, plaintext.length); + hmac.doFinal(r1, 0); + for (int j = 0; j < r1.length; j++) { + count += r1[j]; + } + } + long BCLength = System.currentTimeMillis() - t1; + System.out.println("Legacy HMAC took " + TimeUtil.formatTime(legacyLength, 6, true)); + System.out.println("Current HMAC took " + TimeUtil.formatTime(legacyLength, 6, true)); + System.out.println("BC HMAC took " + TimeUtil.formatTime(BCLength, 6, true)); } } diff --git a/test/freenet/crypt/HMAC_legacy.java b/test/freenet/crypt/HMAC_legacy.java new file mode 100644 index 00000000000..284b6525367 --- /dev/null +++ b/test/freenet/crypt/HMAC_legacy.java @@ -0,0 +1,154 @@ +/* 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); + } + } +}