From ccff1bc205722a94e386c3cfe44852ab55430886 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Wed, 31 Dec 2008 13:55:37 +0000
Subject: [PATCH] - [Issue 3694]  ASN.1 package does incorrect BER encoding/decoding for negative integers.

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Integer.java |   49 ++++++
 opends/src/server/org/opends/server/protocols/asn1/ASN1Long.java                                |   15 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Long.java    |   49 ++++++
 opends/src/server/org/opends/server/protocols/asn1/ASN1Element.java                             |  325 ++++++++++++++++++++++++++++------------
 opends/src/server/org/opends/server/protocols/asn1/ASN1Integer.java                             |   15 +
 5 files changed, 357 insertions(+), 96 deletions(-)

diff --git a/opends/src/server/org/opends/server/protocols/asn1/ASN1Element.java b/opends/src/server/org/opends/server/protocols/asn1/ASN1Element.java
index aca8308..0bf1db5 100644
--- a/opends/src/server/org/opends/server/protocols/asn1/ASN1Element.java
+++ b/opends/src/server/org/opends/server/protocols/asn1/ASN1Element.java
@@ -382,39 +382,79 @@
    */
   public static byte[] encodeValue(int intValue)
   {
-    if ((intValue & 0x0000007F) == intValue)
+    if (intValue < 0)
     {
-      return new byte[]
+      if ((intValue & 0xFFFFFF80) == 0xFFFFFF80)
       {
-        (byte) (intValue & 0xFF)
-      };
-    }
-    else if ((intValue & 0x00007FFF) == intValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) (intValue & 0xFF)
+        };
+      }
+      else if ((intValue & 0xFFFF8000) == 0xFFFF8000)
       {
-        (byte) ((intValue >> 8) & 0xFF),
-        (byte) (intValue & 0xFF)
-      };
-    }
-    else if ((intValue & 0x007FFFFF) == intValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) ((intValue >> 8) & 0xFF),
+          (byte) (intValue & 0xFF)
+        };
+      }
+      else if ((intValue & 0xFF800000) == 0xFF800000)
       {
-        (byte) ((intValue >> 16) & 0xFF),
-        (byte) ((intValue >>  8) & 0xFF),
-        (byte) (intValue & 0xFF)
-      };
+        return new byte[]
+        {
+          (byte) ((intValue >> 16) & 0xFF),
+          (byte) ((intValue >>  8) & 0xFF),
+          (byte) (intValue & 0xFF)
+        };
+      }
+      else
+      {
+        return new byte[]
+        {
+          (byte) ((intValue >> 24) & 0xFF),
+          (byte) ((intValue >> 16) & 0xFF),
+          (byte) ((intValue >>  8) & 0xFF),
+          (byte) (intValue & 0xFF)
+        };
+      }
     }
     else
     {
-      return new byte[]
+      if ((intValue & 0x0000007F) == intValue)
       {
-        (byte) ((intValue >> 24) & 0xFF),
-        (byte) ((intValue >> 16) & 0xFF),
-        (byte) ((intValue >>  8) & 0xFF),
-        (byte) (intValue & 0xFF)
-      };
+        return new byte[]
+        {
+          (byte) (intValue & 0xFF)
+        };
+      }
+      else if ((intValue & 0x00007FFF) == intValue)
+      {
+        return new byte[]
+        {
+          (byte) ((intValue >> 8) & 0xFF),
+          (byte) (intValue & 0xFF)
+        };
+      }
+      else if ((intValue & 0x007FFFFF) == intValue)
+      {
+        return new byte[]
+        {
+          (byte) ((intValue >> 16) & 0xFF),
+          (byte) ((intValue >>  8) & 0xFF),
+          (byte) (intValue & 0xFF)
+        };
+      }
+      else
+      {
+        return new byte[]
+        {
+          (byte) ((intValue >> 24) & 0xFF),
+          (byte) ((intValue >> 16) & 0xFF),
+          (byte) ((intValue >>  8) & 0xFF),
+          (byte) (intValue & 0xFF)
+        };
+      }
     }
   }
 
