From a014918c0d5fde9d03b30fa190b64cb9d748bce8 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Wed, 7 May 2014 14:47:15 +0000
Subject: [PATCH] Transports: Use constant time method for HMAC verification

---
 .../src/net/i2p/crypto/HMACGenerator.java     |  2 +-
 core/java/src/net/i2p/data/DataHelper.java    | 24 +++++++++++++++++--
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java
index 3a8f96a4a8..e80b259cfc 100644
--- a/core/java/src/net/i2p/crypto/HMACGenerator.java
+++ b/core/java/src/net/i2p/crypto/HMACGenerator.java
@@ -109,7 +109,7 @@ public class HMACGenerator {
         mac.doFinal(rv, 0);
         release(mac);
         
-        boolean eq = DataHelper.eq(rv, 0, origMAC, origMACOffset, origMACLength);
+        boolean eq = DataHelper.eqCT(rv, 0, origMAC, origMACOffset, origMACLength);
         releaseTmp(rv);
         return eq;
     }
diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java
index 12b1783291..9c9cfc27cb 100644
--- a/core/java/src/net/i2p/data/DataHelper.java
+++ b/core/java/src/net/i2p/data/DataHelper.java
@@ -983,6 +983,8 @@ public class DataHelper {
      * This treats (null == null) as true, (null == (!null)) as false, 
      * and unequal length arrays as false.
      *
+     * Variable time.
+     *
      * @return Arrays.equals(lhs, rhs)
      */
     public final static boolean eq(byte lhs[], byte rhs[]) {
@@ -1018,22 +1020,40 @@ public class DataHelper {
 
     /**
      *  Unlike eq(byte[], byte[]), this returns false if either lhs or rhs is null.
-     *  @throws AIOOBE if either array isn't long enough
+     *  Variable time.
+     *
+     *  @throws ArrayIndexOutOfBoundsException if either array isn't long enough
      */
     public final static boolean eq(byte lhs[], int offsetLeft, byte rhs[], int offsetRight, int length) {
         if ( (lhs == null) || (rhs == null) ) return false;
-        if (length <= 0) return true;
         for (int i = 0; i < length; i++) {
             if (lhs[offsetLeft + i] != rhs[offsetRight + i]) 
                 return false;
         }
         return true;
     }
+
+    /**
+     *  Unlike eq(), this throws NPE if either lhs or rhs is null.
+     *  Constant time.
+     *
+     *  @throws NullPointerException if lhs or rhs is null
+     *  @throws ArrayIndexOutOfBoundsException if either array isn't long enough
+     *  @since 0.9.13
+     */
+    public final static boolean eqCT(byte lhs[], int offsetLeft, byte rhs[], int offsetRight, int length) {
+        int r = 0;
+        for (int i = 0; i < length; i++) {
+            r |=  lhs[offsetLeft + i] ^ rhs[offsetRight + i];
+        }
+        return r == 0;
+    }
     
     /**
      *  Big endian compare, treats bytes as unsigned.
      *  Shorter arg is lesser.
      *  Args may be null, null is less than non-null.
+     *  Variable time.
      */
     public final static int compareTo(byte lhs[], byte rhs[]) {
         if ((rhs == null) && (lhs == null)) return 0;
-- 
GitLab