CVE-2024-21909: Inefficient Algorithmic Complexity (CWE-407)
value: PeterO.Cbor versions 4.0.0 through 4.5.0 are vulnerable to a denial of 
service vulnerability. An attacker may trigger the denial of service 
condition by providing crafted data to the DecodeFromBytes or other 
decoding mechanisms in PeterO.Cbor. Depending on the usage of the 
library, an unauthenticated and remote attacker may be able to cause the
 denial of service condition.

CWE description: An algorithm in a product has an inefficient worst-case computational complexity that may be detrimental to system performance and can be triggered by an attacker, typically using crafted manipulations that ensure that the worst case is being reached.
CVSS v3 base score: 7.5
Published: 2024-01-03T16:15Z
Repository: https://github.com/peteroupc/CBOR
Language: C#
Files changed: CBOR/PeterO/Cbor/CBORObject.cs, CBOR/PeterO/Cbor/PropertyMap.cs, CBORTest/CBORGenerator.cs, CBORTest/CBORObjectTest.cs, CBORTest/CBORTest.csproj, CBORTest/CBORTestCommon.cs, CBORTest/TestCommon.cs
Commit date: 2022-01-18 05:14:03 -0500
Commit message: fix algorithmic complexity issue; add regression tests

Diff: diff --git a/CBOR/PeterO/Cbor/CBORObject.cs b/CBOR/PeterO/Cbor/CBORObject.cs
index 8fdd9f0e..3e0fb787 100644
--- a/CBOR/PeterO/Cbor/CBORObject.cs
+++ b/CBOR/PeterO/Cbor/CBORObject.cs
@@ -7940,16 +7940,12 @@ public sealed partial class CBORObject : IComparable<CBORObject>,
       // NOTE: Compare map key counts to conform
       // to bytewise lexicographical ordering
       if (listACount != listBCount) {
         return listACount < listBCount ? -1 : 1;
       }
