From 5fe3f14419e4e697bbfc404eab155a74e9b54c4e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 24 Sep 2025 09:00:48 -0400 Subject: [PATCH] Make TypeLengthValue more model-like --- .../TypeLengthValueTests.cs | 49 +++--- .../ASN1/TypeLengthValue.cs | 155 +---------------- .../Extensions/TypeLengthValue.cs | 158 ++++++++++++++++++ .../Printers/PortableExecutable.cs | 2 +- 4 files changed, 185 insertions(+), 179 deletions(-) rename SabreTools.Serialization.Test/{ASN1 => Extensions}/TypeLengthValueTests.cs (61%) create mode 100644 SabreTools.Serialization/Extensions/TypeLengthValue.cs diff --git a/SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs b/SabreTools.Serialization.Test/Extensions/TypeLengthValueTests.cs similarity index 61% rename from SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs rename to SabreTools.Serialization.Test/Extensions/TypeLengthValueTests.cs index bf68af45..3a8fdda7 100644 --- a/SabreTools.Serialization.Test/ASN1/TypeLengthValueTests.cs +++ b/SabreTools.Serialization.Test/Extensions/TypeLengthValueTests.cs @@ -1,8 +1,9 @@ using System; using SabreTools.Serialization.ASN1; +using SabreTools.Serialization.Extensions; using Xunit; -namespace SabreTools.Serialization.Test.ASN1 +namespace SabreTools.Serialization.Test.Extensions { public class TypeLengthValueTests { @@ -12,7 +13,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_EOC() { string expected = "Type: V_ASN1_EOC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -21,7 +22,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ZeroLength() { string expected = "Type: V_ASN1_NULL, Length: 0"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -30,7 +31,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 { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = (object?)false }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = (object?)false }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -39,8 +40,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 { 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 } }; + var boolTlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new Serialization.ASN1.TypeLengthValue[] { boolTlv } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -49,7 +50,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 { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = (object?)false }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = (object?)false }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -58,7 +59,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 { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty() }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty() }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -67,7 +68,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 { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -76,7 +77,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 { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -85,7 +86,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidBoolean() { string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -94,7 +95,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidInteger() { string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -103,7 +104,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 { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -112,7 +113,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 { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -121,7 +122,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 { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -130,7 +131,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 { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -139,7 +140,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidUTF8String() { string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -148,7 +149,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidPrintableString() { string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -157,7 +158,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidTeletexString() { string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -166,7 +167,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidIA5String() { string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -175,7 +176,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_InvalidUTCTime() { string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; + var tlv = new Serialization.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } }; string actual = tlv.Format(); Assert.Equal(expected, actual); } @@ -184,7 +185,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 { 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 } }; + var tlv = new Serialization.ASN1.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); } @@ -193,7 +194,7 @@ namespace SabreTools.Serialization.Test.ASN1 public void Format_ValidBmpString() { string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC"; - var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } }; + var tlv = new Serialization.ASN1.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); } @@ -202,7 +203,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 { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } }; + var tlv = new Serialization.ASN1.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/ASN1/TypeLengthValue.cs b/SabreTools.Serialization/ASN1/TypeLengthValue.cs index ebd3d41a..7929741c 100644 --- a/SabreTools.Serialization/ASN1/TypeLengthValue.cs +++ b/SabreTools.Serialization/ASN1/TypeLengthValue.cs @@ -1,8 +1,4 @@ -using System; -using System.Numerics; -using System.Text; - -namespace SabreTools.Serialization.ASN1 +namespace SabreTools.Serialization.ASN1 { /// /// ASN.1 type/length/value class that all types are based on @@ -23,154 +19,5 @@ namespace SabreTools.Serialization.ASN1 /// Generic value associated with /// public object? Value { get; set; } - - /// - /// Format the TLV as a string - /// - /// Padding level of the item when formatting - /// String representing the TLV, if possible - public string Format(int paddingLevel = 0) - { - // Create the left-padding string - string padding = new(' ', paddingLevel); - - // Create the string builder - var formatBuilder = new StringBuilder(); - - // Append the type - formatBuilder.Append($"{padding}Type: {Type}"); - if (Type == ASN1Type.V_ASN1_EOC) - return formatBuilder.ToString(); - - // Append the length - formatBuilder.Append($", Length: {Length}"); - if (Length == 0) - return formatBuilder.ToString(); - - // If we have a constructed type -#if NET20 || NET35 - if ((Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0) -#else - if (Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED)) -#endif - { - if (Value is not TypeLengthValue[] valueAsObjectArray) - { - formatBuilder.Append(", Value: [INVALID DATA TYPE]"); - return formatBuilder.ToString(); - } - - formatBuilder.Append(", Value:\n"); - for (int i = 0; i < valueAsObjectArray.Length; i++) - { - var child = valueAsObjectArray[i]; - string childString = child.Format(paddingLevel + 1); - formatBuilder.Append($"{childString}\n"); - } - - return formatBuilder.ToString().TrimEnd('\n'); - } - - // Get the value as a byte array - if (Value is not byte[] valueAsByteArray) - { - formatBuilder.Append(", Value: [INVALID DATA TYPE]"); - return formatBuilder.ToString(); - } - else if (valueAsByteArray.Length == 0) - { - formatBuilder.Append(", Value: [NO DATA]"); - return formatBuilder.ToString(); - } - - // If we have a primitive type - switch (Type) - { - /// - case ASN1Type.V_ASN1_BOOLEAN: - if (Length > 1) - formatBuilder.Append($" [Expected length of 1]"); - else if (valueAsByteArray.Length > 1) - formatBuilder.Append($" [Expected value length of 1]"); - - bool booleanValue = valueAsByteArray[0] != 0x00; - formatBuilder.Append($", Value: {booleanValue}"); - break; - - /// - case ASN1Type.V_ASN1_INTEGER: - Array.Reverse(valueAsByteArray); - var integerValue = new BigInteger(valueAsByteArray); - formatBuilder.Append($", Value: {integerValue}"); - break; - - /// - case ASN1Type.V_ASN1_BIT_STRING: - // TODO: Read into a BitArray and print that out instead? - int unusedBits = valueAsByteArray[0]; - if (unusedBits == 0) - formatBuilder.Append($", Value with {unusedBits} unused bits"); - else - formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray, 1).Replace('-', ' ')}"); - break; - - /// - case ASN1Type.V_ASN1_OCTET_STRING: - formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}"); - break; - - /// - /// - case ASN1Type.V_ASN1_OBJECT: - // Derive array of values - ulong[] objectNodes = ObjectIdentifier.ParseDERIntoArray(valueAsByteArray, Length); - - // Append the dot and modified OID-IRI notations - string? dotNotationString = ObjectIdentifier.ParseOIDToDotNotation(objectNodes); - string? oidIriString = ObjectIdentifier.ParseOIDToOIDIRINotation(objectNodes); - formatBuilder.Append($", Value: {dotNotationString} ({oidIriString})"); - break; - - /// - case ASN1Type.V_ASN1_UTF8STRING: - formatBuilder.Append($", Value: {Encoding.UTF8.GetString(valueAsByteArray)}"); - break; - - /// - case ASN1Type.V_ASN1_PRINTABLESTRING: - formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}"); - break; - - //case ASN1Type.V_ASN1_T61STRING: - case ASN1Type.V_ASN1_TELETEXSTRING: - formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}"); - break; - - /// - case ASN1Type.V_ASN1_IA5STRING: - formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}"); - break; - - case ASN1Type.V_ASN1_UTCTIME: - string utctimeString = Encoding.ASCII.GetString(valueAsByteArray); - if (DateTime.TryParse(utctimeString, out DateTime utctimeDateTime)) - formatBuilder.Append($", Value: {utctimeDateTime:yyyy-MM-dd HH:mm:ss}"); - else - formatBuilder.Append($", Value: {utctimeString}"); - break; - - /// - case ASN1Type.V_ASN1_BMPSTRING: - formatBuilder.Append($", Value: {Encoding.Unicode.GetString(valueAsByteArray)}"); - break; - - default: - formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}"); - break; - } - - // Return the formatted string - return formatBuilder.ToString(); - } } } diff --git a/SabreTools.Serialization/Extensions/TypeLengthValue.cs b/SabreTools.Serialization/Extensions/TypeLengthValue.cs new file mode 100644 index 00000000..638e73db --- /dev/null +++ b/SabreTools.Serialization/Extensions/TypeLengthValue.cs @@ -0,0 +1,158 @@ +using System; +using System.Numerics; +using System.Text; + +namespace SabreTools.Serialization.Extensions +{ + public static class TypeLengthValue + { + /// + /// Format a TypeLengthValue as a string + /// + /// Padding level of the item when formatting + /// String representing the TypeLengthValue, if possible + public static string Format(this ASN1.TypeLengthValue tlv, int paddingLevel = 0) + { + // Create the left-padding string + string padding = new(' ', paddingLevel); + + // Create the string builder + var formatBuilder = new StringBuilder(); + + // Append the type + formatBuilder.Append($"{padding}Type: {tlv.Type}"); + if (tlv.Type == ASN1.ASN1Type.V_ASN1_EOC) + return formatBuilder.ToString(); + + // Append the length + formatBuilder.Append($", Length: {tlv.Length}"); + if (tlv.Length == 0) + return formatBuilder.ToString(); + + // If we have a constructed type +#if NET20 || NET35 + if ((tlv.Type & ASN1.ASN1Type.V_ASN1_CONSTRUCTED) != 0) +#else + if (tlv.Type.HasFlag(ASN1.ASN1Type.V_ASN1_CONSTRUCTED)) +#endif + { + if (tlv.Value is not ASN1.TypeLengthValue[] valueAsObjectArray) + { + formatBuilder.Append(", Value: [INVALID DATA TYPE]"); + return formatBuilder.ToString(); + } + + formatBuilder.Append(", Value:\n"); + for (int i = 0; i < valueAsObjectArray.Length; i++) + { + var child = valueAsObjectArray[i]; + string childString = child.Format(paddingLevel + 1); + formatBuilder.Append($"{childString}\n"); + } + + return formatBuilder.ToString().TrimEnd('\n'); + } + + // Get the value as a byte array + if (tlv.Value is not byte[] valueAsByteArray) + { + formatBuilder.Append(", Value: [INVALID DATA TYPE]"); + return formatBuilder.ToString(); + } + else if (valueAsByteArray.Length == 0) + { + formatBuilder.Append(", Value: [NO DATA]"); + return formatBuilder.ToString(); + } + + // If we have a primitive type + switch (tlv.Type) + { + /// + case ASN1.ASN1Type.V_ASN1_BOOLEAN: + if (tlv.Length > 1) + formatBuilder.Append($" [Expected length of 1]"); + else if (valueAsByteArray.Length > 1) + formatBuilder.Append($" [Expected value length of 1]"); + + bool booleanValue = valueAsByteArray[0] != 0x00; + formatBuilder.Append($", Value: {booleanValue}"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_INTEGER: + Array.Reverse(valueAsByteArray); + var integerValue = new BigInteger(valueAsByteArray); + formatBuilder.Append($", Value: {integerValue}"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_BIT_STRING: + // TODO: Read into a BitArray and print that out instead? + int unusedBits = valueAsByteArray[0]; + if (unusedBits == 0) + formatBuilder.Append($", Value with {unusedBits} unused bits"); + else + formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray, 1).Replace('-', ' ')}"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_OCTET_STRING: + formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}"); + break; + + /// + /// + case ASN1.ASN1Type.V_ASN1_OBJECT: + // Derive array of values + ulong[] objectNodes = ASN1.ObjectIdentifier.ParseDERIntoArray(valueAsByteArray, tlv.Length); + + // Append the dot and modified OID-IRI notations + string? dotNotationString = ASN1.ObjectIdentifier.ParseOIDToDotNotation(objectNodes); + string? oidIriString = ASN1.ObjectIdentifier.ParseOIDToOIDIRINotation(objectNodes); + formatBuilder.Append($", Value: {dotNotationString} ({oidIriString})"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_UTF8STRING: + formatBuilder.Append($", Value: {Encoding.UTF8.GetString(valueAsByteArray)}"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_PRINTABLESTRING: + formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}"); + break; + + //case ASN1Type.V_ASN1_T61STRING: + case ASN1.ASN1Type.V_ASN1_TELETEXSTRING: + formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_IA5STRING: + formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}"); + break; + + case ASN1.ASN1Type.V_ASN1_UTCTIME: + string utctimeString = Encoding.ASCII.GetString(valueAsByteArray); + if (DateTime.TryParse(utctimeString, out DateTime utctimeDateTime)) + formatBuilder.Append($", Value: {utctimeDateTime:yyyy-MM-dd HH:mm:ss}"); + else + formatBuilder.Append($", Value: {utctimeString}"); + break; + + /// + case ASN1.ASN1Type.V_ASN1_BMPSTRING: + formatBuilder.Append($", Value: {Encoding.Unicode.GetString(valueAsByteArray)}"); + break; + + default: + formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}"); + break; + } + + // Return the formatted string + return formatBuilder.ToString(); + } + } +} \ No newline at end of file diff --git a/SabreTools.Serialization/Printers/PortableExecutable.cs b/SabreTools.Serialization/Printers/PortableExecutable.cs index 4851d2ba..4fa5f638 100644 --- a/SabreTools.Serialization/Printers/PortableExecutable.cs +++ b/SabreTools.Serialization/Printers/PortableExecutable.cs @@ -478,7 +478,7 @@ namespace SabreTools.Serialization.Printers } else { - foreach (TypeLengthValue tlv in topLevelValues) + foreach (ASN1.TypeLengthValue tlv in topLevelValues) { string tlvString = tlv.Format(paddingLevel: 4); builder.AppendLine(tlvString);