@@ -431,89 +471,179 @@
    */
   public static byte[] encodeLongValue(long longValue)
   {
-    if ((longValue & 0x000000000000007FL) == longValue)
+    if (longValue < 0)
     {
-      return new byte[]
+      if ((longValue & 0xFFFFFFFFFFFFFF80L) == 0xFFFFFFFFFFFFFF80L)
       {
-        (byte) (longValue & 0xFF)
-      };
-    }
-    else if ((longValue & 0x0000000000007FFFL) == longValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0xFFFFFFFFFFFF8000L) == 0xFFFFFFFFFFFF8000L)
       {
-        (byte) ((longValue >> 8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
-    }
-    else if ((longValue & 0x00000000007FFFFFL) == longValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) ((longValue >> 8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0xFFFFFFFFFF800000L) == 0xFFFFFFFFFF800000L)
       {
-        (byte) ((longValue >> 16) & 0xFF),
-        (byte) ((longValue >>  8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
-    }
-    else if ((longValue & 0x000000007FFFFFFFL) == longValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0xFFFFFFFF80000000L) == 0xFFFFFFFF80000000L)
       {
-        (byte) ((longValue >> 24) & 0xFF),
-        (byte) ((longValue >> 16) & 0xFF),
-        (byte) ((longValue >>  8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
-    }
-    else if ((longValue & 0x0000007FFFFFFFFFL) == longValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0xFFFFFF8000000000L) == 0xFFFFFF8000000000L)
       {
-        (byte) ((longValue >> 32) & 0xFF),
-        (byte) ((longValue >> 24) & 0xFF),
-        (byte) ((longValue >> 16) & 0xFF),
-        (byte) ((longValue >>  8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
-    }
-    else if ((longValue & 0x00007FFFFFFFFFFFL) == longValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0xFFFF800000000000L) == 0xFFFF800000000000L)
       {
-        (byte) ((longValue >> 40) & 0xFF),
-        (byte) ((longValue >> 32) & 0xFF),
-        (byte) ((longValue >> 24) & 0xFF),
-        (byte) ((longValue >> 16) & 0xFF),
-        (byte) ((longValue >>  8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
-    }
-    else if ((longValue & 0x007FFFFFFFFFFFFFL) == longValue)
-    {
-      return new byte[]
+        return new byte[]
+        {
+          (byte) ((longValue >> 40) & 0xFF),
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0xFF80000000000000L) == 0xFF80000000000000L)
       {
-        (byte) ((longValue >> 48) & 0xFF),
-        (byte) ((longValue >> 40) & 0xFF),
-        (byte) ((longValue >> 32) & 0xFF),
-        (byte) ((longValue >> 24) & 0xFF),
-        (byte) ((longValue >> 16) & 0xFF),
-        (byte) ((longValue >>  8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
+        return new byte[]
+        {
+          (byte) ((longValue >> 48) & 0xFF),
+          (byte) ((longValue >> 40) & 0xFF),
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 56) & 0xFF),
+          (byte) ((longValue >> 48) & 0xFF),
+          (byte) ((longValue >> 40) & 0xFF),
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
     }
     else
     {
-      return new byte[]
+      if ((longValue & 0x000000000000007FL) == longValue)
       {
-        (byte) ((longValue >> 56) & 0xFF),
-        (byte) ((longValue >> 48) & 0xFF),
-        (byte) ((longValue >> 40) & 0xFF),
-        (byte) ((longValue >> 32) & 0xFF),
-        (byte) ((longValue >> 24) & 0xFF),
-        (byte) ((longValue >> 16) & 0xFF),
-        (byte) ((longValue >>  8) & 0xFF),
-        (byte) (longValue & 0xFF)
-      };
+        return new byte[]
+        {
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0x0000000000007FFFL) == longValue)
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0x00000000007FFFFFL) == longValue)
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0x000000007FFFFFFFL) == longValue)
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0x0000007FFFFFFFFFL) == longValue)
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0x00007FFFFFFFFFFFL) == longValue)
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 40) & 0xFF),
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else if ((longValue & 0x007FFFFFFFFFFFFFL) == longValue)
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 48) & 0xFF),
+          (byte) ((longValue >> 40) & 0xFF),
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
+      else
+      {
+        return new byte[]
+        {
+          (byte) ((longValue >> 56) & 0xFF),
+          (byte) ((longValue >> 48) & 0xFF),
+          (byte) ((longValue >> 40) & 0xFF),
+          (byte) ((longValue >> 32) & 0xFF),
+          (byte) ((longValue >> 24) & 0xFF),
+          (byte) ((longValue >> 16) & 0xFF),
+          (byte) ((longValue >>  8) & 0xFF),
+          (byte) (longValue & 0xFF)
+        };
+      }
     }
   }
 