-      var sortedASet = new List<CBORObject>(mapA.Keys);
-      var sortedBSet = new List<CBORObject>(mapB.Keys);
-      // DebugUtility.Log("---sorting mapA's keys");
-      sortedASet.Sort();
-      // DebugUtility.Log("---sorting mapB's keys");
-      sortedBSet.Sort();
+      var sortedASet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapA));
+      var sortedBSet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapB));
       // DebugUtility.Log("---done sorting");
       listACount = sortedASet.Count;
       listBCount = sortedBSet.Count;
       // Compare the keys
       /* for (var i = 0; i < listACount; ++i) {
diff --git a/CBOR/PeterO/Cbor/PropertyMap.cs b/CBOR/PeterO/Cbor/PropertyMap.cs
index ce82517f..4a49a42a 100644
--- a/CBOR/PeterO/Cbor/PropertyMap.cs
+++ b/CBOR/PeterO/Cbor/PropertyMap.cs
@@ -33,11 +33,11 @@ private sealed class OrderedDictionary<TKey, TValue> :
       // that
       // Dictionary.
       private readonly IDictionary<TKey, TValue> dict;
       private readonly LinkedList<TKey> list;
       public OrderedDictionary() {
-        this.dict = new Dictionary<TKey, TValue>();
+        this.dict = new SortedDictionary<TKey, TValue>();
         this.list = new LinkedList<TKey>();
       }
       public void Add(KeyValuePair<TKey, TValue> kvp) {
         this.Add(kvp.Key, kvp.Value);
       }
@@ -177,10 +177,16 @@ private sealed class OrderedDictionary<TKey, TValue> :
         get {
           return new KeyWrapper<TKey, TValue>(this.dict, this.list);
         }
       }
 
+      public ICollection<TKey> SortedKeys {
+        get {
+           return this.dict.Keys;
+        }
+      }
+
       public ICollection<TValue> Values {
         get {
           return new ValueWrapper<TKey, TValue>(this.dict, this.list);
         }
       }
@@ -893,10 +899,25 @@ private sealed class PropertyData {
           Convert.ToInt64(value,
             CultureInfo.InvariantCulture) :
           Convert.ToInt32(value, CultureInfo.InvariantCulture));
     }
 
+    public static ICollection<TKey>
+    GetSortedKeys<TKey, TValue>(
+      IDictionary<TKey, TValue> dict) {
+      var odict = dict as OrderedDictionary<TKey, TValue>;
+      if (odict != null) {
+        return odict.SortedKeys;
+      }
+      var sdict = dict as SortedDictionary<TKey, TValue>;
+      if (sdict != null) {
+        return sdict.Keys;
+      }
+      throw new InvalidOperationException("Internal error: Map doesn't" +
+"\u0020support sorted keys");
+    }
+
     public static ICollection<KeyValuePair<TKey, TValue>>
     GetEntries<TKey, TValue>(
       IDictionary<TKey, TValue> dict) {
       var c = (ICollection<KeyValuePair<TKey, TValue>>)dict;
       return new ReadOnlyWrapper<KeyValuePair<TKey, TValue>>(c);
diff --git a/CBORTest/CBORGenerator.cs b/CBORTest/CBORGenerator.cs
index b5a00895..b6916eb0 100644
--- a/CBORTest/CBORGenerator.cs
+++ b/CBORTest/CBORGenerator.cs
@@ -82,11 +82,17 @@ private sealed class ByteWriter {
     }
 
     private static int[]
     valueMajorTypes = {
       0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4,
-      4, 5, 6, 6, 7, 7, 7, 7, 7, 7,
+      4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,
+    };
+
+    private static int[]
+    valueMajorTypesHighDepth = {
+      0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+      5, 5, 5, 5, 5, 5, 6, 7,
     };
 
     private static int[] valueMajorTypesHighLength = {
       0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 6,
       6, 7, 7, 7, 7, 7, 7,
@@ -128,24 +134,54 @@ private sealed class ByteWriter {
           }
         }
       }
     }
 
+    private void GenerateSmall(IRandomGenExtended r, int depth, ByteWriter bs) {
+      int v = r.GetInt32(100);
+      if (v < 25) {
+        GenerateArgument(r, 0, r.GetInt32(100), bs);
+      } else if (v < 35) {
+        bs.Write(0x41);
+      bs.Write(0x20);
+      } else if (v < 45) {
+        bs.Write(0x41);
+      bs.Write(0x20);
+      } else if (v < 50) {
+        bs.Write(0x81);
+      this.GenerateSmall(r, depth + 1, bs);
+      } else if (v < 53) {
+        bs.Write(0xa2);
+      bs.Write(0xf7);
+    bs.Write(0xf6);
+  this.GenerateSmall(r, depth + 1, bs);
+  bs.Write(0xf5);
+      } else if (v < 80) {
+        bs.Write(r.GetInt32(0x40));
+      } else if (v < 100) {
+        bs.Write(r.GetInt32(0x60));
+      }
+    }
     private void Generate(IRandomGenExtended r, int depth, ByteWriter bs) {
       int majorType = valueMajorTypes[r.GetInt32(valueMajorTypes.Length)];
+      if (depth > 6) {
+        majorType = valueMajorTypesHighDepth[r.GetInt32(
+              valueMajorTypesHighDepth.Length)];
+      }
       if (bs.ByteLength > 2000000) {
         majorType = valueMajorTypesHighLength[r.GetInt32(
               valueMajorTypesHighLength.Length)];
       }
-      if (majorType == 3 || majorType == 2) {
+      if (majorType == 3 || majorType == 2) { // Byte and text strings
         int len = r.GetInt32(1000);
         if (r.GetInt32(50) == 0 && depth < 2) {
           var v = (long)r.GetInt32(100000) * r.GetInt32(100000);
           len = (int)(v / 100000);
-        }
-        if (depth > 6) {
+        } else if (depth > 6) {
           len = r.GetInt32(100) == 0 ? 1 : 0;
+        } else if (depth > 2) {
+          len = r.GetInt32(16) + 1;
         }
         // TODO: Ensure key uniqueness
         if (r.GetInt32(2) == 0) {
           // Indefinite length
           bs.Write(0x1f + (majorType * 0x20));
@@ -172,24 +208,35 @@ private sealed class ByteWriter {
               bs.Write(r.GetInt32(256));
             }
           }
         }
         return;
-      } else if (majorType == 4 || majorType == 5) {
+      } else if (majorType == 4 || majorType == 5) { // Arrays and maps
         int len = r.GetInt32(8);
         if (r.GetInt32(50) == 0 && depth < 2) {
           var v = (long)r.GetInt32(1000) * r.GetInt32(1000);
           len = (int)(v / 1000);
+        } else if (depth > 6) {
+          len = r.GetInt32(100) == 0 ? 1 : 0;
+        } else if (depth > 2) {
+          len = r.GetInt32(3) + 1;
+        }
+        if (depth > 6) {
+          len = r.GetInt32(100) < 50 ? 1 : (r.GetInt32(100) < 10 ? 2 : 0);
         }
         bool indefiniteLength = r.GetInt32(2) == 0;
         if (indefiniteLength) {
           bs.Write(0x1f + (majorType * 0x20));
         } else {
           GenerateArgument(r, majorType, len, bs);
         }
         for (int i = 0; i < len; ++i) {
-          this.Generate(r, depth + 1, bs);
+          if (depth > 6) {
+            this.GenerateSmall(r, depth + 1, bs);
+          } else {
+            this.Generate(r, depth + 1, bs);
+          }
           if (majorType == 5) {
             this.Generate(r, depth + 1, bs);
           }
         }
         if (indefiniteLength) {
@@ -227,11 +274,11 @@ private sealed class ByteWriter {
           for (int i = 0; i < 8; ++i) {
             bs.Write(r.GetInt32(256));
           }
           break;
       }
-      if (majorType == 6) {
+      if (majorType == 6) { // Tags
         this.Generate(r, depth + 1, bs);
       }
     }
 
     public byte[] Generate(IRandomGenExtended random) {
diff --git a/CBORTest/CBORObjectTest.cs b/CBORTest/CBORObjectTest.cs
index b04e0362..b68fceb0 100644
--- a/CBORTest/CBORObjectTest.cs
+++ b/CBORTest/CBORObjectTest.cs
@@ -9719,10 +9719,149 @@ public sealed class NestedPODClass {
       CBORObject cbor = FromJSON(json, jsonop);
       Assert.AreEqual(CBORType.FloatingPoint, cbor.Type);
       Assert.IsTrue(cbor.AsDoubleValue() == Double.NegativeInfinity);
     }
 
+[Test]
+public void TestRoundTripRegressions() {
+{
+var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
+var bytes = new byte[] {
+  (byte)0xba, 0x00, 0x00, 0x00, 0x03,
+  (byte)0xf9,
+  (byte)0x83, 0x1d,
+  (byte)0xda,
+  (byte)0xb6,
+  (byte)0xda, 0x50, 0x56, 0x1a, 0x50,
+  (byte)0xe3, 0x2c, 0x7a, 0x16,
+  (byte)0xfa, 0x50, 0x32, 0x73, 0x07,
+  (byte)0xfa, (byte)0xb9, 0x2d, 0x73, (byte)0xce, 0x38, (byte)0xd0,
+};
+CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
+}
+{
+var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
+var bytes = new byte[] {
+  (byte)0xbf,
+  (byte)0x9f,
+  (byte)0xbf, 0x39, 0x20,
+  (byte)0x8f, 0x4a, 0x1f, 0x46, 0x26, 0x0b, 0x3e, 0x72, 0x2c, 0x7f, 0x11,
+  0x2e, 0x39,
+  (byte)0x9d,
+  (byte)0xba, 0x1a, 0x11,
+  (byte)0x8d,
+  (byte)0xc0,
+  (byte)0xb4, 0x38,
+  (byte)0xb6,
+  (byte)0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+  (byte)0xd8, 0x3b,
+  (byte)0x99, 0x00, 0x02, 0x3b, 0x05,
+  (byte)0xbb,
+  (byte)0xea,
+  (byte)0x8e, 0x4b,
+  (byte)0xd3, 0x5e, 0x22,
+  (byte)0x9f, 0x59, 0x00, 0x00,
+  (byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x20,
+  (byte)0xbf, 0x1a, 0x00, 0x00, 0x00, 0x61,
+  (byte)0xb9, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x00, 0x0e,
+  (byte)0xba, 0x00, 0x00, 0x00, 0x00,
+  (byte)0xff,
+  (byte)0xff,
+  (byte)0xff,
+  (byte)0xd8, 0x22,
+  (byte)0xf8,
+  (byte)0x93,
+  (byte)0xd9,
+  (byte)0xaf, 0x33, 0x19,
+  (byte)0xf0,
+  (byte)0xf0,
+  (byte)0xf9,
+  (byte)0x85,
+  (byte)0x93,
+  (byte)0x99, 0x00, 0x01, 0x3a,
+  (byte)0xb5,
+  (byte)0xfb, 0x4d, 0x43,
+  (byte)0x98, 0x00,
+  (byte)0xff, (byte)0xfa, (byte)0xb0, (byte)0xb4, (byte)0xdc, 0x6d,
+  (byte)0xff,
+};
+CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
+}
+{
+var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
+var bytes = new byte[] {
+  (byte)0xdb, 0x0d,
+  (byte)0xcb, 0x5d, 0x78,
+  (byte)0x92,
+  (byte)0xc2,
+  (byte)0xc7, 0x2b,
+  (byte)0xb9, 0x00, 0x02, 0x39,
+  (byte)0xee,
+  (byte)0xa0, (byte)0xa0, 0x1a, 0x0e, (byte)0xd9, (byte)0xec, (byte)0xca,
+  (byte)0xf2,
+};
+CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
+}
+{
+var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
+var bytes = new byte[] {
+  (byte)0xbf,
+  (byte)0xfb,
+  (byte)0xb1, 0x21,
+  (byte)0x93,
+  (byte)0x8c,
+  (byte)0xc6,
+  (byte)0xf3,
+  (byte)0xcf,
+  (byte)0xb7, (byte)0xf8, 0x76, 0x18, (byte)0xda, 0x39, 0x60, (byte)0xf4,
+  (byte)0xff,
+};
+CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
+}
+{
+var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
+var bytes = new byte[] {
+  (byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x02, (byte)0xf0, 0x0d, 0x2a, 0x21,
+};
+CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
+}
+{
+var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
+var bytes = new byte[] {
+  (byte)0xba, 0x00, 0x00, 0x00, 0x02,
+  (byte)0xf9, 0x48, 0x37,
+  (byte)0xda,
+  (byte)0xb5, 0x72,
+  (byte)0xcf,
+  (byte)0xf8, 0x31, 0x3b, 0x06, 0x78,
+  (byte)0xdb, 0x44, 0x7d, (byte)0xba, (byte)0xbd, 0x7d, 0x39, (byte)0x98,
+  (byte)0xb9,
+};
+CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
+}
+}
+[Test]
+public void TestMapCompareRegressions() {
+  CBORObject m1, m2;
+  m1 = CBORObject.NewMap().Add(3, 4).Add(1, 2);
+  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
+  Assert.AreEqual(0, m1.CompareTo(m2));
+  TestCommon.CompareTestEqualAndConsistent(m1, m2);
+  m1 = CBORObject.NewMap().Add(3, 2).Add(1, 2);
+  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
+  TestCommon.CompareTestLess(m1, m2);
+  m1 = CBORObject.NewMap().Add(3, 7).Add(1, 2);
+  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
+  TestCommon.CompareTestGreater(m1, m2);
+  m1 = CBORObject.NewMap().Add(3, 4).Add(1, 0);
+  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
+  TestCommon.CompareTestLess(m1, m2);
+  m1 = CBORObject.NewMap().Add(3, 4).Add(1, 7);
+  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
+  TestCommon.CompareTestGreater(m1, m2);
+}
     [Test]
     public void TestToObject_TypeMapper() {
       var mapper = new CBORTypeMapper()
       .AddConverter(typeof(string), new TestConverter());
       CBORObject cbor = CBORObject.FromObject("UpPeR");
diff --git a/CBORTest/CBORTest.csproj b/CBORTest/CBORTest.csproj
index 0adfa772..b7958e22 100644
--- a/CBORTest/CBORTest.csproj
+++ b/CBORTest/CBORTest.csproj
@@ -1,8 +1,8 @@
 <Project Sdk='Microsoft.NET.Sdk'>
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp5.0</TargetFramework>
   </PropertyGroup>
   <PropertyGroup Condition=' &apos;$(Configuration)&apos;==&apos;Debug&apos; '>
     <DebugType>full</DebugType>
     <CodeAnalysisRuleSet>rules.ruleset</CodeAnalysisRuleSet></PropertyGroup>
   <PropertyGroup Condition=' &apos;$(Configuration)&apos;==&apos;Release&apos; '>
diff --git a/CBORTest/CBORTestCommon.cs b/CBORTest/CBORTestCommon.cs
index 911ce55e..20e1f18f 100644
--- a/CBORTest/CBORTestCommon.cs
+++ b/CBORTest/CBORTestCommon.cs
@@ -117,11 +117,12 @@ internal static class CBORTestCommon {
     }
 
     public static CBORObject RandomCBORMap(IRandomGenExtended rand, int depth) {
       int x = rand.GetInt32(100);
       int count = (x < 80) ? 2 : ((x < 93) ? 1 : ((x < 98) ? 0 : 10));
-      CBORObject cborRet = CBORObject.NewMap();
+      CBORObject cborRet = rand.GetInt32(100) < 30 ?
+         CBORObject.NewOrderedMap() : CBORObject.NewMap();
       for (var i = 0; i < count; ++i) {
         CBORObject key = RandomCBORObject(rand, depth + 1);
         CBORObject value = RandomCBORObject(rand, depth + 1);
         cborRet[key] = value;
       }
diff --git a/CBORTest/TestCommon.cs b/CBORTest/TestCommon.cs
index 287b4af4..b5e558e6 100644
--- a/CBORTest/TestCommon.cs
+++ b/CBORTest/TestCommon.cs
@@ -759,11 +759,11 @@ public static void CompareTestLessEqual<T>(T o1, T o2, string msg)
         }
       }
       return true;
     }
 
-    private static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
+    public static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
       if (arr1 == null) {
         return arr2 == null;
       }
       if (arr2 == null) {
         return false;

Vulnerable code:       var sortedASet = new List<CBORObject>(mapA.Keys);
      var sortedBSet = new List<CBORObject>(mapB.Keys);
      // DebugUtility.Log("---sorting mapA's keys");
      sortedASet.Sort();
      // DebugUtility.Log("---sorting mapB's keys");
      sortedBSet.Sort();
        this.dict = new Dictionary<TKey, TValue>();
      4, 5, 6, 6, 7, 7, 7, 7, 7, 7,
      if (majorType == 3 || majorType == 2) {
        }
        if (depth > 6) {
      } else if (majorType == 4 || majorType == 5) {
          this.Generate(r, depth + 1, bs);
      if (majorType == 6) {
    <TargetFramework>netcoreapp2.1</TargetFramework>
      CBORObject cborRet = CBORObject.NewMap();
    private static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {
Fixed code:       var sortedASet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapA));
      var sortedBSet = new List<CBORObject>(PropertyMap.GetSortedKeys(mapB));
        this.dict = new SortedDictionary<TKey, TValue>();
      public ICollection<TKey> SortedKeys {
        get {
           return this.dict.Keys;
        }
      }

    public static ICollection<TKey>
    GetSortedKeys<TKey, TValue>(
      IDictionary<TKey, TValue> dict) {
      var odict = dict as OrderedDictionary<TKey, TValue>;
      if (odict != null) {
        return odict.SortedKeys;
      }
      var sdict = dict as SortedDictionary<TKey, TValue>;
      if (sdict != null) {
        return sdict.Keys;
      }
      throw new InvalidOperationException("Internal error: Map doesn't" +
"\u0020support sorted keys");
    }

      4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,
    };

    private static int[]
    valueMajorTypesHighDepth = {
      0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
      5, 5, 5, 5, 5, 5, 6, 7,
    private void GenerateSmall(IRandomGenExtended r, int depth, ByteWriter bs) {
      int v = r.GetInt32(100);
      if (v < 25) {
        GenerateArgument(r, 0, r.GetInt32(100), bs);
      } else if (v < 35) {
        bs.Write(0x41);
      bs.Write(0x20);
      } else if (v < 45) {
        bs.Write(0x41);
      bs.Write(0x20);
      } else if (v < 50) {
        bs.Write(0x81);
      this.GenerateSmall(r, depth + 1, bs);
      } else if (v < 53) {
        bs.Write(0xa2);
      bs.Write(0xf7);
    bs.Write(0xf6);
  this.GenerateSmall(r, depth + 1, bs);
  bs.Write(0xf5);
      } else if (v < 80) {
        bs.Write(r.GetInt32(0x40));
      } else if (v < 100) {
        bs.Write(r.GetInt32(0x60));
      }
    }
      if (depth > 6) {
        majorType = valueMajorTypesHighDepth[r.GetInt32(
              valueMajorTypesHighDepth.Length)];
      }
      if (majorType == 3 || majorType == 2) { // Byte and text strings
        } else if (depth > 6) {
        } else if (depth > 2) {
          len = r.GetInt32(16) + 1;
      } else if (majorType == 4 || majorType == 5) { // Arrays and maps
        } else if (depth > 6) {
          len = r.GetInt32(100) == 0 ? 1 : 0;
        } else if (depth > 2) {
          len = r.GetInt32(3) + 1;
        }
        if (depth > 6) {
          len = r.GetInt32(100) < 50 ? 1 : (r.GetInt32(100) < 10 ? 2 : 0);
          if (depth > 6) {
            this.GenerateSmall(r, depth + 1, bs);
          } else {
            this.Generate(r, depth + 1, bs);
          }
      if (majorType == 6) { // Tags
[Test]
public void TestRoundTripRegressions() {
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
  (byte)0xba, 0x00, 0x00, 0x00, 0x03,
  (byte)0xf9,
  (byte)0x83, 0x1d,
  (byte)0xda,
  (byte)0xb6,
  (byte)0xda, 0x50, 0x56, 0x1a, 0x50,
  (byte)0xe3, 0x2c, 0x7a, 0x16,
  (byte)0xfa, 0x50, 0x32, 0x73, 0x07,
  (byte)0xfa, (byte)0xb9, 0x2d, 0x73, (byte)0xce, 0x38, (byte)0xd0,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
  (byte)0xbf,
  (byte)0x9f,
  (byte)0xbf, 0x39, 0x20,
  (byte)0x8f, 0x4a, 0x1f, 0x46, 0x26, 0x0b, 0x3e, 0x72, 0x2c, 0x7f, 0x11,
  0x2e, 0x39,
  (byte)0x9d,
  (byte)0xba, 0x1a, 0x11,
  (byte)0x8d,
  (byte)0xc0,
  (byte)0xb4, 0x38,
  (byte)0xb6,
  (byte)0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
  (byte)0xd8, 0x3b,
  (byte)0x99, 0x00, 0x02, 0x3b, 0x05,
  (byte)0xbb,
  (byte)0xea,
  (byte)0x8e, 0x4b,
  (byte)0xd3, 0x5e, 0x22,
  (byte)0x9f, 0x59, 0x00, 0x00,
  (byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x20,
  (byte)0xbf, 0x1a, 0x00, 0x00, 0x00, 0x61,
  (byte)0xb9, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x00, 0x0e,
  (byte)0xba, 0x00, 0x00, 0x00, 0x00,
  (byte)0xff,
  (byte)0xff,
  (byte)0xff,
  (byte)0xd8, 0x22,
  (byte)0xf8,
  (byte)0x93,
  (byte)0xd9,
  (byte)0xaf, 0x33, 0x19,
  (byte)0xf0,
  (byte)0xf0,
  (byte)0xf9,
  (byte)0x85,
  (byte)0x93,
  (byte)0x99, 0x00, 0x01, 0x3a,
  (byte)0xb5,
  (byte)0xfb, 0x4d, 0x43,
  (byte)0x98, 0x00,
  (byte)0xff, (byte)0xfa, (byte)0xb0, (byte)0xb4, (byte)0xdc, 0x6d,
  (byte)0xff,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
  (byte)0xdb, 0x0d,
  (byte)0xcb, 0x5d, 0x78,
  (byte)0x92,
  (byte)0xc2,
  (byte)0xc7, 0x2b,
  (byte)0xb9, 0x00, 0x02, 0x39,
  (byte)0xee,
  (byte)0xa0, (byte)0xa0, 0x1a, 0x0e, (byte)0xd9, (byte)0xec, (byte)0xca,
  (byte)0xf2,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
  (byte)0xbf,
  (byte)0xfb,
  (byte)0xb1, 0x21,
  (byte)0x93,
  (byte)0x8c,
  (byte)0xc6,
  (byte)0xf3,
  (byte)0xcf,
  (byte)0xb7, (byte)0xf8, 0x76, 0x18, (byte)0xda, 0x39, 0x60, (byte)0xf4,
  (byte)0xff,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
  (byte)0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x02, (byte)0xf0, 0x0d, 0x2a, 0x21,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
{
var options = new CBOREncodeOptions("allowduplicatekeys=1;keepkeyorder=1");
var bytes = new byte[] {
  (byte)0xba, 0x00, 0x00, 0x00, 0x02,
  (byte)0xf9, 0x48, 0x37,
  (byte)0xda,
  (byte)0xb5, 0x72,
  (byte)0xcf,
  (byte)0xf8, 0x31, 0x3b, 0x06, 0x78,
  (byte)0xdb, 0x44, 0x7d, (byte)0xba, (byte)0xbd, 0x7d, 0x39, (byte)0x98,
  (byte)0xb9,
};
CBORTestCommon.AssertRoundTrip(CBORObject.DecodeFromBytes(bytes, options));
}
}
[Test]
public void TestMapCompareRegressions() {
  CBORObject m1, m2;
  m1 = CBORObject.NewMap().Add(3, 4).Add(1, 2);
  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
  Assert.AreEqual(0, m1.CompareTo(m2));
  TestCommon.CompareTestEqualAndConsistent(m1, m2);
  m1 = CBORObject.NewMap().Add(3, 2).Add(1, 2);
  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
  TestCommon.CompareTestLess(m1, m2);
  m1 = CBORObject.NewMap().Add(3, 7).Add(1, 2);
  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
  TestCommon.CompareTestGreater(m1, m2);
  m1 = CBORObject.NewMap().Add(3, 4).Add(1, 0);
  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
  TestCommon.CompareTestLess(m1, m2);
  m1 = CBORObject.NewMap().Add(3, 4).Add(1, 7);
  m2 = CBORObject.NewOrderedMap().Add(3, 4).Add(1, 2);
  TestCommon.CompareTestGreater(m1, m2);
}
    <TargetFramework>netcoreapp5.0</TargetFramework>
      CBORObject cborRet = rand.GetInt32(100) < 30 ?
         CBORObject.NewOrderedMap() : CBORObject.NewMap();
    public static bool ByteArraysEqual(byte[] arr1, byte[] arr2) {