diff --git a/SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs b/SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs index 1432c27b..bf68af45 100644 --- a/SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs +++ b/SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using SabreTools.Serialization.ASN1; using Xunit; @@ -7,123 +6,13 @@ namespace SabreTools.Serialization.Test.ASN1 { public class TypeLengthValueTests { - #region Construction - - [Fact] - public void Constructor_EmptyArray_Throws() - { - int index = 0; - byte[] data = []; - Assert.Throws(() => new TypeLengthValue(data, ref index)); - } - - [Fact] - public void Constructor_ValidArrayNegativeIndex_Throws() - { - int index = -1; - byte[] data = [0x00]; - Assert.Throws(() => new TypeLengthValue(data, ref index)); - } - - [Fact] - public void Constructor_ValidArrayOverIndex_Throws() - { - int index = 10; - byte[] data = [0x00]; - Assert.Throws(() => new TypeLengthValue(data, ref index)); - } - - [Fact] - public void Constructor_ValidMinimalArray() - { - int index = 0; - byte[] data = [0x00]; - var tlv = new TypeLengthValue(data, ref index); - - Assert.Equal(ASN1Type.V_ASN1_EOC, tlv.Type); - Assert.Equal(default, tlv.Length); - Assert.Null(tlv.Value); - } - - [Fact] - public void Constructor_EmptyStream_Throws() - { - Stream data = new MemoryStream([], 0, 0, false, false); - Assert.Throws(() => new TypeLengthValue(data)); - } - - [Fact] - public void Constructor_ValidMinimalStream() - { - Stream data = new MemoryStream([0x00]); - var tlv = new TypeLengthValue(data); - - Assert.Equal(ASN1Type.V_ASN1_EOC, tlv.Type); - Assert.Equal(default, tlv.Length); - Assert.Null(tlv.Value); - } - - [Fact] - public void Constructor_ValidBoolean() - { - Stream data = new MemoryStream([0x01, 0x01, 0x01]); - var tlv = new TypeLengthValue(data); - - Assert.Equal(ASN1Type.V_ASN1_BOOLEAN, tlv.Type); - Assert.Equal(1UL, tlv.Length); - Assert.NotNull(tlv.Value); - - byte[]? valueAsArray = tlv.Value as byte[]; - Assert.NotNull(valueAsArray); - byte actual = Assert.Single(valueAsArray); - Assert.Equal(0x01, actual); - } - - [Theory] - [InlineData(new byte[] { 0x26, 0x81, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x82, 0x00, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x83, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x84, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x85, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] - [InlineData(new byte[] { 0x26, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] - public void Constructor_ComplexValue(byte[] arr) - { - Stream data = new MemoryStream(arr); - var tlv = new TypeLengthValue(data); - - Assert.Equal(ASN1Type.V_ASN1_CONSTRUCTED | ASN1Type.V_ASN1_OBJECT, tlv.Type); - Assert.Equal(3UL, tlv.Length); - Assert.NotNull(tlv.Value); - - TypeLengthValue[]? valueAsArray = tlv.Value as TypeLengthValue[]; - Assert.NotNull(valueAsArray); - TypeLengthValue actual = Assert.Single(valueAsArray); - - Assert.Equal(ASN1Type.V_ASN1_BOOLEAN, actual.Type); - Assert.Equal(1UL, actual.Length); - Assert.NotNull(actual.Value); - } - - [Theory] - [InlineData(new byte[] { 0x26, 0x80 })] - [InlineData(new byte[] { 0x26, 0x89 })] - public void Constructor_ComplexValueInvalidLength_Throws(byte[] arr) - { - Stream data = new MemoryStream(arr); - Assert.Throws(() => new TypeLengthValue(data)); - } - - #endregion - #region Formatting [Fact] public void Format_EOC() { string expected = "Type: V_ASN1_EOC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_EOC, 0, null); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -132,7 +21,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ZeroLength() { string expected = "Type: V_ASN1_NULL, Length: 0"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_NULL, 0, null); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -141,7 +30,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidConstructed() { string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 1, Value: [INVALID DATA TYPE]"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, 1, (object?)false); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = (object?)false }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -150,8 +39,8 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidConstructed() { string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 3, Value:\n Type: V_ASN1_BOOLEAN, Length: 1, Value: True"; - var boolTlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 1, new byte[] { 0x01 }); - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, 3, new TypeLengthValue[] { boolTlv }); + var boolTlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } }; + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new TypeLengthValue[] { boolTlv } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -160,7 +49,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidDataType() { string expected = "Type: V_ASN1_OBJECT, Length: 1, Value: [INVALID DATA TYPE]"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT, 1, (object?)false); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = (object?)false }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -169,7 +58,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidLength() { string expected = "Type: V_ASN1_NULL, Length: 1, Value: [NO DATA]"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_NULL, 1, Array.Empty()); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty() }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -178,7 +67,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidBooleanLength() { string expected = "Type: V_ASN1_BOOLEAN, Length: 2 [Expected length of 1], Value: True"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 2, new byte[] { 0x01 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -187,7 +76,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidBooleanArrayLength() { string expected = "Type: V_ASN1_BOOLEAN, Length: 1 [Expected value length of 1], Value: True"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 1, new byte[] { 0x01, 0x00 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -196,7 +85,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidBoolean() { string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 1, new byte[] { 0x01 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -205,7 +94,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidInteger() { string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_INTEGER, 1, new byte[] { 0x01 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -214,7 +103,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidBitString_NoBits() { string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 0 unused bits"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BIT_STRING, 1, new byte[] { 0x00 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -223,7 +112,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidBitString_Bits() { string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 1 unused bits: 01"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BIT_STRING, 1, new byte[] { 0x01, 0x01 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -232,7 +121,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidOctetString() { string expected = "Type: V_ASN1_OCTET_STRING, Length: 1, Value: 01"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OCTET_STRING, 1, new byte[] { 0x01 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -241,7 +130,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidObject() { string expected = "Type: V_ASN1_OBJECT, Length: 3, Value: 0.1.2.3 (/ITU-T/1/2/3)"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT, 3, new byte[] { 0x01, 0x02, 0x03 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -250,7 +139,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidUTF8String() { string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_UTF8STRING, 3, new byte[] { 0x41, 0x42, 0x43 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -259,7 +148,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidPrintableString() { string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_PRINTABLESTRING, 3, new byte[] { 0x41, 0x42, 0x43 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -268,7 +157,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidTeletexString() { string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_TELETEXSTRING, 3, new byte[] { 0x41, 0x42, 0x43 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -277,7 +166,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidIA5String() { string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_IA5STRING, 3, new byte[] { 0x41, 0x42, 0x43 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -286,7 +175,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidUTCTime() { string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_UTCTIME, 3, new byte[] { 0x41, 0x42, 0x43 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -295,7 +184,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidUTCTime() { string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: 1980-01-01 00:00:00"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_UTCTIME, 3, new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -304,7 +193,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidBmpString() { string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BMPSTRING, 6, new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -313,7 +202,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidUnformatted() { string expected = "Type: V_ASN1_OBJECT_DESCRIPTOR, Length: 1, Value: 01"; - var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, 1, new byte[] { 0x01 }); + var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } diff --git a/SabreTools.Serialization.Test/Deserializers/TypeLengthValueTests.cs b/SabreTools.Serialization.Test/Deserializers/TypeLengthValueTests.cs new file mode 100644 index 00000000..837c979f --- /dev/null +++ b/SabreTools.Serialization.Test/Deserializers/TypeLengthValueTests.cs @@ -0,0 +1,145 @@ +using System.IO; +using System.Linq; +using SabreTools.Serialization.Deserializers; +using Xunit; + +namespace SabreTools.Serialization.Test.Deserializers +{ + public class TypeLengthValueTests + { + [Fact] + public void NullArray_Null() + { + byte[]? data = null; + int offset = 0; + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data, offset); + Assert.Null(actual); + } + + [Fact] + public void EmptyArray_Null() + { + byte[]? data = []; + int offset = 0; + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data, offset); + Assert.Null(actual); + } + + [Fact] + public void InvalidArray_Null() + { + byte[]? data = [.. Enumerable.Repeat(0xFF, 1024)]; + int offset = 0; + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data, offset); + Assert.Null(actual); + } + + [Fact] + public void NullStream_Null() + { + Stream? data = null; + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.Null(actual); + } + + [Fact] + public void EmptyStream_Null() + { + Stream? data = new MemoryStream([]); + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.Null(actual); + } + + [Fact] + public void InvalidStream_Null() + { + Stream? data = new MemoryStream([.. Enumerable.Repeat(0xFF, 1024)]); + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.Null(actual); + } + + [Fact] + public void ValidMinimalStream_NotNull() + { + Stream data = new MemoryStream([0x00]); + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.NotNull(actual); + Assert.Equal(Serialization.ASN1.ASN1Type.V_ASN1_EOC, actual.Type); + Assert.Equal(default, actual.Length); + Assert.Null(actual.Value); + } + + [Fact] + public void ValidBoolean_NotNull() + { + Stream data = new MemoryStream([0x01, 0x01, 0x01]); + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.NotNull(actual); + Assert.Equal(Serialization.ASN1.ASN1Type.V_ASN1_BOOLEAN, actual.Type); + Assert.Equal(1UL, actual.Length); + Assert.NotNull(actual.Value); + + byte[]? valueAsArray = actual.Value as byte[]; + Assert.NotNull(valueAsArray); + byte actualValue = Assert.Single(valueAsArray); + Assert.Equal(0x01, actualValue); + } + + [Theory] + [InlineData(new byte[] { 0x26, 0x81, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x82, 0x00, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x83, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x84, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x85, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] + [InlineData(new byte[] { 0x26, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })] + public void ComplexValue_NotNull(byte[] arr) + { + Stream data = new MemoryStream(arr); + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.NotNull(actual); + Assert.Equal(Serialization.ASN1.ASN1Type.V_ASN1_CONSTRUCTED | Serialization.ASN1.ASN1Type.V_ASN1_OBJECT, actual.Type); + Assert.Equal(3UL, actual.Length); + Assert.NotNull(actual.Value); + + Serialization.ASN1.TypeLengthValue[]? valueAsArray = actual.Value as Serialization.ASN1.TypeLengthValue[]; + Assert.NotNull(valueAsArray); + Serialization.ASN1.TypeLengthValue actualSub = Assert.Single(valueAsArray); + + Assert.Equal(Serialization.ASN1.ASN1Type.V_ASN1_BOOLEAN, actualSub.Type); + Assert.Equal(1UL, actualSub.Length); + Assert.NotNull(actualSub.Value); + } + + [Theory] + [InlineData(new byte[] { 0x26, 0x80 })] + [InlineData(new byte[] { 0x26, 0x89 })] + public void ComplexValueInvalidLength_Null(byte[] arr) + { + Stream data = new MemoryStream(arr); + var deserializer = new TypeLengthValue(); + + var actual = deserializer.Deserialize(data); + Assert.Null(actual); + } + } +} \ No newline at end of file diff --git a/SabreTools.Serialization/ASN1/AbstractSyntaxNotationOne.cs b/SabreTools.Serialization/ASN1/AbstractSyntaxNotationOne.cs index 6a2a526e..fb8a6fab 100644 --- a/SabreTools.Serialization/ASN1/AbstractSyntaxNotationOne.cs +++ b/SabreTools.Serialization/ASN1/AbstractSyntaxNotationOne.cs @@ -39,13 +39,19 @@ namespace SabreTools.Serialization.ASN1 if (data.Position < 0 || data.Position >= data.Length) throw new IndexOutOfRangeException(nameof(data)); + // Create the deserializer + var deserializer = new Deserializers.TypeLengthValue(); + // Create the output list to return var topLevelValues = new List(); // Loop through the data and return all top-level values while (data.Position < data.Length) { - var topLevelValue = new TypeLengthValue(data); + var topLevelValue = deserializer.Deserialize(data); + if (topLevelValue == null) + break; + topLevelValues.Add(topLevelValue); } diff --git a/SabreTools.Serialization/ASN1/TypeLengthValue.cs b/SabreTools.Serialization/ASN1/TypeLengthValue.cs index e83aa611..ebd3d41a 100644 --- a/SabreTools.Serialization/ASN1/TypeLengthValue.cs +++ b/SabreTools.Serialization/ASN1/TypeLengthValue.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Numerics; using System.Text; -using SabreTools.IO.Extensions; namespace SabreTools.Serialization.ASN1 { @@ -15,56 +12,17 @@ namespace SabreTools.Serialization.ASN1 /// /// The ASN.1 type /// - public ASN1Type Type { get; private set; } + public ASN1Type Type { get; set; } /// /// Length of the value /// - public ulong Length { get; private set; } + public ulong Length { get; set; } /// /// Generic value associated with /// - public object? Value { get; private set; } - - /// - /// Manual constructor - /// - public TypeLengthValue(ASN1Type type, ulong length, object? value) - { - Type = type; - Length = length; - Value = value; - } - - /// - /// Read from the source data array at an index - /// - /// Byte array representing data to read - /// Index within the array to read at - public TypeLengthValue(byte[] data, ref int index) - { - // If the data is invalid - if (data.Length == 0) - throw new InvalidDataException(nameof(data)); - if (index < 0 || index >= data.Length) - throw new IndexOutOfRangeException(nameof(index)); - - using var stream = new MemoryStream(data); - stream.Seek(index, SeekOrigin.Begin); - if (!Parse(stream)) - throw new InvalidDataException(nameof(data)); - } - - /// - /// Read from the source data stream - /// - /// Stream representing data to read - public TypeLengthValue(Stream data) - { - if (!Parse(data)) - throw new InvalidDataException(nameof(data)); - } + public object? Value { get; set; } /// /// Format the TLV as a string @@ -214,120 +172,5 @@ namespace SabreTools.Serialization.ASN1 // Return the formatted string return formatBuilder.ToString(); } - - /// - /// Parse a stream into TLV data - /// - /// Stream representing data to read - /// Indication if parsing was successful - private bool Parse(Stream data) - { - // If the data is invalid - if (data.Length == 0 || !data.CanRead) - return false; - if (data.Position < 0 || data.Position >= data.Length) - throw new IndexOutOfRangeException(nameof(data)); - - // Get the type and modifiers - Type = (ASN1Type)data.ReadByteValue(); - - // If we have an end indicator, we just return - if (Type == ASN1Type.V_ASN1_EOC) - return true; - - // Get the length of the value - Length = ReadLength(data); - - // Read the value -#if NET20 || NET35 - if ((Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0) -#else - if (Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED)) -#endif - { - var valueList = new List(); - - long currentIndex = data.Position; - while (data.Position < currentIndex + (long)Length) - { - valueList.Add(new TypeLengthValue(data)); - } - - Value = valueList.ToArray(); - } - else - { - // TODO: Get more granular based on type - Value = data.ReadBytes((int)Length); - } - - return true; - } - - /// - /// Reads the length field for a type - /// - /// Stream representing data to read - /// The length value read from the array - private static ulong ReadLength(Stream data) - { - // If the data is invalid - if (data.Length == 0 || !data.CanRead) - throw new InvalidDataException(nameof(data)); - if (data.Position < 0 || data.Position >= data.Length) - throw new IndexOutOfRangeException(nameof(data)); - - // Read the first byte, assuming it's the length - byte length = data.ReadByteValue(); - - // If the bit 7 is not set, then use the value as it is - if ((length & 0x80) == 0) - return length; - - // Otherwise, use the value as the number of remaining bytes to read - int bytesToRead = length & ~0x80; - - // Assemble the length based on byte count - ulong fullLength = 0; - switch (bytesToRead) - { - case 8: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 7; - case 7: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 6; - case 6: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 5; - case 5: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 4; - case 4: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 3; - case 3: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 2; - case 2: - fullLength |= data.ReadByteValue(); - fullLength <<= 8; - goto case 1; - case 1: - fullLength |= data.ReadByteValue(); - break; - - default: - throw new InvalidOperationException(); - } - - return fullLength; - } } } diff --git a/SabreTools.Serialization/Deserializers/TypeLengthValue.cs b/SabreTools.Serialization/Deserializers/TypeLengthValue.cs new file mode 100644 index 00000000..1d4d6c52 --- /dev/null +++ b/SabreTools.Serialization/Deserializers/TypeLengthValue.cs @@ -0,0 +1,146 @@ +using System.Collections.Generic; +using System.IO; +using SabreTools.IO.Extensions; + +namespace SabreTools.Serialization.Deserializers +{ + public class TypeLengthValue : BaseBinaryDeserializer + { + /// + public override ASN1.TypeLengthValue? Deserialize(Stream? data) + { + // If the data is invalid + if (data == null || !data.CanRead) + return null; + + try + { + // Cache the current offset + long initialOffset = data.Position; + + var tlv = ParseTypeLengthValue(data); + if (tlv == null) + return null; + + // Return the Type/Length/Value + return tlv; + } + catch + { + // Ignore the actual error + return null; + } + } + + /// + /// Parse a Stream into a TypeLengthValue + /// + /// Stream to parse + /// Filled TypeLengthValue on success, null on error + public ASN1.TypeLengthValue? ParseTypeLengthValue(Stream data) + { + var obj = new ASN1.TypeLengthValue(); + + // Get the type and modifiers + obj.Type = (ASN1.ASN1Type)data.ReadByteValue(); + + // If we have an end indicator, we just return + if (obj.Type == ASN1.ASN1Type.V_ASN1_EOC) + return obj; + + // Get the length of the value + ulong? length = ReadLength(data); + if (length == null) + return null; + + // Set the length + obj.Length = length.Value; + + // Read the value +#if NET20 || NET35 + if ((obj.Type & ASN1.ASN1Type.V_ASN1_CONSTRUCTED) != 0) +#else + if (obj.Type.HasFlag(ASN1.ASN1Type.V_ASN1_CONSTRUCTED)) +#endif + { + var valueList = new List(); + + long currentIndex = data.Position; + while (data.Position < currentIndex + (long)obj.Length) + { + var value = ParseTypeLengthValue(data); + valueList.Add(value); + } + + obj.Value = valueList.ToArray(); + } + else + { + // TODO: Get more granular based on type + obj.Value = data.ReadBytes((int)obj.Length); + } + + return obj; + } + + /// + /// Reads the length field for a type + /// + /// Stream representing data to read + /// The length value read from the array + private static ulong? ReadLength(Stream data) + { + // Read the first byte, assuming it's the length + byte length = data.ReadByteValue(); + + // If the bit 7 is not set, then use the value as it is + if ((length & 0x80) == 0) + return length; + + // Otherwise, use the value as the number of remaining bytes to read + int bytesToRead = length & ~0x80; + + // Assemble the length based on byte count + ulong fullLength = 0; + switch (bytesToRead) + { + case 8: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 7; + case 7: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 6; + case 6: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 5; + case 5: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 4; + case 4: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 3; + case 3: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 2; + case 2: + fullLength |= data.ReadByteValue(); + fullLength <<= 8; + goto case 1; + case 1: + fullLength |= data.ReadByteValue(); + break; + + default: + return null; + } + + return fullLength; + } + } +}