@@ -947,6 +1077,7 @@
    *          object will be considered equal if it is an ASN.1 element (or a
    *          subclass) with the same type and encoded value.
    */
+  @Override
   public final boolean equals(Object o)
   {
     if (this == o)
@@ -1031,6 +1162,7 @@
    *
    * @return  The hash code for this ASN.1 element.
    */
+  @Override
   public final int hashCode()
   {
     int hashCode = type;
@@ -1050,6 +1182,7 @@
    *
    * @return  A string representation of this ASN.1 element.
    */
+  @Override
   public final String toString()
   {
     StringBuilder buffer = new StringBuilder();
diff --git a/opends/src/server/org/opends/server/protocols/asn1/ASN1Integer.java b/opends/src/server/org/opends/server/protocols/asn1/ASN1Integer.java
index 02d6392..43c3f60 100644
--- a/opends/src/server/org/opends/server/protocols/asn1/ASN1Integer.java
+++ b/opends/src/server/org/opends/server/protocols/asn1/ASN1Integer.java
@@ -146,6 +146,7 @@
    * @throws  ASN1Exception  If the provided array is null or is not between one
    *                         and four bytes in length.
    */
+  @Override
   public void setValue(byte[] value)
          throws ASN1Exception
   {
@@ -201,6 +202,12 @@
     }
 
     int intValue = 0;
+
+    if (value[0] < 0)
+    {
+      intValue = 0xFFFFFFFF;
+    }
+
     for (byte b : value)
     {
       intValue = (intValue << 8) | (b & 0xFF);
@@ -293,6 +300,12 @@
     System.arraycopy(encodedElement, valueStartPos, value, 0, length);
 
     int intValue = 0;
+
+    if (value[0] < 0)
+    {
+      intValue = 0xFFFFFFFF;
+    }
+
     for (byte b : value)
     {
       intValue = (intValue << 8) | (b & 0xFF);
@@ -309,6 +322,7 @@
    *
    * @param  buffer  The buffer to which the information should be appended.
    */
+  @Override
   public void toString(StringBuilder buffer)
   {
     buffer.append("ASN1Integer(type=");
@@ -329,6 +343,7 @@
    * @param  indent  The number of spaces that should be used to indent the
    *                 resulting string representation.
    */
+  @Override
   public void toString(StringBuilder buffer, int indent)
   {
     StringBuilder indentBuf = new StringBuilder(indent);
diff --git a/opends/src/server/org/opends/server/protocols/asn1/ASN1Long.java b/opends/src/server/org/opends/server/protocols/asn1/ASN1Long.java
index 5577c47..6bfc595 100644
--- a/opends/src/server/org/opends/server/protocols/asn1/ASN1Long.java
+++ b/opends/src/server/org/opends/server/protocols/asn1/ASN1Long.java
@@ -159,6 +159,7 @@
    * @throws  ASN1Exception  If the provided array is null or is not between one
    *                         and four bytes in length.
    */
+  @Override
   public void setValue(byte[] value)
          throws ASN1Exception
   {
@@ -214,6 +215,12 @@
     }
 
     long longValue = 0;
+
+    if (value[0] < 0)
+    {
+      longValue = 0xFFFFFFFFFFFFFFFFL;
+    }
+
     for (byte b : value)
     {
       longValue = (longValue << 8) | (b & 0xFF);
@@ -304,6 +311,12 @@
     System.arraycopy(encodedElement, valueStartPos, value, 0, length);
 
     long longValue = 0;
+
+    if (value[0] < 0)
+    {
+      longValue = 0xFFFFFFFFFFFFFFFFL;
+    }
+
     for (byte b : value)
     {
       longValue = (longValue << 8) | (b & 0xFF);
@@ -320,6 +333,7 @@
    *
    * @param  buffer  The buffer to which the information should be appended.
    */
+  @Override
   public void toString(StringBuilder buffer)
   {
     buffer.append("ASN1Long(type=");
@@ -340,6 +354,7 @@
    * @param  indent  The number of spaces that should be used to indent the
    *                 resulting string representation.
    */
+  @Override
   public void toString(StringBuilder buffer, int indent)
   {
     StringBuilder indentBuf = new StringBuilder(indent);
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Integer.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Integer.java
index ed2b2ee..d3b1247 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Integer.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Integer.java
@@ -135,6 +135,55 @@
 
 
   /**
+   * Tests that negative integers are encoded according
+   * to ASN.1 BER specification.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testNegativeIntEncoding()
+         throws Exception
+  {
+    byte[] value = null;
+    // Some negative integers of interest
+    // to test specific ranges/boundaries.
+    value = ASN1Integer.encodeValue(-1);
+    assertEquals(value[0], (byte) 0xFF);
+    value = ASN1Integer.encodeValue(-2);
+    assertEquals(value[0], (byte) 0xFE);
+    value = ASN1Integer.encodeValue(-127);
+    assertEquals(value[0], (byte) 0x81);
+    value = ASN1Integer.encodeValue(-128);
+    assertEquals(value[0], (byte) 0x80);
+    value = ASN1Integer.encodeValue(-255);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x01);
+    value = ASN1Integer.encodeValue(-256);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x00);
+    value = ASN1Integer.encodeValue(-65535);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x01);
+    value = ASN1Integer.encodeValue(-65536);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x00);
+    value = ASN1Integer.encodeValue(-2147483647);
+    assertEquals(value[0], (byte) 0x80);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x00);
+    assertEquals(value[3], (byte) 0x01);
+    value = ASN1Integer.encodeValue(-2147483648);
+    assertEquals(value[0], (byte) 0x80);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x00);
+    assertEquals(value[3], (byte) 0x00);
+  }
+
+
+
+  /**
    * Tests the <CODE>setValue</CODE> method that takes an int argument.
    *
    * @param  i  The integer value to use for the test.
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Long.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Long.java
index 97bec61..1a3b31a 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Long.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/asn1/TestASN1Long.java
@@ -135,6 +135,55 @@
 
 
   /**
+   * Tests that negative integers are encoded according
+   * to ASN.1 BER specification.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testNegativeLongEncoding()
+         throws Exception
+  {
+    byte[] value = null;
+    // Some negative integers of interest
+    // to test specific ranges/boundaries.
+    value = ASN1Long.encodeLongValue(-1L);
+    assertEquals(value[0], (byte) 0xFF);
+    value = ASN1Long.encodeLongValue(-2L);
+    assertEquals(value[0], (byte) 0xFE);
+    value = ASN1Long.encodeLongValue(-127L);
+    assertEquals(value[0], (byte) 0x81);
+    value = ASN1Long.encodeLongValue(-128L);
+    assertEquals(value[0], (byte) 0x80);
+    value = ASN1Long.encodeLongValue(-255L);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x01);
+    value = ASN1Long.encodeLongValue(-256L);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x00);
+    value = ASN1Long.encodeLongValue(-65535L);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x01);
+    value = ASN1Long.encodeLongValue(-65536L);
+    assertEquals(value[0], (byte) 0xFF);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x00);
+    value = ASN1Long.encodeLongValue(-2147483647L);
+    assertEquals(value[0], (byte) 0x80);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x00);
+    assertEquals(value[3], (byte) 0x01);
+    value = ASN1Long.encodeLongValue(-2147483648L);
+    assertEquals(value[0], (byte) 0x80);
+    assertEquals(value[1], (byte) 0x00);
+    assertEquals(value[2], (byte) 0x00);
+    assertEquals(value[3], (byte) 0x00);
+  }
+
+
+
+  /**
    * Tests the <CODE>setValue</CODE> method that takes a long argument.
    *
    * @param  l  The long value to use for the test.

--
Gitblit v1.10.0