Ver código fonte

HADOOP-11343. Overflow is not properly handled in caclulating final iv for AES CTR. Contributed by Jerry Chen.

(cherry picked from commit 0707e4eca906552c960e3b8c4e20d9913145eca6)
(cherry picked from commit dabdd2d746d1e1194c124c5c7fe73fcc025e78d2)
Andrew Wang 10 anos atrás
pai
commit
65180617fb

+ 3 - 0
hadoop-common-project/hadoop-common/CHANGES.txt

@@ -21,6 +21,9 @@ Release 2.6.1 - UNRELEASED
     HADOOP-11333. Fix deadlock in DomainSocketWatcher when the notification
     pipe is full (zhaoyunjiong via cmccabe)
 
+    HADOOP-11343. Overflow is not properly handled in caclulating final iv for
+    AES CTR. (Jerry Chen via wang)
+
 Release 2.6.0 - 2014-11-18
 
   INCOMPATIBLE CHANGES

+ 12 - 15
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/AesCtrCryptoCodec.java

@@ -33,7 +33,6 @@ public abstract class AesCtrCryptoCodec extends CryptoCodec {
    * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
    */
   private static final int AES_BLOCK_SIZE = SUITE.getAlgorithmBlockSize();
-  private static final int CTR_OFFSET = 8;
 
   @Override
   public CipherSuite getCipherSuite() {
@@ -48,20 +47,18 @@ public abstract class AesCtrCryptoCodec extends CryptoCodec {
   public void calculateIV(byte[] initIV, long counter, byte[] IV) {
     Preconditions.checkArgument(initIV.length == AES_BLOCK_SIZE);
     Preconditions.checkArgument(IV.length == AES_BLOCK_SIZE);
-    
-    System.arraycopy(initIV, 0, IV, 0, CTR_OFFSET);
-    long l = 0;
-    for (int i = 0; i < 8; i++) {
-      l = ((l << 8) | (initIV[CTR_OFFSET + i] & 0xff));
+
+    int i = IV.length; // IV length
+    int j = 0; // counter bytes index
+    int sum = 0;
+    while (i-- > 0) {
+      // (sum >>> Byte.SIZE) is the carry for addition
+      sum = (initIV[i] & 0xff) + (sum >>> Byte.SIZE);
+      if (j++ < 8) { // Big-endian, and long is 8 bytes length
+        sum += (byte) counter & 0xff;
+        counter >>>= 8;
+      }
+      IV[i] = (byte) sum;
     }
-    l += counter;
-    IV[CTR_OFFSET + 0] = (byte) (l >>> 56);
-    IV[CTR_OFFSET + 1] = (byte) (l >>> 48);
-    IV[CTR_OFFSET + 2] = (byte) (l >>> 40);
-    IV[CTR_OFFSET + 3] = (byte) (l >>> 32);
-    IV[CTR_OFFSET + 4] = (byte) (l >>> 24);
-    IV[CTR_OFFSET + 5] = (byte) (l >>> 16);
-    IV[CTR_OFFSET + 6] = (byte) (l >>> 8);
-    IV[CTR_OFFSET + 7] = (byte) (l);
   }
 }

+ 64 - 0
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoCodec.java

@@ -23,7 +23,9 @@ import static org.junit.Assert.assertTrue;
 import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
+import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -41,6 +43,8 @@ import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Test;
 
+import com.google.common.primitives.Longs;
+
 public class TestCryptoCodec {
   private static final Log LOG= LogFactory.getLog(TestCryptoCodec.class);
   private static final byte[] key = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
@@ -230,4 +234,64 @@ public class TestCryptoCodec {
     Assert.assertEquals(len, rand1.length);
     Assert.assertFalse(Arrays.equals(rand, rand1));
   }
+  
+  /**
+   * Regression test for IV calculation, see HADOOP-11343
+   */
+  @Test(timeout=120000)
+  public void testCalculateIV() throws Exception {
+    JceAesCtrCryptoCodec codec = new JceAesCtrCryptoCodec();
+    codec.setConf(conf);
+
+    SecureRandom sr = new SecureRandom();
+    byte[] initIV = new byte[16];
+    byte[] IV = new byte[16];
+
+    long iterations = 1000;
+    long counter = 10000;
+
+    // Overflow test, IV: 00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff 
+    for(int i = 0; i < 8; i++) {
+      initIV[8 + i] = (byte)0xff;
+    }
+
+    for(long j = 0; j < counter; j++) {
+      assertIVCalculation(codec, initIV, j, IV);
+    }
+
+    // Random IV and counter sequence test
+    for(long i = 0; i < iterations; i++) {
+      sr.nextBytes(initIV);
+
+      for(long j = 0; j < counter; j++) {
+        assertIVCalculation(codec, initIV, j, IV);
+      }
+    }
+
+    // Random IV and random counter test
+    for(long i = 0; i < iterations; i++) {
+      sr.nextBytes(initIV);
+
+      for(long j = 0; j < counter; j++) {
+        long c = sr.nextLong();
+        assertIVCalculation(codec, initIV, c, IV);
+      }
+    }
+  }
+
+  private void assertIVCalculation(CryptoCodec codec, byte[] initIV,
+      long counter, byte[] IV) {
+    codec.calculateIV(initIV, counter, IV);
+
+    BigInteger iv = new BigInteger(1, IV);
+    BigInteger ref = calculateRef(initIV, counter);
+
+    assertTrue("Calculated IV don't match with the reference", iv.equals(ref));
+  }
+
+  private static BigInteger calculateRef(byte[] initIV, long counter) {
+    byte[] cb = Longs.toByteArray(counter);
+    BigInteger bi = new BigInteger(1, initIV);
+    return bi.add(new BigInteger(1, cb));
+  }
 }