diff --git a/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Der.cs b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Der.cs index 40f035a22baff8..aef49d207dcb87 100644 --- a/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Der.cs +++ b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Der.cs @@ -4,8 +4,8 @@ using System; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace Internal.Cryptography { @@ -19,10 +19,8 @@ internal static partial class AsymmetricAlgorithmHelpers /// public static byte[] ConvertIeee1363ToDer(ReadOnlySpan input) { - using (AsnWriter writer = WriteIeee1363ToDer(input)) - { - return writer.Encode(); - } + AsnWriter writer = WriteIeee1363ToDer(input); + return writer.Encode(); } internal static bool TryConvertIeee1363ToDer( @@ -30,10 +28,8 @@ internal static bool TryConvertIeee1363ToDer( Span destination, out int bytesWritten) { - using (AsnWriter writer = WriteIeee1363ToDer(input)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = WriteIeee1363ToDer(input); + return writer.TryEncode(destination, out bytesWritten); } private static AsnWriter WriteIeee1363ToDer(ReadOnlySpan input) @@ -73,16 +69,23 @@ internal static int ConvertDerToIeee1363(ReadOnlySpan input, int fieldSize Debug.Assert(destination.Length >= encodedSize); - AsnValueReader reader = new AsnValueReader(input, AsnEncodingRules.DER); - AsnValueReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); - ReadOnlySpan rDer = sequenceReader.ReadIntegerBytes(); - ReadOnlySpan sDer = sequenceReader.ReadIntegerBytes(); - sequenceReader.ThrowIfNotEmpty(); - - CopySignatureField(rDer, destination.Slice(0, fieldSizeBytes)); - CopySignatureField(sDer, destination.Slice(fieldSizeBytes, fieldSizeBytes)); - return encodedSize; + try + { + AsnValueReader reader = new AsnValueReader(input, AsnEncodingRules.DER); + AsnValueReader sequenceReader = reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + ReadOnlySpan rDer = sequenceReader.ReadIntegerBytes(); + ReadOnlySpan sDer = sequenceReader.ReadIntegerBytes(); + sequenceReader.ThrowIfNotEmpty(); + + CopySignatureField(rDer, destination.Slice(0, fieldSizeBytes)); + CopySignatureField(sDer, destination.Slice(fieldSizeBytes, fieldSizeBytes)); + return encodedSize; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static int GetMaxDerSignatureSize(int fieldSizeBits) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.GetIntegerBytes.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.GetIntegerBytes.cs index 9026991b9a03e5..d7606b1aa0b71e 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.GetIntegerBytes.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.ASN1.GetIntegerBytes.cs @@ -2,9 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; - +using System.Formats.Asn1; +using System.Security.Cryptography; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -35,8 +36,17 @@ internal static byte[] GetAsn1IntegerBytes(SafeSharedAsn1IntegerHandle asn1Integ (handle, buf) => EncodeAsn1Integer(handle, buf), asn1Integer); - AsnReader reader = new AsnReader(derEncoded, AsnEncodingRules.DER); - return reader.ReadIntegerBytes().ToArray(); + try + { + return AsnDecoder.ReadIntegerBytes( + derEncoded, + AsnEncodingRules.DER, + out _).ToArray(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml.cs index ccea20e13954bf..6d907ad7c57d8e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml.cs @@ -4,16 +4,15 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { [StructLayout(LayoutKind.Sequential)] internal partial struct AlgorithmIdentifierAsn { - internal Oid Algorithm; + internal string Algorithm; internal ReadOnlyMemory? Parameters; internal void Encode(AsnWriter writer) @@ -25,11 +24,25 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(Algorithm); + try + { + writer.WriteObjectIdentifier(Algorithm); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } if (Parameters.HasValue) { - writer.WriteEncodedValue(Parameters.Value.Span); + try + { + writer.WriteEncodedValue(Parameters.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSequence(tag); @@ -42,11 +55,18 @@ internal static AlgorithmIdentifierAsn Decode(ReadOnlyMemory encoded, AsnE internal static AlgorithmIdentifierAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out AlgorithmIdentifierAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out AlgorithmIdentifierAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out AlgorithmIdentifierAsn decoded) @@ -55,6 +75,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out AlgorithmIdentifierAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out AlgorithmIdentifierAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.manual.cs index 65eb4e68c979bf..2c361266651cac 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.manual.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.manual.cs @@ -15,7 +15,7 @@ public AttributeAsn(AsnEncodedData attribute) throw new ArgumentNullException(nameof(attribute)); } - AttrType = new Oid(attribute.Oid!); + AttrType = attribute.Oid!.Value!; AttrValues = new[] { new ReadOnlyMemory(attribute.RawData) }; } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs index 47e0d8635822ac..4e4ab3960bb441 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs @@ -5,16 +5,15 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { [StructLayout(LayoutKind.Sequential)] internal partial struct AttributeAsn { - internal Oid AttrType; + internal string AttrType; internal ReadOnlyMemory[] AttrValues; internal void Encode(AsnWriter writer) @@ -26,12 +25,26 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(AttrType); + try + { + writer.WriteObjectIdentifier(AttrType); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PushSetOf(); for (int i = 0; i < AttrValues.Length; i++) { - writer.WriteEncodedValue(AttrValues[i].Span); + try + { + writer.WriteEncodedValue(AttrValues[i].Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSetOf(); @@ -45,11 +58,18 @@ internal static AttributeAsn Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static AttributeAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out AttributeAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out AttributeAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out AttributeAsn decoded) @@ -58,6 +78,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out AttributeAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out AttributeAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/CurveAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/CurveAsn.xml.cs index 83f250782e15ed..eb2983a2b55880 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/CurveAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/CurveAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -31,7 +30,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (Seed.HasValue) { - writer.WriteBitString(Seed.Value.Span); + writer.WriteBitString(Seed.Value.Span, 0); } writer.PopSequence(tag); @@ -44,11 +43,18 @@ internal static CurveAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules r internal static CurveAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CurveAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CurveAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CurveAsn decoded) @@ -57,6 +63,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CurveAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CurveAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -65,7 +83,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.A = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } @@ -75,7 +93,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read } - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.B = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } @@ -88,7 +106,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.PrimitiveBitString)) { - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan)) { decoded.Seed = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DigestInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DigestInfoAsn.xml.cs index 64c0b1b70127a4..e242ec278305d2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DigestInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DigestInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -37,11 +36,18 @@ internal static DigestInfoAsn Decode(ReadOnlyMemory encoded, AsnEncodingRu internal static DigestInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out DigestInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out DigestInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out DigestInfoAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out DigestInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out DigestInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -59,7 +77,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.DigestAlgorithm); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Digest = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs index eda1cfd4eac1ef..abc27ba7f7ce35 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DirectoryStringAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -77,7 +76,14 @@ internal void Encode(AsnWriter writer) } } - writer.WriteEncodedValue(UniversalString.Value.Span); + try + { + writer.WriteEncodedValue(UniversalString.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } wroteValue = true; } @@ -107,14 +113,33 @@ internal void Encode(AsnWriter writer) internal static DirectoryStringAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out DirectoryStringAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out DirectoryStringAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out DirectoryStringAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out DirectoryStringAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DssParms.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DssParms.xml.cs index a45f9f585fc5bf..8839e6457cb9f9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/DssParms.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/DssParms.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -39,11 +38,18 @@ internal static DssParms Decode(ReadOnlyMemory encoded, AsnEncodingRules r internal static DssParms Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out DssParms decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out DssParms decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out DssParms decoded) @@ -52,6 +58,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out DssParms decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out DssParms decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECDomainParameters.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECDomainParameters.xml.cs index 727797d9687549..5d481c43b1a8ee 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECDomainParameters.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECDomainParameters.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -14,7 +13,7 @@ namespace System.Security.Cryptography.Asn1 internal partial struct ECDomainParameters { internal System.Security.Cryptography.Asn1.SpecifiedECDomain? Specified; - internal Oid? Named; + internal string? Named; #if DEBUG static ECDomainParameters() @@ -53,7 +52,14 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteObjectIdentifier(Named); + try + { + writer.WriteObjectIdentifier(Named); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } wroteValue = true; } @@ -65,14 +71,33 @@ internal void Encode(AsnWriter writer) internal static ECDomainParameters Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out ECDomainParameters decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out ECDomainParameters decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out ECDomainParameters decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out ECDomainParameters decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml index c881bb0cb96936..c5fcf187449e23 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml @@ -14,8 +14,8 @@ publicKey [1] BIT STRING OPTIONAL } --> - + - \ No newline at end of file + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml.cs index bb43eedb9154b1..c416a841c7aec9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml.cs @@ -4,16 +4,15 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { [StructLayout(LayoutKind.Sequential)] internal partial struct ECPrivateKey { - internal byte Version; + internal int Version; internal ReadOnlyMemory PrivateKey; internal System.Security.Cryptography.Asn1.ECDomainParameters? Parameters; internal ReadOnlyMemory? PublicKey; @@ -41,7 +40,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (PublicKey.HasValue) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteBitString(PublicKey.Value.Span); + writer.WriteBitString(PublicKey.Value.Span, 0); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); } @@ -55,11 +54,18 @@ internal static ECPrivateKey Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static ECPrivateKey Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out ECPrivateKey decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out ECPrivateKey decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out ECPrivateKey decoded) @@ -68,6 +74,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out ECPrivateKey decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out ECPrivateKey decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -77,13 +95,13 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (!sequenceReader.TryReadUInt8(out decoded.Version)) + if (!sequenceReader.TryReadInt32(out decoded.Version)) { sequenceReader.ThrowIfNotEmpty(); } - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.PrivateKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } @@ -108,7 +126,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read { explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (explicitReader.TryReadPrimitiveBitString(out _, out tmpSpan)) { decoded.PublicKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs index b1b5c401d7e908..3d70d7a4edbf6c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/EdiPartyNameAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -46,11 +45,18 @@ internal static EdiPartyNameAsn Decode(ReadOnlyMemory encoded, AsnEncoding internal static EdiPartyNameAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EdiPartyNameAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out EdiPartyNameAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EdiPartyNameAsn decoded) @@ -59,6 +65,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EdiPartyNameAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EdiPartyNameAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/EncryptedPrivateKeyInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/EncryptedPrivateKeyInfoAsn.xml.cs index 5e1dbe8faaeeed..598aafcb76adba 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/EncryptedPrivateKeyInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/EncryptedPrivateKeyInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -37,11 +36,18 @@ internal static EncryptedPrivateKeyInfoAsn Decode(ReadOnlyMemory encoded, internal static EncryptedPrivateKeyInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EncryptedPrivateKeyInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out EncryptedPrivateKeyInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EncryptedPrivateKeyInfoAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncryptedPrivateKeyInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncryptedPrivateKeyInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -59,7 +77,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.EncryptionAlgorithm); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.EncryptedData = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/FieldID.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/FieldID.xml.cs index b95903d6c188af..6344dc69933d23 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/FieldID.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/FieldID.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -25,8 +24,22 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(FieldType); - writer.WriteEncodedValue(Parameters.Span); + try + { + writer.WriteObjectIdentifier(FieldType); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + try + { + writer.WriteEncodedValue(Parameters.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(tag); } @@ -37,11 +50,18 @@ internal static FieldID Decode(ReadOnlyMemory encoded, AsnEncodingRules ru internal static FieldID Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out FieldID decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out FieldID decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out FieldID decoded) @@ -50,6 +70,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out FieldID decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out FieldID decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -57,7 +89,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.FieldType = sequenceReader.ReadObjectIdentifierAsString(); + decoded.FieldType = sequenceReader.ReadObjectIdentifier(); tmpSpan = sequenceReader.ReadEncodedValue(); decoded.Parameters = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs index 5525fc315cd610..1a26d9f65a2cf6 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralNameAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -67,7 +66,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String, Rfc822Name); + writer.WriteCharacterString(UniversalTagNumber.IA5String, Rfc822Name, new Asn1Tag(TagClass.ContextSpecific, 1)); wroteValue = true; } @@ -76,7 +75,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String, DnsName); + writer.WriteCharacterString(UniversalTagNumber.IA5String, DnsName, new Asn1Tag(TagClass.ContextSpecific, 2)); wroteValue = true; } @@ -94,7 +93,14 @@ internal void Encode(AsnWriter writer) } } - writer.WriteEncodedValue(X400Address.Value.Span); + try + { + writer.WriteEncodedValue(X400Address.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } wroteValue = true; } @@ -104,7 +110,14 @@ internal void Encode(AsnWriter writer) throw new CryptographicException(); writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteEncodedValue(DirectoryName.Value.Span); + try + { + writer.WriteEncodedValue(DirectoryName.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); wroteValue = true; } @@ -123,7 +136,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String, Uri); + writer.WriteCharacterString(UniversalTagNumber.IA5String, Uri, new Asn1Tag(TagClass.ContextSpecific, 6)); wroteValue = true; } @@ -132,7 +145,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 7), IPAddress.Value.Span); + writer.WriteOctetString(IPAddress.Value.Span, new Asn1Tag(TagClass.ContextSpecific, 7)); wroteValue = true; } @@ -141,7 +154,14 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 8), RegisteredId); + try + { + writer.WriteObjectIdentifier(RegisteredId, new Asn1Tag(TagClass.ContextSpecific, 8)); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } wroteValue = true; } @@ -153,14 +173,33 @@ internal void Encode(AsnWriter writer) internal static GeneralNameAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out GeneralNameAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out GeneralNameAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out GeneralNameAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out GeneralNameAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -178,11 +217,11 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { - decoded.Rfc822Name = reader.ReadCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String); + decoded.Rfc822Name = reader.ReadCharacterString(UniversalTagNumber.IA5String, new Asn1Tag(TagClass.ContextSpecific, 1)); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) { - decoded.DnsName = reader.ReadCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String); + decoded.DnsName = reader.ReadCharacterString(UniversalTagNumber.IA5String, new Asn1Tag(TagClass.ContextSpecific, 2)); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) { @@ -205,12 +244,12 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) { - decoded.Uri = reader.ReadCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String); + decoded.Uri = reader.ReadCharacterString(UniversalTagNumber.IA5String, new Asn1Tag(TagClass.ContextSpecific, 6)); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7))) { - if (reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 7), out tmpSpan)) + if (reader.TryReadPrimitiveOctetString(out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 7))) { decoded.IPAddress = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } @@ -222,7 +261,7 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) { - decoded.RegisteredId = reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 8)); + decoded.RegisteredId = reader.ReadObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 8)); } else { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs index 06fb623cc81c5a..b81ec3a201204d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -56,51 +55,42 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for HashFunc. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + HashFunc.Encode(tmp); + + if (!tmp.EncodedValueEquals(DefaultHashFunc)) { - HashFunc.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultHashFunc)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); } } // DEFAULT value handler for MaskGenFunc. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + MaskGenFunc.Encode(tmp); + + if (!tmp.EncodedValueEquals(DefaultMaskGenFunc)) { - MaskGenFunc.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultMaskGenFunc)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); } } // DEFAULT value handler for PSourceFunc. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + PSourceFunc.Encode(tmp); + + if (!tmp.EncodedValueEquals(DefaultPSourceFunc)) { - PSourceFunc.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultPSourceFunc)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); } } @@ -114,11 +104,18 @@ internal static OaepParamsAsn Decode(ReadOnlyMemory encoded, AsnEncodingRu internal static OaepParamsAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out OaepParamsAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out OaepParamsAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out OaepParamsAsn decoded) @@ -127,6 +124,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OaepParamsAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OaepParamsAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs index 18bf2470c68d2c..42a4147f7b9999 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OtherNameAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -25,9 +24,23 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(TypeId); + try + { + writer.WriteObjectIdentifier(TypeId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(Value.Span); + try + { + writer.WriteEncodedValue(Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); writer.PopSequence(tag); } @@ -39,11 +52,18 @@ internal static OtherNameAsn Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static OtherNameAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out OtherNameAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out OtherNameAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out OtherNameAsn decoded) @@ -52,6 +72,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OtherNameAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OtherNameAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -60,7 +92,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.TypeId = sequenceReader.ReadObjectIdentifierAsString(); + decoded.TypeId = sequenceReader.ReadObjectIdentifier(); explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmpSpan = explicitReader.ReadEncodedValue(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBEParameter.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBEParameter.xml.cs index 2544f2b50c49ad..0d55b3d3756803 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBEParameter.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBEParameter.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -37,11 +36,18 @@ internal static PBEParameter Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static PBEParameter Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PBEParameter decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PBEParameter decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PBEParameter decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PBEParameter decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PBEParameter decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -58,7 +76,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Salt = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBES2Params.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBES2Params.xml.cs index 2d45e582209967..a7b6f9e70f7a83 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBES2Params.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PBES2Params.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -37,11 +36,18 @@ internal static PBES2Params Decode(ReadOnlyMemory encoded, AsnEncodingRule internal static PBES2Params Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PBES2Params decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PBES2Params decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PBES2Params decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PBES2Params decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PBES2Params decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml index 271a95aa4a0af5..3f9723b3d862dc 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml @@ -24,6 +24,6 @@ --> - + - \ No newline at end of file + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs index 78730906951dd0..ca7f36c49d67e0 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -17,7 +16,7 @@ internal partial struct Pbkdf2Params internal System.Security.Cryptography.Asn1.Pbkdf2SaltChoice Salt; internal int IterationCount; - internal byte? KeyLength; + internal int? KeyLength; internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn Prf; #if DEBUG @@ -53,15 +52,12 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for Prf. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - Prf.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + Prf.Encode(tmp); - if (!encoded.SequenceEqual(DefaultPrf)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultPrf)) + { + tmp.CopyTo(writer); } } @@ -75,11 +71,18 @@ internal static Pbkdf2Params Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static Pbkdf2Params Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out Pbkdf2Params decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out Pbkdf2Params decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Pbkdf2Params decoded) @@ -88,6 +91,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Pbkdf2Params decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Pbkdf2Params decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -104,7 +119,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Integer)) { - if (sequenceReader.TryReadUInt8(out byte tmpKeyLength)) + if (sequenceReader.TryReadInt32(out int tmpKeyLength)) { decoded.KeyLength = tmpKeyLength; } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2SaltChoice.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2SaltChoice.xml.cs index 9a1c275d7d19e0..7c2f02619275b1 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2SaltChoice.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2SaltChoice.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -65,14 +64,33 @@ internal void Encode(AsnWriter writer) internal static Pbkdf2SaltChoice Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out Pbkdf2SaltChoice decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out Pbkdf2SaltChoice decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Pbkdf2SaltChoice decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out Pbkdf2SaltChoice decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -83,7 +101,7 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi if (tag.HasSameClassAndValue(Asn1Tag.PrimitiveOctetString)) { - if (reader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (reader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Specified = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/CertBagAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/CertBagAsn.xml.cs index fe4ac883519003..38fef15a46c795 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/CertBagAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/CertBagAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs12 { @@ -25,9 +24,23 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(CertId); + try + { + writer.WriteObjectIdentifier(CertId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(CertValue.Span); + try + { + writer.WriteEncodedValue(CertValue.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); writer.PopSequence(tag); } @@ -39,11 +52,18 @@ internal static CertBagAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules internal static CertBagAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CertBagAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CertBagAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertBagAsn decoded) @@ -52,6 +72,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertBagAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertBagAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -60,7 +92,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.CertId = sequenceReader.ReadObjectIdentifierAsString(); + decoded.CertId = sequenceReader.ReadObjectIdentifier(); explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmpSpan = explicitReader.ReadEncodedValue(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs index da404561afb879..c7b0ae0696ddec 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs12 { @@ -50,15 +49,12 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for IterationCount. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - tmp.WriteInteger(IterationCount); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteInteger(IterationCount); - if (!encoded.SequenceEqual(DefaultIterationCount)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultIterationCount)) + { + tmp.CopyTo(writer); } } @@ -72,11 +68,18 @@ internal static MacData Decode(ReadOnlyMemory encoded, AsnEncodingRules ru internal static MacData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out MacData decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out MacData decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out MacData decoded) @@ -85,6 +88,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out MacData decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out MacData decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -95,7 +110,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.DigestInfoAsn.Decode(ref sequenceReader, rebind, out decoded.Mac); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.MacSalt = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs index 4c7dd5a5bc0f80..e9004e5a4bc4e7 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs @@ -19,7 +19,7 @@ internal bool VerifyMac( HashAlgorithmName hashAlgorithm; int expectedOutputSize; - string? algorithmValue = MacData.Value.Mac.DigestAlgorithm.Algorithm.Value; + string algorithmValue = MacData.Value.Mac.DigestAlgorithm.Algorithm; switch (algorithmValue) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml index a46703b33d3a1c..33dd50bb02037d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml @@ -13,7 +13,7 @@ macData MacData OPTIONAL } --> - + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml.cs index 5364bdfc72d3d9..56dfa3caa31963 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml.cs @@ -4,16 +4,15 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs12 { [StructLayout(LayoutKind.Sequential)] internal partial struct PfxAsn { - internal byte Version; + internal int Version; internal System.Security.Cryptography.Asn1.Pkcs7.ContentInfoAsn AuthSafe; internal System.Security.Cryptography.Asn1.Pkcs12.MacData? MacData; @@ -44,11 +43,18 @@ internal static PfxAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules rul internal static PfxAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PfxAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PfxAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PfxAsn decoded) @@ -57,12 +63,24 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PfxAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PfxAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); - if (!sequenceReader.TryReadUInt8(out decoded.Version)) + if (!sequenceReader.TryReadInt32(out decoded.Version)) { sequenceReader.ThrowIfNotEmpty(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/SafeBagAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/SafeBagAsn.xml.cs index c34015fc821a2a..dd548c6a4aa411 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/SafeBagAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/SafeBagAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs12 { @@ -27,9 +26,23 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(BagId); + try + { + writer.WriteObjectIdentifier(BagId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(BagValue.Span); + try + { + writer.WriteEncodedValue(BagValue.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); if (BagAttributes != null) @@ -54,11 +67,18 @@ internal static SafeBagAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules internal static SafeBagAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SafeBagAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out SafeBagAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SafeBagAsn decoded) @@ -67,6 +87,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SafeBagAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SafeBagAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -76,7 +108,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.BagId = sequenceReader.ReadObjectIdentifierAsString(); + decoded.BagId = sequenceReader.ReadObjectIdentifier(); explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmpSpan = explicitReader.ReadEncodedValue(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/ContentInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/ContentInfoAsn.xml.cs index d7ef164faad6b8..ed94ab299ae727 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/ContentInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/ContentInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs7 { @@ -25,9 +24,23 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(ContentType); + try + { + writer.WriteObjectIdentifier(ContentType); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(Content.Span); + try + { + writer.WriteEncodedValue(Content.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); writer.PopSequence(tag); } @@ -39,11 +52,18 @@ internal static ContentInfoAsn Decode(ReadOnlyMemory encoded, AsnEncodingR internal static ContentInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out ContentInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out ContentInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out ContentInfoAsn decoded) @@ -52,6 +72,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out ContentInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out ContentInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -60,7 +92,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.ContentType = sequenceReader.ReadObjectIdentifierAsString(); + decoded.ContentType = sequenceReader.ReadObjectIdentifier(); explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmpSpan = explicitReader.ReadEncodedValue(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedContentInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedContentInfoAsn.xml.cs index b915beaa2826f5..2b9466abe65aaf 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedContentInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedContentInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs7 { @@ -26,12 +25,19 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(ContentType); + try + { + writer.WriteObjectIdentifier(ContentType); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } ContentEncryptionAlgorithm.Encode(writer); if (EncryptedContent.HasValue) { - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), EncryptedContent.Value.Span); + writer.WriteOctetString(EncryptedContent.Value.Span, new Asn1Tag(TagClass.ContextSpecific, 0)); } writer.PopSequence(tag); @@ -44,11 +50,18 @@ internal static EncryptedContentInfoAsn Decode(ReadOnlyMemory encoded, Asn internal static EncryptedContentInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EncryptedContentInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out EncryptedContentInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EncryptedContentInfoAsn decoded) @@ -57,6 +70,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncryptedContentInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncryptedContentInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -64,13 +89,13 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.ContentType = sequenceReader.ReadObjectIdentifierAsString(); + decoded.ContentType = sequenceReader.ReadObjectIdentifier(); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.ContentEncryptionAlgorithm); if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 0))) { decoded.EncryptedContent = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedDataAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedDataAsn.xml.cs index fffd2c0e6f8405..2342c2bd0c25a5 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedDataAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs7/EncryptedDataAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1.Pkcs7 { @@ -52,11 +51,18 @@ internal static EncryptedDataAsn Decode(ReadOnlyMemory encoded, AsnEncodin internal static EncryptedDataAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EncryptedDataAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out EncryptedDataAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EncryptedDataAsn decoded) @@ -65,6 +71,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncryptedDataAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncryptedDataAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml index 830e897103b634..bfd83bb8aab9ec 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml @@ -19,10 +19,10 @@ PrivateKey ::= OCTET STRING Attributes ::= SET OF Attribute --> - + - \ No newline at end of file + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml.cs index 648489e6c9e2c3..f8cece5ea15e0e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PrivateKeyInfoAsn.xml.cs @@ -5,16 +5,15 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { [StructLayout(LayoutKind.Sequential)] internal partial struct PrivateKeyInfoAsn { - internal byte Version; + internal int Version; internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn PrivateKeyAlgorithm; internal ReadOnlyMemory PrivateKey; internal System.Security.Cryptography.Asn1.AttributeAsn[]? Attributes; @@ -54,11 +53,18 @@ internal static PrivateKeyInfoAsn Decode(ReadOnlyMemory encoded, AsnEncodi internal static PrivateKeyInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PrivateKeyInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PrivateKeyInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PrivateKeyInfoAsn decoded) @@ -67,6 +73,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PrivateKeyInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PrivateKeyInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -76,14 +94,14 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (!sequenceReader.TryReadUInt8(out decoded.Version)) + if (!sequenceReader.TryReadInt32(out decoded.Version)) { sequenceReader.ThrowIfNotEmpty(); } System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.PrivateKeyAlgorithm); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.PrivateKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs index 6c42acd09a1cc3..3aa81006fb357b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -73,68 +72,56 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for HashAlgorithm. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + HashAlgorithm.Encode(tmp); + + if (!tmp.EncodedValueEquals(DefaultHashAlgorithm)) { - HashAlgorithm.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultHashAlgorithm)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); } } // DEFAULT value handler for MaskGenAlgorithm. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + MaskGenAlgorithm.Encode(tmp); + + if (!tmp.EncodedValueEquals(DefaultMaskGenAlgorithm)) { - MaskGenAlgorithm.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultMaskGenAlgorithm)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); } } // DEFAULT value handler for SaltLength. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteInteger(SaltLength); + + if (!tmp.EncodedValueEquals(DefaultSaltLength)) { - tmp.WriteInteger(SaltLength); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultSaltLength)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); } } // DEFAULT value handler for TrailerField. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteInteger(TrailerField); + + if (!tmp.EncodedValueEquals(DefaultTrailerField)) { - tmp.WriteInteger(TrailerField); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual(DefaultTrailerField)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); } } @@ -148,11 +135,18 @@ internal static PssParamsAsn Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static PssParamsAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PssParamsAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PssParamsAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PssParamsAsn decoded) @@ -161,6 +155,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PssParamsAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PssParamsAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml index 7c4f2a485e70d4..0947908578f3d0 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml @@ -27,7 +27,7 @@ Since we don't support otherPrimeInfos (Version=1) just don't map it in. --> - + @@ -36,4 +36,4 @@ - \ No newline at end of file + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml.cs index a5ffdaf86380e6..476b201ee9a71a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPrivateKeyAsn.xml.cs @@ -4,16 +4,15 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { [StructLayout(LayoutKind.Sequential)] internal partial struct RSAPrivateKeyAsn { - internal byte Version; + internal int Version; internal System.Numerics.BigInteger Modulus; internal System.Numerics.BigInteger PublicExponent; internal System.Numerics.BigInteger PrivateExponent; @@ -51,11 +50,18 @@ internal static RSAPrivateKeyAsn Decode(ReadOnlyMemory encoded, AsnEncodin internal static RSAPrivateKeyAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out RSAPrivateKeyAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out RSAPrivateKeyAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out RSAPrivateKeyAsn decoded) @@ -64,12 +70,24 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RSAPrivateKeyAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RSAPrivateKeyAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); - if (!sequenceReader.TryReadUInt8(out decoded.Version)) + if (!sequenceReader.TryReadInt32(out decoded.Version)) { sequenceReader.ThrowIfNotEmpty(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPublicKeyAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPublicKeyAsn.xml.cs index ee46993ffab349..5fb986a0cc0866 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPublicKeyAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/RSAPublicKeyAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -37,11 +36,18 @@ internal static RSAPublicKeyAsn Decode(ReadOnlyMemory encoded, AsnEncoding internal static RSAPublicKeyAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out RSAPublicKeyAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out RSAPublicKeyAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out RSAPublicKeyAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RSAPublicKeyAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RSAPublicKeyAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Rc2CbcParameters.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Rc2CbcParameters.xml.cs index 580692b1f0047d..e31fc76318e615 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Rc2CbcParameters.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Rc2CbcParameters.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -37,11 +36,18 @@ internal static Rc2CbcParameters Decode(ReadOnlyMemory encoded, AsnEncodin internal static Rc2CbcParameters Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out Rc2CbcParameters decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out Rc2CbcParameters decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Rc2CbcParameters decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rc2CbcParameters decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rc2CbcParameters decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -64,7 +82,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read } - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Iv = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml index ee3b4852e5b791..75056194341b6a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml @@ -21,7 +21,7 @@ HashAlgorithm ::= AlgorithmIdentifier {{ HashFunctions }} ECPoint ::= OCTET STRING --> - + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml.cs index dbe070a250e6f7..ae447d0e3c1b17 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml.cs @@ -4,22 +4,21 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { [StructLayout(LayoutKind.Sequential)] internal partial struct SpecifiedECDomain { - internal byte Version; + internal int Version; internal System.Security.Cryptography.Asn1.FieldID FieldID; internal System.Security.Cryptography.Asn1.CurveAsn Curve; internal ReadOnlyMemory Base; internal ReadOnlyMemory Order; internal ReadOnlyMemory? Cofactor; - internal Oid? Hash; + internal string? Hash; internal void Encode(AsnWriter writer) { @@ -44,7 +43,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (Hash != null) { - writer.WriteObjectIdentifier(Hash); + try + { + writer.WriteObjectIdentifier(Hash); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSequence(tag); @@ -57,11 +63,18 @@ internal static SpecifiedECDomain Decode(ReadOnlyMemory encoded, AsnEncodi internal static SpecifiedECDomain Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SpecifiedECDomain decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out SpecifiedECDomain decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SpecifiedECDomain decoded) @@ -70,6 +83,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SpecifiedECDomain decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SpecifiedECDomain decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -78,7 +103,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (!sequenceReader.TryReadUInt8(out decoded.Version)) + if (!sequenceReader.TryReadInt32(out decoded.Version)) { sequenceReader.ThrowIfNotEmpty(); } @@ -86,7 +111,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.FieldID.Decode(ref sequenceReader, rebind, out decoded.FieldID); System.Security.Cryptography.Asn1.CurveAsn.Decode(ref sequenceReader, rebind, out decoded.Curve); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Base = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs index 24125bcfb02990..bb2fbdd5b6d881 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -26,7 +25,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) writer.PushSequence(tag); Algorithm.Encode(writer); - writer.WriteBitString(SubjectPublicKey.Span); + writer.WriteBitString(SubjectPublicKey.Span, 0); writer.PopSequence(tag); } @@ -37,11 +36,18 @@ internal static SubjectPublicKeyInfoAsn Decode(ReadOnlyMemory encoded, Asn internal static SubjectPublicKeyInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SubjectPublicKeyInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out SubjectPublicKeyInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SubjectPublicKeyInfoAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SubjectPublicKeyInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SubjectPublicKeyInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -59,7 +77,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.Algorithm); - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan)) { decoded.SubjectPublicKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.manual.cs index 3c226d8f4fb2a4..6b9ab9f317025a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.manual.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.manual.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; namespace System.Security.Cryptography.Asn1 @@ -16,7 +15,7 @@ public X509ExtensionAsn(X509Extension extension) throw new ArgumentNullException(nameof(extension)); } - ExtnId = extension.Oid!; + ExtnId = extension.Oid!.Value!; Critical = extension.Critical; ExtnValue = extension.RawData; } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml.cs index 45ead32bef0c21..6652878c8871b9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Asn1 { @@ -15,7 +14,7 @@ internal partial struct X509ExtensionAsn { private static ReadOnlySpan DefaultCritical => new byte[] { 0x01, 0x01, 0x00 }; - internal Oid ExtnId; + internal string ExtnId; internal bool Critical; internal ReadOnlyMemory ExtnValue; @@ -40,19 +39,23 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(ExtnId); + try + { + writer.WriteObjectIdentifier(ExtnId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } // DEFAULT value handler for Critical. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - tmp.WriteBoolean(Critical); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteBoolean(Critical); - if (!encoded.SequenceEqual(DefaultCritical)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultCritical)) + { + tmp.CopyTo(writer); } } @@ -67,11 +70,18 @@ internal static X509ExtensionAsn Decode(ReadOnlyMemory encoded, AsnEncodin internal static X509ExtensionAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out X509ExtensionAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out X509ExtensionAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out X509ExtensionAsn decoded) @@ -80,6 +90,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out X509ExtensionAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out X509ExtensionAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -101,7 +123,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read } - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.ExtnValue = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xsd b/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xsd index d8560ad3381c6b..90a5235e9fcb5f 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xsd +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xsd @@ -30,7 +30,7 @@ - + @@ -97,7 +97,6 @@ - @@ -106,32 +105,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -156,7 +129,7 @@ - + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xslt b/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xslt index 3433cd17a4e0af..12f7ebfc328fd9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xslt +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/asn.xslt @@ -54,9 +54,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace { @@ -93,11 +92,18 @@ namespace internal static Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out decoded) @@ -106,6 +112,18 @@ namespace } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory<byte> rebind, out decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory<byte> rebind, out decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -130,9 +148,8 @@ namespace #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace { @@ -169,14 +186,33 @@ namespace internal static Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory<byte> rebind, out decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -208,7 +244,7 @@ namespace - Error: neiher optional or defaultDerInit may be specified for fields in a Choice type () + Error: neither optional or defaultDerInit may be specified for fields in a Choice type () @@ -230,22 +266,20 @@ namespace + // DEFAULT value handler for . { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); - + - ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan(); - if (!encoded.SequenceEqual()) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals()) + { + tmp.CopyTo(writer); } } @@ -304,19 +338,16 @@ namespace // DEFAULT value handler for . { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - - - - ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual()) - { - writer.PushSequence(); - writer.WriteEncodedValue(encoded); - writer.PopSequence(); - } + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + + + + + if (!tmp.EncodedValueEquals()) + { + writer.PushSequence(); + tmp.CopyTo(writer); + writer.PopSequence(); } } @@ -414,11 +445,11 @@ namespace tmp; - .Decode(ref , rebind, out tmp); + .Decode(ref , rebind, out tmp); = tmp; - .Decode(ref , rebind, out ); + .Decode(ref , rebind, out ); @@ -445,7 +476,14 @@ namespace } - .WriteEncodedValue(.Value.Span); + try + { + .WriteEncodedValue(.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } @@ -482,7 +520,7 @@ namespace - .WriteBoolean(.Value); + .WriteBoolean(.Value); @@ -501,15 +539,11 @@ namespace internal ReadOnlyMemory<byte>? ; - - internal byte? ; - internal int? ; System.Numerics.BigInteger ReadOnlyMemory<byte> - byte int @@ -518,7 +552,7 @@ namespace - .WriteInteger(.Value); + .WriteInteger(.Value); @@ -527,7 +561,7 @@ namespace - .WriteInteger(.Value.Span); + .WriteInteger(.Value.Span); @@ -546,32 +580,6 @@ namespace tmpSpan = .ReadIntegerBytes(); - - - - - - - - if (.TryReadUInt8(out byte tmp)) - { - = tmp; - } - else - { - .ThrowIfNotEmpty(); - } - - - - if (!.TryReadUInt8(out )) - { - .ThrowIfNotEmpty(); - } - - - - @@ -579,7 +587,7 @@ namespace - if (.TryReadInt32(out int tmp)) + if (.TryReadInt32(out int tmp)) { = tmp; } @@ -590,7 +598,7 @@ namespace - if (!.TryReadInt32(out )) + if (!.TryReadInt32(out )) { .ThrowIfNotEmpty(); } @@ -611,7 +619,7 @@ namespace - .WriteBitString(.Value.Span); + .WriteBitString(.Value.Span, 0); @@ -620,12 +628,12 @@ namespace - if (.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (.TryReadPrimitiveBitString(out _, out tmpSpan)) { } else { - = .ReadBitString(out _); + = .ReadBitString(out _); } @@ -643,7 +651,7 @@ namespace - .WriteNamedBitList(.Value); + .WriteNamedBitList(.Value); @@ -667,7 +675,7 @@ namespace - .WriteOctetString(.Value.Span); + .WriteOctetString(.Value.Span); @@ -676,7 +684,7 @@ namespace - if (.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (.TryReadPrimitiveOctetString(out tmpSpan)) { } else @@ -688,13 +696,10 @@ namespace Asn1Tag.PrimitiveOctetString - - internal Oid? ; - + internal string? ; - Oid - string + string @@ -704,11 +709,18 @@ namespace - .WriteObjectIdentifier(); + try + { + .WriteObjectIdentifier(); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } - + @@ -720,18 +732,6 @@ namespace - - - - - - - - - = .ReadObjectIdentifierAsString(); - - - Asn1Tag.ObjectIdentifier @@ -745,7 +745,7 @@ namespace - .WriteEnumeratedValue(.Value); + .WriteEnumeratedValue(.Value); @@ -769,7 +769,7 @@ namespace - .WriteCharacterString(UniversalTagNumber., ); + .WriteCharacterString(UniversalTagNumber., ); @@ -777,7 +777,7 @@ namespace - = .ReadCharacterString(UniversalTagNumber.); + = .ReadCharacterString(UniversalTagNumber.); new Asn1Tag(UniversalTagNumber.) @@ -846,7 +846,7 @@ namespace - .WriteUtcTime(.Value); + .WriteUtcTime(.Value); @@ -855,7 +855,7 @@ namespace - = .ReadUtcTime(); + = .ReadUtcTime(); = .ReadUtcTime(); @@ -870,9 +870,9 @@ namespace - .WriteGeneralizedTime(.Value, omitFractionalSeconds: ); + .WriteGeneralizedTime(.Value, omitFractionalSeconds: ); - .WriteGeneralizedTime(.Value); + .WriteGeneralizedTime(.Value, false); @@ -880,12 +880,16 @@ namespace - - - = .ReadGeneralizedTime(disallowFractions: ); - - = .ReadGeneralizedTime(); - + + + = .ReadGeneralizedTime(); + + + if (!.Value.Ticks % TimeSpan.TicksPerSecond != 0) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + Asn1Tag.GeneralizedTime @@ -893,9 +897,6 @@ namespace - - , - , diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs deleted file mode 100644 index e777cc12191043..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs +++ /dev/null @@ -1,919 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlySpan value) - => TryReadPrimitiveBitStringValue(Asn1Tag.PrimitiveBitString, out unusedBitCount, out value); - - /// - /// Reads the next value as a BIT STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, - out int unusedBitCount, - out ReadOnlySpan value) - { - bool isPrimitive = TryReadPrimitiveBitStringValue( - expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out unusedBitCount, - out value, - out byte normalizedLastByte); - - if (isPrimitive) - { - // A BER reader which encountered a situation where an "unused" bit was not - // set to 0. - if (value.Length != 0 && normalizedLastByte != value[value.Length - 1]) - { - unusedBitCount = 0; - value = default; - return false; - } - - // Skip the tag+length (header) and the unused bit count byte (1) and the contents. - _data = _data.Slice(headerLength + value.Length + 1); - } - - return isPrimitive; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyBitStringBytes( - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - Asn1Tag.PrimitiveBitString, - destination, - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - if (TryReadPrimitiveBitStringValue( - expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out unusedBitCount, - out ReadOnlySpan value, - out byte normalizedLastByte)) - { - if (value.Length > destination.Length) - { - bytesWritten = 0; - unusedBitCount = 0; - return false; - } - - CopyBitStringValue(value, normalizedLastByte, destination); - - bytesWritten = value.Length; - // contents doesn't include the unusedBitCount value, so add one byte for that. - _data = _data.Slice(headerLength + value.Length + 1); - return true; - } - - Debug.Assert(actualTag.IsConstructed); - - bool read = TryCopyConstructedBitStringValue( - Slice(_data, headerLength, contentsLength), - destination, - contentsLength == null, - out unusedBitCount, - out int bytesRead, - out bytesWritten); - - if (read) - { - _data = _data.Slice(headerLength + bytesRead); - } - - return read; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public byte[] ReadBitString(out int unusedBitCount) - { - return ReadBitString(Asn1Tag.PrimitiveBitString, out unusedBitCount); - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public byte[] ReadBitString(Asn1Tag expectedTag, out int unusedBitCount) - { - ReadOnlySpan span; - - if (TryReadPrimitiveBitStringValue(expectedTag, out unusedBitCount, out span)) - { - return span.ToArray(); - } - - span = PeekEncodedValue(); - - // Guaranteed long enough - byte[] rented = CryptoPool.Rent(span.Length); - int dataLength = 0; - - try - { - if (!TryCopyBitStringBytes(expectedTag, rented, out unusedBitCount, out dataLength)) - { - Debug.Fail("TryCopyBitStringBytes failed with a pre-allocated buffer"); - throw new CryptographicException(); - } - - byte[] alloc = new byte[dataLength]; - rented.AsSpan(0, dataLength).CopyTo(alloc); - return alloc; - } - finally - { - CryptoPool.Return(rented, dataLength); - } - } - - private void ParsePrimitiveBitStringContents( - ReadOnlySpan source, - out int unusedBitCount, - out ReadOnlySpan value, - out byte normalizedLastByte) - { - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER && source.Length > MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - // T-REC-X.690-201508 sec 8.6.2.3 - if (source.Length == 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - unusedBitCount = source[0]; - - // T-REC-X.690-201508 sec 8.6.2.2 - if (unusedBitCount > 7) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (source.Length == 1) - { - // T-REC-X.690-201508 sec 8.6.2.4 - if (unusedBitCount > 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(unusedBitCount == 0); - value = ReadOnlySpan.Empty; - normalizedLastByte = 0; - return; - } - - // Build a mask for the bits that are used so the normalized value can be computed - // - // If 3 bits are "unused" then build a mask for them to check for 0. - // -1 << 3 => 0b1111_1111 << 3 => 0b1111_1000 - int mask = -1 << unusedBitCount; - byte lastByte = source[source.Length - 1]; - byte maskedByte = (byte)(lastByte & mask); - - if (maskedByte != lastByte) - { - // T-REC-X.690-201508 sec 11.2.1 - if (RuleSet == AsnEncodingRules.DER || RuleSet == AsnEncodingRules.CER) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - normalizedLastByte = maskedByte; - value = source.Slice(1); - } - - private delegate void BitStringCopyAction( - ReadOnlySpan value, - byte normalizedLastByte, - Span destination); - - private static void CopyBitStringValue( - ReadOnlySpan value, - byte normalizedLastByte, - Span destination) - { - if (value.Length == 0) - { - return; - } - - value.CopyTo(destination); - // Replace the last byte with the normalized answer. - destination[value.Length - 1] = normalizedLastByte; - } - - private int CountConstructedBitString(ReadOnlySpan source, bool isIndefinite) - { - Span destination = Span.Empty; - - return ProcessConstructedBitString( - source, - destination, - null, - isIndefinite, - out _, - out _); - } - - private void CopyConstructedBitString( - ReadOnlySpan source, - Span destination, - bool isIndefinite, - out int unusedBitCount, - out int bytesRead, - out int bytesWritten) - { - Span tmpDest = destination; - - bytesWritten = ProcessConstructedBitString( - source, - tmpDest, - (value, lastByte, dest) => CopyBitStringValue(value, lastByte, dest), - isIndefinite, - out unusedBitCount, - out bytesRead); - } - - private int ProcessConstructedBitString( - ReadOnlySpan source, - Span destination, - BitStringCopyAction? copyAction, - bool isIndefinite, - out int lastUnusedBitCount, - out int bytesRead) - { - lastUnusedBitCount = 0; - bytesRead = 0; - int lastSegmentLength = MaxCERSegmentSize; - - ReadOnlySpan originalSpan = _data; - AsnValueReader tmpReader = OpenUnchecked(source, RuleSet); - Stack<(int Offset, int Length, bool Indefinite, int BytesRead)>? readerStack = null; - int totalLength = 0; - Asn1Tag tag = Asn1Tag.ConstructedBitString; - Span curDest = destination; - - while (true) - { - while (tmpReader.HasData) - { - tag = tmpReader.ReadTagAndLength(out int? length, out int headerLength); - - if (tag == Asn1Tag.PrimitiveBitString) - { - if (lastUnusedBitCount != 0) - { - // T-REC-X.690-201508 sec 8.6.4, only the last segment may have - // a number of bits not a multiple of 8. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(length != null); - ReadOnlySpan encodedValue = Slice(tmpReader._data, headerLength, length.Value); - - ParsePrimitiveBitStringContents( - encodedValue, - out lastUnusedBitCount, - out ReadOnlySpan contents, - out byte normalizedLastByte); - - int localLen = headerLength + encodedValue.Length; - tmpReader._data = tmpReader._data.Slice(localLen); - - bytesRead += localLen; - totalLength += contents.Length; - lastSegmentLength = encodedValue.Length; - - if (copyAction != null) - { - copyAction(contents, normalizedLastByte, curDest); - curDest = curDest.Slice(contents.Length); - } - } - else if (tag == Asn1Tag.EndOfContents && isIndefinite) - { - ValidateEndOfContents(tag, length, headerLength); - - bytesRead += headerLength; - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); - tmpReader._data = topSpan.Slice(bytesRead); - - bytesRead += pushedBytesRead; - isIndefinite = wasIndefinite; - } - else - { - // We have matched the EndOfContents that brought us here. - break; - } - } - else if (tag == Asn1Tag.ConstructedBitString) - { - if (RuleSet == AsnEncodingRules.CER) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack == null) - { - readerStack = new Stack<(int, int, bool, int)>(); - } - - if (!originalSpan.Overlaps(tmpReader._data, out int curOffset)) - { - Debug.Fail("Non-overlapping data encountered..."); - throw new CryptographicException(); - } - - readerStack.Push((curOffset, tmpReader._data.Length, isIndefinite, bytesRead)); - - tmpReader._data = Slice(tmpReader._data, headerLength, length); - bytesRead = headerLength; - isIndefinite = (length == null); - } - else - { - // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - if (isIndefinite && tag != Asn1Tag.EndOfContents) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - - ReadOnlySpan tmpSpan = originalSpan.Slice(topOffset, topLength); - tmpReader._data = tmpSpan.Slice(bytesRead); - - isIndefinite = wasIndefinite; - bytesRead += pushedBytesRead; - } - else - { - return totalLength; - } - } - } - - private bool TryCopyConstructedBitStringValue( - ReadOnlySpan source, - Span dest, - bool isIndefinite, - out int unusedBitCount, - out int bytesRead, - out int bytesWritten) - { - // Call CountConstructedBitString to get the required byte and to verify that the - // data is well-formed before copying into dest. - int contentLength = CountConstructedBitString(source, isIndefinite); - - // Since the unused bits byte from the segments don't count, only one segment - // returns 999 (or less), the second segment bumps the count to 1000, and is legal. - // - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER && contentLength < MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (dest.Length < contentLength) - { - unusedBitCount = 0; - bytesRead = 0; - bytesWritten = 0; - return false; - } - - CopyConstructedBitString( - source, - dest, - isIndefinite, - out unusedBitCount, - out bytesRead, - out bytesWritten); - - Debug.Assert(bytesWritten == contentLength); - return true; - } - - private bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out int unusedBitCount, - out ReadOnlySpan value, - out byte normalizedLastByte) - { - actualTag = ReadTagAndLength(out contentsLength, out headerLength); - CheckExpectedTag(actualTag, expectedTag, UniversalTagNumber.BitString); - - if (actualTag.IsConstructed) - { - if (RuleSet == AsnEncodingRules.DER) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - unusedBitCount = 0; - value = default; - normalizedLastByte = 0; - return false; - } - - Debug.Assert(contentsLength.HasValue); - ReadOnlySpan encodedValue = Slice(_data, headerLength, contentsLength.Value); - - ParsePrimitiveBitStringContents( - encodedValue, - out unusedBitCount, - out value, - out normalizedLastByte); - - return true; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlyMemory value) - => TryReadPrimitiveBitStringValue(Asn1Tag.PrimitiveBitString, out unusedBitCount, out value); - - /// - /// Reads the next value as a BIT STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, - out int unusedBitCount, - out ReadOnlyMemory value) - { - AsnValueReader reader = OpenValueReader(); - - if (reader.TryReadPrimitiveBitStringValue(expectedTag, out unusedBitCount, out ReadOnlySpan span)) - { - value = AsnValueReader.Slice(_data, span); - reader.MatchSlice(ref _data); - return true; - } - - value = default; - return false; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyBitStringBytes( - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - Asn1Tag.PrimitiveBitString, - destination, - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - AsnValueReader reader = OpenValueReader(); - - if (reader.TryCopyBitStringBytes(expectedTag, destination, out unusedBitCount, out bytesWritten)) - { - reader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyBitStringBytes( - ArraySegment destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - Asn1Tag.PrimitiveBitString, - destination.AsSpan(), - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - ArraySegment destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - expectedTag, - destination.AsSpan(), - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public byte[] ReadBitString(out int unusedBitCount) - { - return ReadBitString(Asn1Tag.PrimitiveBitString, out unusedBitCount); - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public byte[] ReadBitString(Asn1Tag expectedTag, out int unusedBitCount) - { - AsnValueReader valueReader = OpenValueReader(); - byte[] ret = valueReader.ReadBitString(expectedTag, out unusedBitCount); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs deleted file mode 100644 index 8743adfbdec90a..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a Boolean with tag UNIVERSAL 1. - /// - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool ReadBoolean() => ReadBoolean(Asn1Tag.Boolean); - - /// - /// Reads the next value as a Boolean with a specified tag. - /// - /// The tag to check for before reading. - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool ReadBoolean(Asn1Tag expectedTag) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Boolean); - - // T-REC-X.690-201508 sec 8.2.1 - if (tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(length.HasValue); - bool value = ReadBooleanValue( - Slice(_data, headerLength, length.Value), - RuleSet); - - _data = _data.Slice(headerLength + length.Value); - return value; - } - - private static bool ReadBooleanValue( - ReadOnlySpan source, - AsnEncodingRules ruleSet) - { - // T-REC-X.690-201508 sec 8.2.1 - if (source.Length != 1) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - byte val = source[0]; - - // T-REC-X.690-201508 sec 8.2.2 - if (val == 0) - { - return false; - } - - // T-REC-X.690-201508 sec 11.1 - if (val != 0xFF && (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return true; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a Boolean with tag UNIVERSAL 1. - /// - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool ReadBoolean() => ReadBoolean(Asn1Tag.Boolean); - - /// - /// Reads the next value as a Boolean with a specified tag. - /// - /// The tag to check for before reading. - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool ReadBoolean(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - bool ret = valueReader.ReadBoolean(expectedTag); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs deleted file mode 100644 index 296e596d1da446..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs +++ /dev/null @@ -1,401 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, - /// returning the contents as a over the original data. - /// - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public ReadOnlySpan ReadEnumeratedBytes() => - ReadEnumeratedBytes(Asn1Tag.Enumerated); - - /// - /// Reads the next value as a Enumerated with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public ReadOnlySpan ReadEnumeratedBytes(Asn1Tag expectedTag) - { - // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Enumerated, out int headerLength); - - _data = _data.Slice(headerLength + contents.Length); - return contents; - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public TEnum ReadEnumeratedValue() where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(tEnum)); - } - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public TEnum ReadEnumeratedValue(Asn1Tag expectedTag) where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum)); - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public Enum ReadEnumeratedValue(Type tEnum) => - ReadEnumeratedValue(Asn1Tag.Enumerated, tEnum); - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) - { - const UniversalTagNumber tagNumber = UniversalTagNumber.Enumerated; - - // This will throw an ArgumentException if TEnum isn't an enum type, - // so we don't need to validate it. - Type backingType = tEnum.GetEnumUnderlyingType(); - - if (tEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, - nameof(tEnum)); - } - - // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. - int sizeLimit = Marshal.SizeOf(backingType); - - if (backingType == typeof(int) || - backingType == typeof(long) || - backingType == typeof(short) || - backingType == typeof(sbyte)) - { - if (!TryReadSignedInteger(sizeLimit, expectedTag, tagNumber, out long value)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return (Enum)Enum.ToObject(tEnum, value); - } - - if (backingType == typeof(uint) || - backingType == typeof(ulong) || - backingType == typeof(ushort) || - backingType == typeof(byte)) - { - if (!TryReadUnsignedInteger(sizeLimit, expectedTag, tagNumber, out ulong value)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return (Enum)Enum.ToObject(tEnum, value); - } - - Debug.Fail($"No handler for type {backingType.Name}"); - throw new CryptographicException(); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, - /// returning the contents as a over the original data. - /// - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public ReadOnlyMemory ReadEnumeratedBytes() => - ReadEnumeratedBytes(Asn1Tag.Enumerated); - - /// - /// Reads the next value as a Enumerated with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public ReadOnlyMemory ReadEnumeratedBytes(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan bytes = valueReader.ReadEnumeratedBytes(expectedTag); - ReadOnlyMemory memory = AsnValueReader.Slice(_data, bytes); - - valueReader.MatchSlice(ref _data); - return memory; - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public TEnum ReadEnumeratedValue() where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(tEnum)); - } - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public TEnum ReadEnumeratedValue(Asn1Tag expectedTag) where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum)); - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public Enum ReadEnumeratedValue(Type tEnum) => - ReadEnumeratedValue(Asn1Tag.Enumerated, tEnum); - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) - { - AsnValueReader valueReader = OpenValueReader(); - Enum ret = valueReader.ReadEnumeratedValue(expectedTag, tEnum); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs deleted file mode 100644 index 3939428bdaae35..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs +++ /dev/null @@ -1,1217 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Numerics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a over the original data. - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public ReadOnlySpan ReadIntegerBytes() => - ReadIntegerBytes(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public ReadOnlySpan ReadIntegerBytes(Asn1Tag expectedTag) - { - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength); - - _data = _data.Slice(headerLength + contents.Length); - return contents; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a . - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public BigInteger ReadInteger() => ReadInteger(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public BigInteger ReadInteger(Asn1Tag expectedTag) - { - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength); - - // TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing - byte[] tmp = CryptoPool.Rent(contents.Length); - BigInteger value; - - try - { - byte fill = (contents[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF; - // Fill the unused portions of tmp with positive or negative padding. - new Span(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill); - contents.CopyTo(tmp); - // Convert to Little-Endian. - AsnWriter.Reverse(new Span(tmp, 0, contents.Length)); - value = new BigInteger(tmp); - } - finally - { - // Let CryptoPool.Return clear the whole tmp so that not even the sign bit - // is returned to the array pool. - CryptoPool.Return(tmp); - } - - _data = _data.Slice(headerLength + contents.Length); - return value; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt32(out int value) => - TryReadInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt32(Asn1Tag expectedTag, out int value) - { - if (TryReadSignedInteger(sizeof(int), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (int)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt32(out uint value) => - TryReadUInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt32(Asn1Tag expectedTag, out uint value) - { - if (TryReadUnsignedInteger(sizeof(uint), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (uint)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt64(out long value) => - TryReadInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt64(Asn1Tag expectedTag, out long value) - { - return TryReadSignedInteger(sizeof(long), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt64(out ulong value) => - TryReadUInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt64(Asn1Tag expectedTag, out ulong value) - { - return TryReadUnsignedInteger(sizeof(ulong), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt16(out short value) => - TryReadInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt16(Asn1Tag expectedTag, out short value) - { - if (TryReadSignedInteger(sizeof(short), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (short)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt16(out ushort value) => - TryReadUInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt16(Asn1Tag expectedTag, out ushort value) - { - if (TryReadUnsignedInteger(sizeof(ushort), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (ushort)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt8(out sbyte value) => - TryReadInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the Integer value is not valid - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt8(Asn1Tag expectedTag, out sbyte value) - { - if (TryReadSignedInteger(sizeof(sbyte), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (sbyte)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt8(out byte value) => - TryReadUInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt8(Asn1Tag expectedTag, out byte value) - { - if (TryReadUnsignedInteger(sizeof(byte), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (byte)ulongValue; - return true; - } - - value = 0; - return false; - } - - private ReadOnlySpan GetIntegerContents( - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out int headerLength) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out headerLength); - CheckExpectedTag(tag, expectedTag, tagNumber); - - // T-REC-X.690-201508 sec 8.3.1 - if (tag.IsConstructed || length < 1) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - // Slice first so that an out of bounds value triggers a CryptographicException. - ReadOnlySpan contents = Slice(_data, headerLength, length!.Value); - - // T-REC-X.690-201508 sec 8.3.2 - if (contents.Length > 1) - { - ushort bigEndianValue = (ushort)(contents[0] << 8 | contents[1]); - const ushort RedundancyMask = 0b1111_1111_1000_0000; - ushort masked = (ushort)(bigEndianValue & RedundancyMask); - - // If the first 9 bits are all 0 or are all 1, the value is invalid. - if (masked == 0 || masked == RedundancyMask) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - return contents; - } - - internal bool TryReadSignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out long value) - { - Debug.Assert(sizeLimit <= sizeof(long)); - - ReadOnlySpan contents = GetIntegerContents(expectedTag, tagNumber, out int headerLength); - - if (contents.Length > sizeLimit) - { - value = 0; - return false; - } - - bool isNegative = (contents[0] & 0x80) != 0; - long accum = isNegative ? -1 : 0; - - for (int i = 0; i < contents.Length; i++) - { - accum <<= 8; - accum |= contents[i]; - } - - _data = _data.Slice(headerLength + contents.Length); - value = accum; - return true; - } - - internal bool TryReadUnsignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out ulong value) - { - Debug.Assert(sizeLimit <= sizeof(ulong)); - - ReadOnlySpan contents = GetIntegerContents(expectedTag, tagNumber, out int headerLength); - int contentLength = contents.Length; - - bool isNegative = (contents[0] & 0x80) != 0; - - if (isNegative) - { - value = 0; - return false; - } - - // Ignore any padding zeros. - if (contents.Length > 1 && contents[0] == 0) - { - contents = contents.Slice(1); - } - - if (contents.Length > sizeLimit) - { - value = 0; - return false; - } - - ulong accum = 0; - - for (int i = 0; i < contents.Length; i++) - { - accum <<= 8; - accum |= contents[i]; - } - - _data = _data.Slice(headerLength + contentLength); - value = accum; - return true; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a over the original data. - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public ReadOnlyMemory ReadIntegerBytes() => - ReadIntegerBytes(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public ReadOnlyMemory ReadIntegerBytes(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan bytes = valueReader.ReadIntegerBytes(expectedTag); - ReadOnlyMemory memory = AsnValueReader.Slice(_data, bytes); - - valueReader.MatchSlice(ref _data); - return memory; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a . - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public BigInteger ReadInteger() => ReadInteger(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public BigInteger ReadInteger(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - BigInteger ret = valueReader.ReadInteger(expectedTag); - valueReader.MatchSlice(ref _data); - return ret; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt32(out int value) => - TryReadInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt32(Asn1Tag expectedTag, out int value) - { - if (TryReadSignedInteger(sizeof(int), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (int)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt32(out uint value) => - TryReadUInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt32(Asn1Tag expectedTag, out uint value) - { - if (TryReadUnsignedInteger(sizeof(uint), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (uint)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt64(out long value) => - TryReadInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt64(Asn1Tag expectedTag, out long value) - { - return TryReadSignedInteger(sizeof(long), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt64(out ulong value) => - TryReadUInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt64(Asn1Tag expectedTag, out ulong value) - { - return TryReadUnsignedInteger(sizeof(ulong), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt16(out short value) => - TryReadInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt16(Asn1Tag expectedTag, out short value) - { - if (TryReadSignedInteger(sizeof(short), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (short)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt16(out ushort value) => - TryReadUInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt16(Asn1Tag expectedTag, out ushort value) - { - if (TryReadUnsignedInteger(sizeof(ushort), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (ushort)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt8(out sbyte value) => - TryReadInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the Integer value is not valid - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt8(Asn1Tag expectedTag, out sbyte value) - { - if (TryReadSignedInteger(sizeof(sbyte), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (sbyte)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt8(out byte value) => - TryReadUInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt8(Asn1Tag expectedTag, out byte value) - { - if (TryReadUnsignedInteger(sizeof(byte), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (byte)ulongValue; - return true; - } - - value = 0; - return false; - } - - private bool TryReadSignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out long value) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryReadSignedInteger(sizeLimit, expectedTag, tagNumber, out value)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - private bool TryReadUnsignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out ulong value) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryReadUnsignedInteger(sizeLimit, expectedTag, tagNumber, out value)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs deleted file mode 100644 index d972df2324837e..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs +++ /dev/null @@ -1,427 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public TFlagsEnum ReadNamedBitListValue() where TFlagsEnum : struct => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// The bit alignment performed by this method is to interpret the most significant bit - /// in the first byte of the value as the least significant bit in , - /// with bits increasing in value until the least significant bit of the first byte, proceeding - /// with the most significant bit of the second byte, and so on. Under this scheme, the following - /// ASN.1 type declaration and C# enumeration can be used together: - /// - /// - /// KeyUsage ::= BIT STRING { - /// digitalSignature (0), - /// nonRepudiation (1), - /// keyEncipherment (2), - /// dataEncipherment (3), - /// keyAgreement (4), - /// keyCertSign (5), - /// cRLSign (6), - /// encipherOnly (7), - /// decipherOnly (8) } - /// - /// - /// - /// [Flags] - /// enum KeyUsage - /// { - /// None = 0, - /// DigitalSignature = 1 << (0), - /// NonRepudiation = 1 << (1), - /// KeyEncipherment = 1 << (2), - /// DataEncipherment = 1 << (3), - /// KeyAgreement = 1 << (4), - /// KeyCertSign = 1 << (5), - /// CrlSign = 1 << (6), - /// EncipherOnly = 1 << (7), - /// DecipherOnly = 1 << (8), - /// } - /// - /// - /// Note that while the example here uses the KeyUsage NamedBitList from - /// RFC 3280 (4.2.1.3), - /// the example enum uses values thar are different from - /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. - /// - public TFlagsEnum ReadNamedBitListValue(Asn1Tag expectedTag) where TFlagsEnum : struct - { - Type tFlagsEnum = typeof(TFlagsEnum); - - return (TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum)); - } - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public Enum ReadNamedBitListValue(Type tFlagsEnum) => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString, tFlagsEnum); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR--- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) - { - // This will throw an ArgumentException if TEnum isn't an enum type, - // so we don't need to validate it. - Type backingType = tFlagsEnum.GetEnumUnderlyingType(); - - if (!tFlagsEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, - nameof(tFlagsEnum)); - } - - int sizeLimit = Marshal.SizeOf(backingType); - ReadOnlySpan saveData = _data; - Span stackSpan; - - unsafe - { - byte* stackPtr = stackalloc byte[sizeLimit]; - stackSpan = new Span(stackPtr, sizeLimit); - } - - // If TryCopyBitStringBytes succeeds but anything else fails _data will have moved, - // so if anything throws here just move _data back to what it was. - try - { - if (!TryCopyBitStringBytes(expectedTag, stackSpan, out int unusedBitCount, out int bytesWritten)) - { - throw new CryptographicException( - SR.Format(SR.Cryptography_Asn_NamedBitListValueTooBig, tFlagsEnum.Name)); - } - - if (bytesWritten == 0) - { - // The mode isn't relevant, zero is always zero. - return (Enum)Enum.ToObject(tFlagsEnum, 0); - } - - ReadOnlySpan valueSpan = stackSpan.Slice(0, bytesWritten); - - // Now that the 0-bounds check is out of the way: - // - // T-REC-X.690-201508 sec 11.2.2 - if (RuleSet == AsnEncodingRules.DER || - RuleSet == AsnEncodingRules.CER) - { - byte lastByte = valueSpan[bytesWritten - 1]; - - // No unused bits tests 0x01, 1 is 0x02, 2 is 0x04, etc. - // We already know that TryCopyBitStringBytes checked that the - // declared unused bits were 0, this checks that the last "used" bit - // isn't also zero. - byte testBit = (byte)(1 << unusedBitCount); - - if ((lastByte & testBit) == 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - // Consider a NamedBitList defined as - // - // SomeList ::= BIT STRING { - // a(0), b(1), c(2), d(3), e(4), f(5), g(6), h(7), i(8), j(9), k(10) - // } - // - // The BIT STRING encoding of (a | j) is - // unusedBitCount = 6, - // contents: 0x80 0x40 (0b10000000_01000000) - // - // A the C# exposure of this structure we adhere to is - // - // [Flags] - // enum SomeList - // { - // A = 1, - // B = 1 << 1, - // C = 1 << 2, - // ... - // } - // - // Which happens to be exactly backwards from how the bits are encoded, but the complexity - // only needs to live here. - return (Enum)Enum.ToObject(tFlagsEnum, InterpretNamedBitListReversed(valueSpan)); - } - catch - { - _data = saveData; - throw; - } - } - - private static long InterpretNamedBitListReversed(ReadOnlySpan valueSpan) - { - Debug.Assert(valueSpan.Length <= sizeof(long)); - - long accum = 0; - long currentBitValue = 1; - - for (int byteIdx = 0; byteIdx < valueSpan.Length; byteIdx++) - { - byte byteVal = valueSpan[byteIdx]; - - for (int bitIndex = 7; bitIndex >= 0; bitIndex--) - { - int test = 1 << bitIndex; - - if ((byteVal & test) != 0) - { - accum |= currentBitValue; - } - - currentBitValue <<= 1; - } - } - - return accum; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public TFlagsEnum ReadNamedBitListValue() where TFlagsEnum : struct => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// The bit alignment performed by this method is to interpret the most significant bit - /// in the first byte of the value as the least significant bit in , - /// with bits increasing in value until the least significant bit of the first byte, proceeding - /// with the most significant bit of the second byte, and so on. Under this scheme, the following - /// ASN.1 type declaration and C# enumeration can be used together: - /// - /// - /// KeyUsage ::= BIT STRING { - /// digitalSignature (0), - /// nonRepudiation (1), - /// keyEncipherment (2), - /// dataEncipherment (3), - /// keyAgreement (4), - /// keyCertSign (5), - /// cRLSign (6), - /// encipherOnly (7), - /// decipherOnly (8) } - /// - /// - /// - /// [Flags] - /// enum KeyUsage - /// { - /// None = 0, - /// DigitalSignature = 1 << (0), - /// NonRepudiation = 1 << (1), - /// KeyEncipherment = 1 << (2), - /// DataEncipherment = 1 << (3), - /// KeyAgreement = 1 << (4), - /// KeyCertSign = 1 << (5), - /// CrlSign = 1 << (6), - /// EncipherOnly = 1 << (7), - /// DecipherOnly = 1 << (8), - /// } - /// - /// - /// Note that while the example here uses the KeyUsage NamedBitList from - /// RFC 3280 (4.2.1.3), - /// the example enum uses values thar are different from - /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. - /// - public TFlagsEnum ReadNamedBitListValue(Asn1Tag expectedTag) where TFlagsEnum : struct - { - Type tFlagsEnum = typeof(TFlagsEnum); - - return (TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum)); - } - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public Enum ReadNamedBitListValue(Type tFlagsEnum) => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString, tFlagsEnum); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR--- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) - { - AsnValueReader valueReader = OpenValueReader(); - Enum ret = valueReader.ReadNamedBitListValue(expectedTag, tFlagsEnum); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs deleted file mode 100644 index c7f781d46d152d..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a NULL with tag UNIVERSAL 5. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public void ReadNull() => ReadNull(Asn1Tag.Null); - - /// - /// Reads the next value as a NULL with a specified tag. - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public void ReadNull(Asn1Tag expectedTag) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Null); - - // T-REC-X.690-201508 sec 8.8.1 - // T-REC-X.690-201508 sec 8.8.2 - if (tag.IsConstructed || length != 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - _data = _data.Slice(headerLength); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a NULL with tag UNIVERSAL 5. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public void ReadNull() => ReadNull(Asn1Tag.Null); - - /// - /// Reads the next value as a NULL with a specified tag. - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public void ReadNull(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - valueReader.ReadNull(expectedTag); - valueReader.MatchSlice(ref _data); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs deleted file mode 100644 index 4b3756c45c4dec..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs +++ /dev/null @@ -1,789 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyOctetStringBytes( - Span destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - Asn1Tag.PrimitiveOctetString, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, - Span destination, - out int bytesWritten) - { - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contents)) - { - if (contents.Length > destination.Length) - { - bytesWritten = 0; - return false; - } - - contents.CopyTo(destination); - bytesWritten = contents.Length; - _data = _data.Slice(headerLength + contents.Length); - return true; - } - - Debug.Assert(actualTag.IsConstructed); - - bool copied = TryCopyConstructedOctetStringContents( - Slice(_data, headerLength, contentLength), - destination, - contentLength == null, - out int bytesRead, - out bytesWritten); - - if (copied) - { - _data = _data.Slice(headerLength + bytesRead); - } - - return copied; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// - /// a copy of the contents in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public byte[] ReadOctetString() - { - return ReadOctetString(Asn1Tag.PrimitiveOctetString); - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - /// - public byte[] ReadOctetString(Asn1Tag expectedTag) - { - ReadOnlySpan span; - - if (TryReadPrimitiveOctetStringBytes(expectedTag, out span)) - { - return span.ToArray(); - } - - span = PeekEncodedValue(); - - // Guaranteed long enough - byte[] rented = CryptoPool.Rent(span.Length); - int dataLength = 0; - - try - { - if (!TryCopyOctetStringBytes(expectedTag, rented, out dataLength)) - { - Debug.Fail("TryCopyOctetStringBytes failed with a pre-allocated buffer"); - throw new CryptographicException(); - } - - byte[] alloc = new byte[dataLength]; - rented.AsSpan(0, dataLength).CopyTo(alloc); - return alloc; - } - finally - { - CryptoPool.Return(rented, dataLength); - } - } - - private bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contents, - UniversalTagNumber universalTagNumber = UniversalTagNumber.OctetString) - { - actualTag = ReadTagAndLength(out contentLength, out headerLength); - CheckExpectedTag(actualTag, expectedTag, universalTagNumber); - - if (actualTag.IsConstructed) - { - if (RuleSet == AsnEncodingRules.DER) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - contents = default; - return false; - } - - Debug.Assert(contentLength.HasValue); - ReadOnlySpan encodedValue = Slice(_data, headerLength, contentLength.Value); - - if (RuleSet == AsnEncodingRules.CER && encodedValue.Length > MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - contents = encodedValue; - return true; - } - - internal bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out ReadOnlySpan contents) - { - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out _, - out _, - out int headerLength, - out contents, - universalTagNumber)) - { - _data = _data.Slice(headerLength + contents.Length); - return true; - } - - return false; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveOctetStringBytes(out ReadOnlySpan contents) => - TryReadPrimitiveOctetStringBytes(Asn1Tag.PrimitiveOctetString, out contents); - - /// - /// Reads the next value as an OCTET STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveOctetStringBytes(Asn1Tag expectedTag, out ReadOnlySpan contents) - { - return TryReadPrimitiveOctetStringBytes(expectedTag, UniversalTagNumber.OctetString, out contents); - } - - private int CountConstructedOctetString(ReadOnlySpan source, bool isIndefinite) - { - int contentLength = CopyConstructedOctetString( - source, - Span.Empty, - false, - isIndefinite, - out _); - - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER && contentLength <= MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return contentLength; - } - - private void CopyConstructedOctetString( - ReadOnlySpan source, - Span destination, - bool isIndefinite, - out int bytesRead, - out int bytesWritten) - { - bytesWritten = CopyConstructedOctetString( - source, - destination, - true, - isIndefinite, - out bytesRead); - } - - private int CopyConstructedOctetString( - ReadOnlySpan source, - Span destination, - bool write, - bool isIndefinite, - out int bytesRead) - { - bytesRead = 0; - int lastSegmentLength = MaxCERSegmentSize; - - ReadOnlySpan originalSpan = _data; - AsnValueReader tmpReader = OpenUnchecked(source, RuleSet); - Stack<(int Offset, int Length, bool IsIndefinite, int BytesRead)>? readerStack = null; - int totalLength = 0; - Asn1Tag tag = Asn1Tag.ConstructedBitString; - Span curDest = destination; - - while (true) - { - while (tmpReader.HasData) - { - tag = tmpReader.ReadTagAndLength(out int? length, out int headerLength); - - if (tag == Asn1Tag.PrimitiveOctetString) - { - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(length != null); - - // The call to Slice here sanity checks the data bounds, length.Value is not - // reliable unless this call has succeeded. - ReadOnlySpan contents = Slice(tmpReader._data, headerLength, length.Value); - - int localLen = headerLength + contents.Length; - tmpReader._data = tmpReader._data.Slice(localLen); - - bytesRead += localLen; - totalLength += contents.Length; - lastSegmentLength = contents.Length; - - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength > MaxCERSegmentSize) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (write) - { - contents.CopyTo(curDest); - curDest = curDest.Slice(contents.Length); - } - } - else if (tag == Asn1Tag.EndOfContents && isIndefinite) - { - ValidateEndOfContents(tag, length, headerLength); - - bytesRead += headerLength; - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); - tmpReader._data = topSpan.Slice(bytesRead); - - bytesRead += pushedBytesRead; - isIndefinite = wasIndefinite; - } - else - { - // We have matched the EndOfContents that brought us here. - break; - } - } - else if (tag == Asn1Tag.ConstructedOctetString) - { - if (RuleSet == AsnEncodingRules.CER) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack == null) - { - readerStack = new Stack<(int, int, bool, int)>(); - } - - if (!originalSpan.Overlaps(tmpReader._data, out int curOffset)) - { - Debug.Fail("Non-overlapping data encountered..."); - throw new CryptographicException(); - } - - readerStack.Push((curOffset, tmpReader._data.Length, isIndefinite, bytesRead)); - - tmpReader._data = Slice(tmpReader._data, headerLength, length); - bytesRead = headerLength; - isIndefinite = (length == null); - } - else - { - // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - if (isIndefinite && tag != Asn1Tag.EndOfContents) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); - - tmpReader._data = topSpan.Slice(bytesRead); - - isIndefinite = wasIndefinite; - bytesRead += pushedBytesRead; - } - else - { - return totalLength; - } - } - } - - private bool TryCopyConstructedOctetStringContents( - ReadOnlySpan source, - Span dest, - bool isIndefinite, - out int bytesRead, - out int bytesWritten) - { - bytesRead = 0; - - int contentLength = CountConstructedOctetString(source, isIndefinite); - - if (dest.Length < contentLength) - { - bytesWritten = 0; - return false; - } - - CopyConstructedOctetString(source, dest, isIndefinite, out bytesRead, out bytesWritten); - - Debug.Assert(bytesWritten == contentLength); - return true; - } - - private ReadOnlySpan GetOctetStringContents( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out int bytesRead, - ref byte[]? rented, - Span tmpSpace = default) - { - Debug.Assert(rented == null); - - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contentsOctets, - universalTagNumber)) - { - bytesRead = headerLength + contentsOctets.Length; - return contentsOctets; - } - - Debug.Assert(actualTag.IsConstructed); - - ReadOnlySpan source = Slice(_data, headerLength, contentLength); - bool isIndefinite = contentLength == null; - int octetStringLength = CountConstructedOctetString(source, isIndefinite); - - if (tmpSpace.Length < octetStringLength) - { - rented = CryptoPool.Rent(octetStringLength); - tmpSpace = rented; - } - - CopyConstructedOctetString( - source, - tmpSpace, - isIndefinite, - out int localBytesRead, - out int bytesWritten); - - Debug.Assert(bytesWritten == octetStringLength); - - bytesRead = headerLength + localBytesRead; - return tmpSpace.Slice(0, bytesWritten); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyOctetStringBytes( - Span destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - Asn1Tag.PrimitiveOctetString, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, - Span destination, - out int bytesWritten) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryCopyOctetStringBytes(expectedTag, destination, out bytesWritten)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyOctetStringBytes( - ArraySegment destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - Asn1Tag.PrimitiveOctetString, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - expectedTag, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// - /// a copy of the contents in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public byte[] ReadOctetString() - { - return ReadOctetString(Asn1Tag.PrimitiveOctetString); - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - /// - public byte[] ReadOctetString(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - byte[] ret = valueReader.ReadOctetString(expectedTag); - valueReader.MatchSlice(ref _data); - return ret; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents) => - TryReadPrimitiveOctetStringBytes(Asn1Tag.PrimitiveOctetString, out contents); - - /// - /// Reads the next value as an OCTET STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveOctetStringBytes(Asn1Tag expectedTag, out ReadOnlyMemory contents) - { - return TryReadPrimitiveOctetStringBytes(expectedTag, UniversalTagNumber.OctetString, out contents); - } - - private bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out ReadOnlyMemory contents) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan span; - - if (valueReader.TryReadPrimitiveOctetStringBytes(expectedTag, universalTagNumber, out span)) - { - contents = AsnValueReader.Slice(_data, span); - valueReader.MatchSlice(ref _data); - return true; - } - - contents = default; - return false; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs deleted file mode 100644 index 6f89866a3f84cc..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with tag UNIVERSAL 16 - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public AsnValueReader ReadSequence() => ReadSequence(Asn1Tag.Sequence); - - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// The tag to check for before reading. - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnValueReader ReadSequence(Asn1Tag expectedTag) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Sequence); - - // T-REC-X.690-201508 sec 8.9.1 - // T-REC-X.690-201508 sec 8.10.1 - if (!tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - int suffix = 0; - - if (length == null) - { - length = SeekEndOfContents(_data.Slice(headerLength)); - suffix = EndOfContentsEncodedLength; - } - - ReadOnlySpan contents = Slice(_data, headerLength, length.Value); - - _data = _data.Slice(headerLength + contents.Length + suffix); - return OpenUnchecked(contents, RuleSet); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with tag UNIVERSAL 16 - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public AsnReader ReadSequence() => ReadSequence(Asn1Tag.Sequence); - - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// The tag to check for before reading. - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnReader ReadSequence(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - AsnValueReader innerValueReader = valueReader.ReadSequence(expectedTag); - - AsnReader ret = new AsnReader(_data, RuleSet); - innerValueReader.MatchSlice(ref ret._data); - - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs deleted file mode 100644 index cd3adaa4cb8cb5..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs +++ /dev/null @@ -1,184 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public AsnValueReader ReadSetOf(bool skipSortOrderValidation = false) => - ReadSetOf(Asn1Tag.SetOf, skipSortOrderValidation); - - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// The tag to check for before reading. - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnValueReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.SetOf); - - // T-REC-X.690-201508 sec 8.12.1 - if (!tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - int suffix = 0; - - if (length == null) - { - length = SeekEndOfContents(_data.Slice(headerLength)); - suffix = EndOfContentsEncodedLength; - } - - ReadOnlySpan contents = Slice(_data, headerLength, length.Value); - - if (!skipSortOrderValidation) - { - // T-REC-X.690-201508 sec 11.6 - // BER data is not required to be sorted. - if (RuleSet == AsnEncodingRules.DER || - RuleSet == AsnEncodingRules.CER) - { - AsnValueReader reader = OpenUnchecked(contents, RuleSet); - ReadOnlySpan current = ReadOnlySpan.Empty; - - while (reader.HasData) - { - ReadOnlySpan previous = current; - current = reader.ReadEncodedValue(); - - if (SetOfValueComparer.Compare(current, previous) < 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - } - } - - _data = _data.Slice(headerLength + contents.Length + suffix); - return OpenUnchecked(contents, RuleSet); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public AsnReader ReadSetOf(bool skipSortOrderValidation = false) => - ReadSetOf(Asn1Tag.SetOf, skipSortOrderValidation); - - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// The tag to check for before reading. - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) - { - AsnValueReader valueReader = OpenValueReader(); - AsnValueReader innerValueReader = valueReader.ReadSetOf(expectedTag, skipSortOrderValidation); - - AsnReader ret = new AsnReader(_data, RuleSet); - innerValueReader.MatchSlice(ref ret._data); - - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs deleted file mode 100644 index 6775e776771008..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs +++ /dev/null @@ -1,1163 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the contents as an unprocessed - /// over the original data. - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the character string. - /// - /// - /// true and advances the reader if the value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber encodingType, - out ReadOnlySpan contents) - { - return TryReadPrimitiveCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - out contents); - } - - /// - /// Reads the next value as a character with a specified tag, returning the contents - /// as an unprocessed over the original data. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - out ReadOnlySpan contents) - { - CheckCharacterStringEncodingType(encodingType); - - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - return TryReadPrimitiveOctetStringBytes(expectedTag, encodingType, out contents); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - CheckCharacterStringEncodingType(encodingType); - - bool copied = TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination, - out int bytesRead, - out bytesWritten); - - if (copied) - { - _data = _data.Slice(bytesRead); - } - - return copied; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - public bool TryCopyCharacterString( - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - return TryCopyCharacterString( - new Asn1Tag(encodingType), - encodingType, - destination, - out charsWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - return TryCopyCharacterString(expectedTag, encodingType, encoding, destination, out charsWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the decoded value as a . - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - /// - public string ReadCharacterString(UniversalTagNumber encodingType) => - ReadCharacterString(new Asn1Tag(encodingType), encodingType); - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, returning the decoded value as a . - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public string ReadCharacterString(Asn1Tag expectedTag, UniversalTagNumber encodingType) - { - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - return ReadCharacterString(expectedTag, encodingType, encoding); - } - - // T-REC-X.690-201508 sec 8.23 - private bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - Span destination, - out int bytesRead, - out int bytesWritten) - { - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contents, - universalTagNumber)) - { - bytesWritten = contents.Length; - - if (destination.Length < bytesWritten) - { - bytesWritten = 0; - bytesRead = 0; - return false; - } - - contents.CopyTo(destination); - bytesRead = headerLength + bytesWritten; - return true; - } - - Debug.Assert(actualTag.IsConstructed); - - bool copied = TryCopyConstructedOctetStringContents( - Slice(_data, headerLength, contentLength), - destination, - contentLength == null, - out int contentBytesRead, - out bytesWritten); - - if (copied) - { - bytesRead = headerLength + contentBytesRead; - } - else - { - bytesRead = 0; - } - - return copied; - } - - private static unsafe bool TryCopyCharacterString( - ReadOnlySpan source, - Span destination, - Text.Encoding encoding, - out int charsWritten) - { - if (source.Length == 0) - { - charsWritten = 0; - return true; - } - - fixed (byte* bytePtr = &MemoryMarshal.GetReference(source)) - fixed (char* charPtr = &MemoryMarshal.GetReference(destination)) - { - try - { - int charCount = encoding.GetCharCount(bytePtr, source.Length); - - if (charCount > destination.Length) - { - charsWritten = 0; - return false; - } - - charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length); - Debug.Assert(charCount == charsWritten); - } - catch (DecoderFallbackException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - - return true; - } - } - - private string ReadCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - Text.Encoding encoding) - { - byte[]? rented = null; - - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - ReadOnlySpan contents = GetOctetStringContents( - expectedTag, - universalTagNumber, - out int bytesRead, - ref rented); - - try - { - string str; - - if (contents.Length == 0) - { - str = string.Empty; - } - else - { - unsafe - { - fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents)) - { - try - { - str = encoding.GetString(bytePtr, contents.Length); - } - catch (DecoderFallbackException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - } - } - - _data = _data.Slice(bytesRead); - return str; - } - finally - { - if (rented != null) - { - CryptoPool.Return(rented, contents.Length); - } - } - } - - private bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - Text.Encoding encoding, - Span destination, - out int charsWritten) - { - byte[]? rented = null; - - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - ReadOnlySpan contents = GetOctetStringContents( - expectedTag, - universalTagNumber, - out int bytesRead, - ref rented); - - try - { - bool copied = TryCopyCharacterString( - contents, - destination, - encoding, - out charsWritten); - - if (copied) - { - _data = _data.Slice(bytesRead); - } - - return copied; - } - finally - { - if (rented != null) - { - CryptoPool.Return(rented, contents.Length); - } - } - } - - private static void CheckCharacterStringEncodingType(UniversalTagNumber encodingType) - { - // T-REC-X.680-201508 sec 41 - switch (encodingType) - { - case UniversalTagNumber.BMPString: - case UniversalTagNumber.GeneralString: - case UniversalTagNumber.GraphicString: - case UniversalTagNumber.IA5String: - case UniversalTagNumber.ISO646String: - case UniversalTagNumber.NumericString: - case UniversalTagNumber.PrintableString: - case UniversalTagNumber.TeletexString: - // T61String is an alias for TeletexString (already listed) - case UniversalTagNumber.UniversalString: - case UniversalTagNumber.UTF8String: - case UniversalTagNumber.VideotexString: - // VisibleString is an alias for ISO646String (already listed) - return; - } - - throw new ArgumentOutOfRangeException(nameof(encodingType)); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the contents as an unprocessed - /// over the original data. - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the character string. - /// - /// - /// true and advances the reader if the value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber encodingType, - out ReadOnlyMemory contents) - { - return TryReadPrimitiveCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - out contents); - } - - /// - /// Reads the next value as a character with a specified tag, returning the contents - /// as an unprocessed over the original data. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - out ReadOnlyMemory contents) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan span; - - if (valueReader.TryReadPrimitiveCharacterStringBytes(expectedTag, encodingType, out span)) - { - contents = AsnValueReader.Slice(_data, span); - valueReader.MatchSlice(ref _data); - return true; - } - - contents = default; - return false; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryCopyCharacterStringBytes(expectedTag, encodingType, destination, out bytesWritten)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - public bool TryCopyCharacterString( - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - return TryCopyCharacterString( - new Asn1Tag(encodingType), - encodingType, - destination, - out charsWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryCopyCharacterString(expectedTag, encodingType, destination, out charsWritten)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - /// - public bool TryCopyCharacterString( - UniversalTagNumber encodingType, - ArraySegment destination, - out int charsWritten) - { - return TryCopyCharacterString( - new Asn1Tag(encodingType), - encodingType, - destination.AsSpan(), - out charsWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int charsWritten) - { - return TryCopyCharacterString( - expectedTag, - encodingType, - destination.AsSpan(), - out charsWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the decoded value as a . - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - /// - public string ReadCharacterString(UniversalTagNumber encodingType) => - ReadCharacterString(new Asn1Tag(encodingType), encodingType); - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, returning the decoded value as a . - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public string ReadCharacterString(Asn1Tag expectedTag, UniversalTagNumber encodingType) - { - AsnValueReader valueReader = OpenValueReader(); - string ret = valueReader.ReadCharacterString(expectedTag, encodingType); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs deleted file mode 100644 index 81bcbfd27d50e2..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs +++ /dev/null @@ -1,675 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers.Text; -using System.Diagnostics; - -namespace System.Security.Cryptography.Asn1 -{ - /// - /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. - /// - internal ref partial struct AsnValueReader - { - // T-REC-X.690-201508 sec 9.2 - internal const int MaxCERSegmentSize = 1000; - - // T-REC-X.690-201508 sec 8.1.5 says only 0000 is legal. - internal const int EndOfContentsEncodedLength = 2; - - private ReadOnlySpan _data; - - /// - /// The in use by this reader. - /// - public AsnEncodingRules RuleSet { get; } - - /// - /// An indication of whether or not the reader has remaining data available to process. - /// - public bool HasData => !_data.IsEmpty; - - /// - /// Construct an over with a given ruleset. - /// - /// The data to read. - /// The encoding constraints for the reader. - /// - /// This constructor does not evaluate for correctness, - /// any correctness checks are done as part of member methods. - /// - /// This constructor does not copy . The caller is responsible for - /// ensuring that the values do not change until the reader is finished. - /// - /// - /// is not defined. - /// - public AsnValueReader(ReadOnlySpan data, AsnEncodingRules ruleSet) - { - CheckEncodingRules(ruleSet); - - _data = data; - RuleSet = ruleSet; - } - - // Reversed parameter order, to be used by OpenUnchecked. - private AsnValueReader(AsnEncodingRules ruleSet, ReadOnlySpan data) - { - _data = data; - RuleSet = ruleSet; - } - - internal static AsnValueReader OpenUnchecked(ReadOnlySpan data, AsnEncodingRules ruleSet) - { - return new AsnValueReader(ruleSet, data); - } - - /// - /// Throws a standardized if the reader has remaining - /// data, performs no function if returns false. - /// - /// - /// This method provides a standardized target and standardized exception for reading a - /// "closed" structure, such as the nested content for an explicitly tagged value. - /// - public void ThrowIfNotEmpty() - { - if (HasData) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - /// - /// Read the encoded tag at the next data position, without advancing the reader. - /// - /// - /// The decoded value. - /// - /// - /// a tag could not be decoded at the reader's current position. - /// - public Asn1Tag PeekTag() - { - if (Asn1Tag.TryDecode(_data, out Asn1Tag tag, out int bytesRead)) - { - return tag; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - /// - /// Get a view of the next encoded value without - /// advancing the reader. For indefinite length encodings this includes the - /// End of Contents marker. - /// - /// A view of the next encoded value. - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - /// - public ReadOnlySpan PeekEncodedValue() - { - return Slice(_data, 0, GetNextEncodedValueLength()); - } - - internal int GetNextEncodedValueLength() - { - ReadTagAndLength(out int? length, out int bytesRead); - - if (length == null) - { - int contentsLength = SeekEndOfContents(_data.Slice(bytesRead)); - return bytesRead + contentsLength + AsnReader.EndOfContentsEncodedLength; - } - - return bytesRead + length.Value; - } - - /// - /// Get a view of the content octets (bytes) of the - /// next encoded value without advancing the reader. - /// - /// - /// A view of the contents octets of the next encoded value. - /// - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - public ReadOnlySpan PeekContentBytes() - { - (int offset, int length) = GetNextContentRange(); - return Slice(_data, offset, length); - } - - internal (int Offset, int Length) GetNextContentRange() - { - ReadTagAndLength(out int? length, out int bytesRead); - - if (length == null) - { - return (bytesRead, SeekEndOfContents(_data.Slice(bytesRead))); - } - - return (bytesRead, length.Value); - } - - /// - /// Get a view of the next encoded value, - /// and advance the reader past it. For an indefinite length encoding this includes - /// the End of Contents marker. - /// - /// A view of the next encoded value. - /// - public ReadOnlySpan ReadEncodedValue() - { - ReadOnlySpan encodedValue = PeekEncodedValue(); - _data = _data.Slice(encodedValue.Length); - return encodedValue; - } - - private static bool TryReadLength( - ReadOnlySpan source, - AsnEncodingRules ruleSet, - out int? length, - out int bytesRead) - { - length = null; - bytesRead = 0; - - AssertEncodingRules(ruleSet); - - if (source.IsEmpty) - { - return false; - } - - // T-REC-X.690-201508 sec 8.1.3 - - byte lengthOrLengthLength = source[bytesRead]; - bytesRead++; - const byte MultiByteMarker = 0x80; - - // 0x00-0x7F are direct length values. - // 0x80 is BER/CER indefinite length. - // 0x81-0xFE says that the length takes the next 1-126 bytes. - // 0xFF is forbidden. - if (lengthOrLengthLength == MultiByteMarker) - { - // T-REC-X.690-201508 sec 10.1 (DER: Length forms) - if (ruleSet == AsnEncodingRules.DER) - { - bytesRead = 0; - return false; - } - - // Null length == indefinite. - return true; - } - - if (lengthOrLengthLength < MultiByteMarker) - { - length = lengthOrLengthLength; - return true; - } - - if (lengthOrLengthLength == 0xFF) - { - bytesRead = 0; - return false; - } - - byte lengthLength = (byte)(lengthOrLengthLength & ~MultiByteMarker); - - // +1 for lengthOrLengthLength - if (lengthLength + 1 > source.Length) - { - bytesRead = 0; - return false; - } - - // T-REC-X.690-201508 sec 9.1 (CER: Length forms) - // T-REC-X.690-201508 sec 10.1 (DER: Length forms) - bool minimalRepresentation = - ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER; - - // The ITU-T specifications tecnically allow lengths up to ((2^128) - 1), but - // since Span's length is a signed Int32 we're limited to identifying memory - // that is within ((2^31) - 1) bytes of the tag start. - if (minimalRepresentation && lengthLength > sizeof(int)) - { - bytesRead = 0; - return false; - } - - uint parsedLength = 0; - - for (int i = 0; i < lengthLength; i++) - { - byte current = source[bytesRead]; - bytesRead++; - - if (parsedLength == 0) - { - if (minimalRepresentation && current == 0) - { - bytesRead = 0; - return false; - } - - if (!minimalRepresentation && current != 0) - { - // Under BER rules we could have had padding zeros, so - // once the first data bits come in check that we fit within - // sizeof(int) due to Span bounds. - - if (lengthLength - i > sizeof(int)) - { - bytesRead = 0; - return false; - } - } - } - - parsedLength <<= 8; - parsedLength |= current; - } - - // This value cannot be represented as a Span length. - if (parsedLength > int.MaxValue) - { - bytesRead = 0; - return false; - } - - if (minimalRepresentation && parsedLength < MultiByteMarker) - { - bytesRead = 0; - return false; - } - - Debug.Assert(bytesRead > 0); - length = (int)parsedLength; - return true; - } - - internal Asn1Tag ReadTagAndLength(out int? contentsLength, out int bytesRead) - { - if (Asn1Tag.TryDecode(_data, out Asn1Tag tag, out int tagBytesRead) && - TryReadLength(_data.Slice(tagBytesRead), RuleSet, out int? length, out int lengthBytesRead)) - { - int allBytesRead = tagBytesRead + lengthBytesRead; - - if (tag.IsConstructed) - { - // T-REC-X.690-201508 sec 9.1 (CER: Length forms) says constructed is always indefinite. - if (RuleSet == AsnEncodingRules.CER && length != null) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - else if (length == null) - { - // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - bytesRead = allBytesRead; - contentsLength = length; - return tag; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLength) - { - // T-REC-X.690-201508 sec 8.1.5 excludes the BER 8100 length form for 0. - if (tag.IsConstructed || length != 0 || headerLength != EndOfContentsEncodedLength) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - /// - /// Get the number of bytes between the start of and - /// the End-of-Contents marker - /// - private int SeekEndOfContents(ReadOnlySpan source) - { - return SeekEndOfContents(source, RuleSet); - } - - /// - /// Get the number of bytes between the start of and - /// the End-of-Contents marker - /// - internal static int SeekEndOfContents(ReadOnlySpan source, AsnEncodingRules ruleSet) - { - ReadOnlySpan cur = source; - int totalLen = 0; - - AsnValueReader tmpReader = OpenUnchecked(cur, ruleSet); - // Our reader is bounded by int.MaxValue. - // The most aggressive data input would be a one-byte tag followed by - // indefinite length "ad infinitum", which would be half the input. - // So the depth marker can never overflow the signed integer space. - int depth = 1; - - while (tmpReader.HasData) - { - Asn1Tag tag = tmpReader.ReadTagAndLength(out int? length, out int bytesRead); - - if (tag == Asn1Tag.EndOfContents) - { - ValidateEndOfContents(tag, length, bytesRead); - - depth--; - - if (depth == 0) - { - // T-REC-X.690-201508 sec 8.1.1.1 / 8.1.1.3 indicate that the - // End-of-Contents octets are "after" the contents octets, not - // "at the end" of them, so we don't include these bytes in the - // accumulator. - return totalLen; - } - } - - // We found another indefinite length, that means we need to find another - // EndOfContents marker to balance it out. - if (length == null) - { - depth++; - tmpReader._data = tmpReader._data.Slice(bytesRead); - totalLen += bytesRead; - } - else - { - // This will throw a CryptographicException if the length exceeds our bounds. - ReadOnlySpan tlv = Slice(tmpReader._data, 0, bytesRead + length.Value); - - // No exception? Then slice the data and continue. - tmpReader._data = tmpReader._data.Slice(tlv.Length); - totalLen += tlv.Length; - } - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan data, int bytesToRead) - { - int value = ParseNonNegativeInt(Slice(data, 0, bytesToRead)); - data = data.Slice(bytesToRead); - - return value; - } - - internal static int ParseNonNegativeInt(ReadOnlySpan data) - { - if (Utf8Parser.TryParse(data, out uint value, out int consumed) && - value <= int.MaxValue && - consumed == data.Length) - { - return (int)value; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private static ReadOnlySpan SliceAtMost(ReadOnlySpan source, int longestPermitted) - { - int len = Math.Min(longestPermitted, source.Length); - return source.Slice(0, len); - } - - private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int length) - { - Debug.Assert(offset >= 0); - - if (length < 0 || source.Length - offset < length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return source.Slice(offset, length); - } - - private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int? length) - { - Debug.Assert(offset >= 0); - - if (length == null) - { - return source.Slice(offset); - } - - int lengthVal = length.Value; - - if (lengthVal < 0 || source.Length - offset < lengthVal) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return source.Slice(offset, lengthVal); - } - - internal static ReadOnlyMemory Slice(ReadOnlyMemory bigger, ReadOnlySpan smaller) - { - if (smaller.IsEmpty) - { - return default; - } - - if (bigger.Span.Overlaps(smaller, out int offset)) - { - return bigger.Slice(offset, smaller.Length); - } - - Debug.Fail("AsnReader asked for a matching slice from a non-overlapping input"); - throw new CryptographicException(); - } - - /// - /// Slices to represent the same data this reader value has. - /// - /// - /// does not represent a super-range of the data this reader is processing. - /// - internal void MatchSlice(ref ReadOnlyMemory memory) - { - memory = Slice(memory, _data); - } - - [Conditional("DEBUG")] - private static void AssertEncodingRules(AsnEncodingRules ruleSet) - { - Debug.Assert(ruleSet >= AsnEncodingRules.BER && ruleSet <= AsnEncodingRules.DER); - } - - internal static void CheckEncodingRules(AsnEncodingRules ruleSet) - { - if (ruleSet != AsnEncodingRules.BER && - ruleSet != AsnEncodingRules.CER && - ruleSet != AsnEncodingRules.DER) - { - throw new ArgumentOutOfRangeException(nameof(ruleSet)); - } - } - - internal static void CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber) - { - if (expectedTag.TagClass == TagClass.Universal && expectedTag.TagValue != (int)tagNumber) - { - throw new ArgumentException( - SR.Cryptography_Asn_UniversalValueIsFixed, - nameof(expectedTag)); - } - - if (expectedTag.TagClass != tag.TagClass || expectedTag.TagValue != tag.TagValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - } - - /// - /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. - /// - internal partial class AsnReader - { - internal const int MaxCERSegmentSize = AsnValueReader.MaxCERSegmentSize; - internal const int EndOfContentsEncodedLength = AsnValueReader.EndOfContentsEncodedLength; - - private ReadOnlyMemory _data; - - /// - /// The in use by this reader. - /// - public AsnEncodingRules RuleSet { get; } - - /// - /// An indication of whether or not the reader has remaining data available to process. - /// - public bool HasData => !_data.IsEmpty; - - /// - /// Construct an over with a given ruleset. - /// - /// The data to read. - /// The encoding constraints for the reader. - /// - /// This constructor does not evaluate for correctness, - /// any correctness checks are done as part of member methods. - /// - /// This constructor does not copy . The caller is responsible for - /// ensuring that the values do not change until the reader is finished. - /// - /// - /// is not defined. - /// - public AsnReader(ReadOnlyMemory data, AsnEncodingRules ruleSet) - { - AsnValueReader.CheckEncodingRules(ruleSet); - - _data = data; - RuleSet = ruleSet; - } - - /// - /// Throws a standardized if the reader has remaining - /// data, performs no function if returns false. - /// - /// - /// This method provides a standardized target and standardized exception for reading a - /// "closed" structure, such as the nested content for an explicitly tagged value. - /// - public void ThrowIfNotEmpty() - { - if (HasData) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - /// - /// Read the encoded tag at the next data position, without advancing the reader. - /// - /// - /// The decoded value. - /// - /// - /// a tag could not be decoded at the reader's current position. - /// - public Asn1Tag PeekTag() - { - if (Asn1Tag.TryDecode(_data.Span, out Asn1Tag tag, out int bytesRead)) - { - return tag; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private AsnValueReader OpenValueReader() - { - return AsnValueReader.OpenUnchecked(_data.Span, RuleSet); - } - - /// - /// Get a view of the next encoded value without - /// advancing the reader. For indefinite length encodings this includes the - /// End of Contents marker. - /// - /// A view of the next encoded value. - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - /// - public ReadOnlyMemory PeekEncodedValue() - { - AsnValueReader reader = OpenValueReader(); - return Slice(_data, 0, reader.GetNextEncodedValueLength()); - } - - /// - /// Get a view of the content octets (bytes) of the - /// next encoded value without advancing the reader. - /// - /// - /// A view of the contents octets of the next encoded value. - /// - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - public ReadOnlyMemory PeekContentBytes() - { - AsnValueReader reader = OpenValueReader(); - (int offset, int length) = reader.GetNextContentRange(); - return Slice(_data, offset, length); - } - - /// - /// Get a view of the next encoded value, - /// and advance the reader past it. For an indefinite length encoding this includes - /// the End of Contents marker. - /// - /// A view of the next encoded value. - /// - public ReadOnlyMemory ReadEncodedValue() - { - ReadOnlyMemory encodedValue = PeekEncodedValue(); - _data = _data.Slice(encodedValue.Length); - return encodedValue; - } - - internal Asn1Tag ReadTagAndLength(out int? contentsLength, out int bytesRead) - { - AsnValueReader reader = OpenValueReader(); - return reader.ReadTagAndLength(out contentsLength, out bytesRead); - } - - private static ReadOnlyMemory Slice(ReadOnlyMemory source, int offset, int length) - { - Debug.Assert(offset >= 0); - - if (length < 0 || source.Length - offset < length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return source.Slice(offset, length); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs new file mode 100644 index 00000000000000..23b3292f817c0f --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + internal ref struct AsnValueReader + { + private static readonly byte[] s_singleByte = new byte[1]; + + private ReadOnlySpan _span; + private readonly AsnEncodingRules _ruleSet; + + internal AsnValueReader(ReadOnlySpan span, AsnEncodingRules ruleSet) + { + _span = span; + _ruleSet = ruleSet; + } + + internal bool HasData => !_span.IsEmpty; + + internal void ThrowIfNotEmpty() + { + if (!_span.IsEmpty) + { + new AsnReader(s_singleByte, _ruleSet).ThrowIfNotEmpty(); + } + } + + internal Asn1Tag PeekTag() + { + return Asn1Tag.Decode(_span, out _); + } + + public ReadOnlySpan PeekEncodedValue() + { + AsnDecoder.ReadEncodedValue(_span, _ruleSet, out _, out _, out int consumed); + return _span.Slice(0, consumed); + } + + internal ReadOnlySpan ReadEncodedValue() + { + ReadOnlySpan value = PeekEncodedValue(); + _span = _span.Slice(value.Length); + return value; + } + + internal bool ReadBoolean(Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.ReadBoolean(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal BigInteger ReadInteger(Asn1Tag? expectedTag = default) + { + BigInteger ret = AsnDecoder.ReadInteger(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal bool TryReadInt32(out int value, Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.TryReadInt32(_span, _ruleSet, out value, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal ReadOnlySpan ReadIntegerBytes(Asn1Tag? expectedTag = default) + { + ReadOnlySpan ret = AsnDecoder.ReadIntegerBytes(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal bool TryReadPrimitiveBitString( + out int unusedBitCount, + out ReadOnlySpan value, + Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.TryReadPrimitiveBitString( + _span, + _ruleSet, + out unusedBitCount, + out value, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = default) + { + byte[] ret = AsnDecoder.ReadBitString( + _span, + _ruleSet, + out unusedBitCount, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal TFlagsEnum ReadNamedBitListValue(Asn1Tag? expectedTag = default) where TFlagsEnum : Enum + { + TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal bool TryReadPrimitiveOctetString( + out ReadOnlySpan value, + Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.TryReadPrimitiveOctetString( + _span, + _ruleSet, + out value, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal byte[] ReadOctetString(Asn1Tag? expectedTag = default) + { + byte[] ret = AsnDecoder.ReadOctetString( + _span, + _ruleSet, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal string ReadObjectIdentifier(Asn1Tag? expectedTag = default) + { + string ret = AsnDecoder.ReadObjectIdentifier(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal AsnValueReader ReadSequence(Asn1Tag? expectedTag = default) + { + AsnDecoder.ReadSequence( + _span, + _ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + expectedTag); + + ReadOnlySpan content = _span.Slice(contentOffset, contentLength); + _span = _span.Slice(bytesConsumed); + return new AsnValueReader(content, _ruleSet); + } + + internal AsnValueReader ReadSetOf(Asn1Tag? expectedTag = default) + { + AsnDecoder.ReadSetOf( + _span, + _ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + expectedTag: expectedTag); + + ReadOnlySpan content = _span.Slice(contentOffset, contentLength); + _span = _span.Slice(bytesConsumed); + return new AsnValueReader(content, _ruleSet); + } + + internal DateTimeOffset ReadUtcTime(Asn1Tag? expectedTag = default) + { + DateTimeOffset ret = AsnDecoder.ReadUtcTime(_span, _ruleSet, out int consumed, expectedTag: expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + public DateTimeOffset ReadGeneralizedTime(Asn1Tag? expectedTag = default) + { + DateTimeOffset ret = AsnDecoder.ReadGeneralizedTime(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + public string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = default) + { + string ret = AsnDecoder.ReadCharacterString(_span, _ruleSet, encodingType, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + } + + internal static class AsnWriterExtensions + { + internal static void WriteEncodedValueForCrypto( + this AsnWriter writer, + ReadOnlySpan value) + { + try + { + writer.WriteEncodedValue(value); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void WriteObjectIdentifierForCrypto( + this AsnWriter writer, + string value) + { + try + { + writer.WriteObjectIdentifier(value); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs deleted file mode 100644 index 64eb1102611c10..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs +++ /dev/null @@ -1,387 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write a Bit String value with a tag UNIVERSAL 3. - /// - /// The value to write. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// is not in the range [0,7] - /// - /// - /// has length 0 and is not 0 --OR-- - /// is not empty and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString(ReadOnlySpan bitString, int unusedBitCount = 0) - { - WriteBitStringCore(Asn1Tag.PrimitiveBitString, bitString, unusedBitCount); - } - - /// - /// Write a Bit String value with a specified tag. - /// - /// The tag to write. - /// The value to write. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// is not in the range [0,7] - /// - /// - /// has length 0 and is not 0 --OR-- - /// is not empty and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount = 0) - { - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - // Primitive or constructed, doesn't matter. - WriteBitStringCore(tag, bitString, unusedBitCount); - } - - // T-REC-X.690-201508 sec 8.6 - private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount) - { - // T-REC-X.690-201508 sec 8.6.2.2 - if (unusedBitCount < 0 || unusedBitCount > 7) - { - throw new ArgumentOutOfRangeException( - nameof(unusedBitCount), - unusedBitCount, - SR.Cryptography_Asn_UnusedBitCountRange); - } - - CheckDisposed(); - - // T-REC-X.690-201508 sec 8.6.2.3 - if (bitString.Length == 0 && unusedBitCount != 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1]; - - // T-REC-X.690-201508 sec 11.2 - // - // This could be ignored for BER, but since DER is more common and - // it likely suggests a program error on the caller, leave it enabled for - // BER for now. - if (!CheckValidLastByte(lastByte, unusedBitCount)) - { - // TODO: Probably warrants a distinct message. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (RuleSet == AsnEncodingRules.CER) - { - // T-REC-X.690-201508 sec 9.2 - // - // If it's not within a primitive segment, use the constructed encoding. - // (>= instead of > because of the unused bit count byte) - if (bitString.Length >= AsnReader.MaxCERSegmentSize) - { - WriteConstructedCerBitString(tag, bitString, unusedBitCount); - return; - } - } - - // Clear the constructed flag, if present. - WriteTag(tag.AsPrimitive()); - // The unused bits byte requires +1. - WriteLength(bitString.Length + 1); - _buffer[_offset] = (byte)unusedBitCount; - _offset++; - bitString.CopyTo(_buffer.AsSpan(_offset)); - _offset += bitString.Length; - } - -#if NETCOREAPP || NETSTANDARD2_1 - /// - /// Write a Bit String value via a callback, with a tag UNIVERSAL 3. - /// - /// The total number of bytes to write. - /// A state object to pass to . - /// A callback to invoke for populating the Bit String. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// is negative --OR-- - /// is not in the range [0,7] - /// - /// - /// is 0 and is not 0 --OR-- - /// is not 0 and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString( - int byteLength, - TState state, - SpanAction action, - int unusedBitCount = 0) - { - WriteBitStringCore(Asn1Tag.PrimitiveBitString, byteLength, state, action, unusedBitCount); - } - - /// - /// Write a Bit String value via a callback, with a specified tag. - /// - /// The tag to write. - /// The total number of bytes to write. - /// A state object to pass to . - /// A callback to invoke for populating the Bit String. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// is negative --OR-- - /// is not in the range [0,7] - /// - /// - /// is 0 and is not 0 --OR-- - /// is not 0 and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString( - Asn1Tag tag, - int byteLength, - TState state, - SpanAction action, - int unusedBitCount = 0) - { - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - // Primitive or constructed, doesn't matter. - WriteBitStringCore(tag, byteLength, state, action, unusedBitCount); - } - - // T-REC-X.690-201508 sec 8.6 - private void WriteBitStringCore( - Asn1Tag tag, - int byteLength, - TState state, - SpanAction action, - int unusedBitCount = 0) - { - if (byteLength == 0) - { - WriteBitStringCore(tag, ReadOnlySpan.Empty, unusedBitCount); - return; - } - - // T-REC-X.690-201508 sec 8.6.2.2 - if (unusedBitCount < 0 || unusedBitCount > 7) - { - throw new ArgumentOutOfRangeException( - nameof(unusedBitCount), - unusedBitCount, - SR.Cryptography_Asn_UnusedBitCountRange); - } - - CheckDisposed(); - - int savedOffset = _offset; - Span scratchSpace; - byte[]? ensureNoExtraCopy = null; - int expectedSize = 0; - - // T-REC-X.690-201508 sec 9.2 - // - // If it's not within a primitive segment, use the constructed encoding. - // (>= instead of > because of the unused bit count byte) - bool segmentedWrite = - RuleSet == AsnEncodingRules.CER && byteLength >= AsnReader.MaxCERSegmentSize; - - if (segmentedWrite) - { - // Rather than call the callback multiple times, grow the buffer to allow - // for enough space for the final output, then return a buffer where the last segment - // is in the correct place. (Data will shift backwards to the right spot while writing - // other segments). - expectedSize = DetermineCerBitStringTotalLength(tag, byteLength); - EnsureWriteCapacity(expectedSize); - int overhead = expectedSize - byteLength; - - // Start writing where the last content byte is in the correct place, which is - // after all of the overhead, but ending before the two byte end-of-contents marker. - int scratchStart = overhead - 2; - ensureNoExtraCopy = _buffer; - scratchSpace = _buffer.AsSpan(scratchStart, byteLength); - - // Don't let gapped-writes be unpredictable. - scratchSpace.Clear(); - } - else - { - WriteTag(tag.AsPrimitive()); - // The unused bits byte requires +1. - WriteLength(byteLength + 1); - - _buffer[_offset] = (byte)unusedBitCount; - _offset++; - - scratchSpace = _buffer.AsSpan(_offset, byteLength); - } - - action(scratchSpace, state); - - // T-REC-X.690-201508 sec 11.2 - // - // This could be ignored for BER, but since DER is more common and - // it likely suggests a program error on the caller, leave it enabled for - // BER for now. - if (!CheckValidLastByte(scratchSpace[byteLength - 1], unusedBitCount)) - { - // Since we are restoring _offset we won't clear this on a grow or Dispose, - // so clear it now. - _offset = savedOffset; - scratchSpace.Clear(); - - // TODO: Probably warrants a distinct message. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (segmentedWrite) - { - WriteConstructedCerBitString(tag, scratchSpace, unusedBitCount); - Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); - Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during while writing a bit string via callback"); - } - else - { - _offset += byteLength; - } - } -#endif - - private static bool CheckValidLastByte(byte lastByte, int unusedBitCount) - { - // If 3 bits are "unused" then build a mask for them to check for 0. - // 1 << 3 => 0b0000_1000 - // subtract 1 => 0b000_0111 - int mask = (1 << unusedBitCount) - 1; - return ((lastByte & mask) == 0); - } - - private static int DetermineCerBitStringTotalLength(Asn1Tag tag, int contentLength) - { - const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; - // Every segment has an "unused bit count" byte. - const int MaxCERContentSize = MaxCERSegmentSize - 1; - Debug.Assert(contentLength > MaxCERContentSize); - - int fullSegments = Math.DivRem(contentLength, MaxCERContentSize, out int lastContentSize); - - // The tag size is 1 byte. - // The length will always be encoded as 82 03 E8 (3 bytes) - // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) - const int FullSegmentEncodedSize = 1004; - Debug.Assert( - FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); - - int remainingEncodedSize; - - if (lastContentSize == 0) - { - remainingEncodedSize = 0; - } - else - { - // One byte of tag, minimum one byte of length, and one byte of unused bit count. - remainingEncodedSize = 3 + lastContentSize + GetEncodedLengthSubsequentByteCount(lastContentSize); - } - - // Reduce the number of copies by pre-calculating the size. - // +2 for End-Of-Contents - // +1 for 0x80 indefinite length - // +tag length - return fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 3 + tag.CalculateEncodedSize(); - } - - // T-REC-X.690-201508 sec 9.2, 8.6 - private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan payload, int unusedBitCount) - { - const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; - // Every segment has an "unused bit count" byte. - const int MaxCERContentSize = MaxCERSegmentSize - 1; - Debug.Assert(payload.Length > MaxCERContentSize); - - int expectedSize = DetermineCerBitStringTotalLength(tag, payload.Length); - EnsureWriteCapacity(expectedSize); - int savedOffset = _offset; - - WriteTag(tag.AsConstructed()); - // T-REC-X.690-201508 sec 9.1 - // Constructed CER uses the indefinite form. - WriteLength(-1); - - byte[] ensureNoExtraCopy = _buffer; - - ReadOnlySpan remainingData = payload; - Span dest; - Asn1Tag primitiveBitString = Asn1Tag.PrimitiveBitString; - - while (remainingData.Length > MaxCERContentSize) - { - // T-REC-X.690-201508 sec 8.6.4.1 - WriteTag(primitiveBitString); - WriteLength(MaxCERSegmentSize); - // 0 unused bits in this segment. - _buffer[_offset] = 0; - _offset++; - - dest = _buffer.AsSpan(_offset); - remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); - - remainingData = remainingData.Slice(MaxCERContentSize); - _offset += MaxCERContentSize; - } - - WriteTag(primitiveBitString); - WriteLength(remainingData.Length + 1); - - _buffer[_offset] = (byte)unusedBitCount; - _offset++; - - dest = _buffer.AsSpan(_offset); - remainingData.CopyTo(dest); - _offset += remainingData.Length; - - WriteEndOfContents(); - - Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); - Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerBitString)}"); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs deleted file mode 100644 index 695c958ccdeb0d..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The boxed enumeration value to write - /// - /// is null - /// - /// - /// is not a boxed enum value --OR-- - /// the unboxed type of is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - WriteEnumeratedValue(Asn1Tag.Enumerated, enumValue); - } - - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The boxed enumeration value to write - /// - /// is null - /// - /// - /// is not a boxed enum value --OR-- - /// is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(TEnum enumValue) where TEnum : struct - { - WriteEnumeratedValue(Asn1Tag.Enumerated, typeof(TEnum), enumValue); - } - - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The tag to write. - /// The boxed enumeration value to write. - /// - /// is null - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not a boxed enum value --OR-- - /// the unboxed type of is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(Asn1Tag tag, object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - WriteEnumeratedValue(tag.AsPrimitive(), enumValue.GetType(), enumValue); - } - - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The tag to write. - /// The boxed enumeration value to write. - /// - /// is null - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not an enum --OR-- - /// is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(Asn1Tag tag, TEnum enumValue) where TEnum : struct - { - WriteEnumeratedValue(tag.AsPrimitive(), typeof(TEnum), enumValue); - } - - // T-REC-X.690-201508 sec 8.4 - private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object enumValue) - { - CheckUniversalTag(tag, UniversalTagNumber.Enumerated); - - Type backingType = tEnum.GetEnumUnderlyingType(); - - if (tEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, - nameof(tEnum)); - } - - if (backingType == typeof(ulong)) - { - ulong numericValue = Convert.ToUInt64(enumValue); - // T-REC-X.690-201508 sec 8.4 - WriteNonNegativeIntegerCore(tag, numericValue); - } - else - { - // All other types fit in a (signed) long. - long numericValue = Convert.ToInt64(enumValue); - // T-REC-X.690-201508 sec 8.4 - WriteIntegerCore(tag, numericValue); - } - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs deleted file mode 100644 index 76c7d0d263c827..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write a [] enum value as a NamedBitList with - /// tag UNIVERSAL 3. - /// - /// The boxed enumeration value to write - /// - /// is null - /// - /// - /// is not a boxed enum value --OR-- - /// the unboxed type of is not declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteNamedBitList(object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); - } - - /// - /// Write a [] enum value as a NamedBitList with - /// tag UNIVERSAL 3. - /// - /// The enumeration value to write - /// - /// is not an enum value --OR-- - /// is not declared [] - /// - /// The writer has been Disposed. - /// - public void WriteNamedBitList(TEnum enumValue) where TEnum : struct - { - WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); - } - - /// - /// Write a [] enum value as a NamedBitList with - /// a specified tag. - /// - /// The tag to write. - /// The boxed enumeration value to write - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not a boxed enum value --OR-- - /// the unboxed type of is not declared [] - /// - /// - /// is null - /// - /// The writer has been Disposed. - public void WriteNamedBitList(Asn1Tag tag, object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - WriteNamedBitList(tag, enumValue.GetType(), enumValue); - } - - /// - /// Write a [] enum value as a NamedBitList with - /// a specified tag. - /// - /// The tag to write. - /// The enumeration value to write - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not an enum value --OR-- - /// is not declared [] - /// - /// The writer has been Disposed. - public void WriteNamedBitList(Asn1Tag tag, TEnum enumValue) where TEnum : struct - { - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - WriteNamedBitList(tag, typeof(TEnum), enumValue); - } - - private void WriteNamedBitList(Asn1Tag tag, Type tEnum, object enumValue) - { - Type backingType = tEnum.GetEnumUnderlyingType(); - - if (!tEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, - nameof(tEnum)); - } - - ulong integralValue; - - if (backingType == typeof(ulong)) - { - integralValue = Convert.ToUInt64(enumValue); - } - else - { - // All other types fit in a (signed) long. - long numericValue = Convert.ToInt64(enumValue); - integralValue = unchecked((ulong)numericValue); - } - - WriteNamedBitList(tag, integralValue); - } - - // T-REC-X.680-201508 sec 22 - // T-REC-X.690-201508 sec 8.6, 11.2.2 - private void WriteNamedBitList(Asn1Tag tag, ulong integralValue) - { - Span temp = stackalloc byte[sizeof(ulong)]; - // Reset to all zeros, since we're just going to or-in bits we need. - temp.Clear(); - - int indexOfHighestSetBit = -1; - - for (int i = 0; integralValue != 0; integralValue >>= 1, i++) - { - if ((integralValue & 1) != 0) - { - temp[i / 8] |= (byte)(0x80 >> (i % 8)); - indexOfHighestSetBit = i; - } - } - - if (indexOfHighestSetBit < 0) - { - // No bits were set; this is an empty bit string. - // T-REC-X.690-201508 sec 11.2.2-note2 - WriteBitString(tag, ReadOnlySpan.Empty); - } - else - { - // At least one bit was set. - // Determine the shortest length necessary to represent the bit string. - - // Since "bit 0" gets written down 0 => 1. - // Since "bit 8" is in the second byte 8 => 2. - // That makes the formula ((bit / 8) + 1) instead of ((bit + 7) / 8). - int byteLen = (indexOfHighestSetBit / 8) + 1; - int unusedBitCount = 7 - (indexOfHighestSetBit % 8); - - WriteBitString( - tag, - temp.Slice(0, byteLen), - unusedBitCount); - } - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs deleted file mode 100644 index 78d4268233921e..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Begin writing a Sequence with tag UNIVERSAL 16. - /// - /// The writer has been Disposed. - /// - /// - public void PushSequence() - { - PushSequenceCore(Asn1Tag.Sequence); - } - - /// - /// Begin writing a Sequence with a specified tag. - /// - /// The tag to write. - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// The writer has been Disposed. - /// - public void PushSequence(Asn1Tag tag) - { - CheckUniversalTag(tag, UniversalTagNumber.Sequence); - - // Assert the constructed flag, in case it wasn't. - PushSequenceCore(tag.AsConstructed()); - } - - /// - /// Indicate that the open Sequence with tag UNIVERSAL 16 is closed, - /// returning the writer to the parent context. - /// - /// - /// the writer is not currently positioned within a Sequence with tag UNIVERSAL 16 - /// - /// The writer has been Disposed. - /// - /// - public void PopSequence() - { - PopSequenceCore(Asn1Tag.Sequence); - } - - /// - /// Indicate that the open Sequence with the specified tag is closed, - /// returning the writer to the parent context. - /// - /// The tag to write. - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// the writer is not currently positioned within a Sequence with the specified tag - /// - /// The writer has been Disposed. - /// - /// - public void PopSequence(Asn1Tag tag) - { - // PopSequence shouldn't be used to pop a SetOf. - CheckUniversalTag(tag, UniversalTagNumber.Sequence); - - // Assert the constructed flag, in case it wasn't. - PopSequenceCore(tag.AsConstructed()); - } - - // T-REC-X.690-201508 sec 8.9, 8.10 - private void PushSequenceCore(Asn1Tag tag) - { - PushTag(tag.AsConstructed(), UniversalTagNumber.Sequence); - } - - // T-REC-X.690-201508 sec 8.9, 8.10 - private void PopSequenceCore(Asn1Tag tag) - { - PopTag(tag, UniversalTagNumber.Sequence); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs deleted file mode 100644 index 83cb6e8b140414..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs +++ /dev/null @@ -1,203 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write the provided string using the specified encoding type using the UNIVERSAL - /// tag corresponding to the encoding type. - /// - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// is null - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// The writer has been Disposed. - /// - public void WriteCharacterString(UniversalTagNumber encodingType, string str) - { - if (str == null) - throw new ArgumentNullException(nameof(str)); - - WriteCharacterString(encodingType, str.AsSpan()); - } - - /// - /// Write the provided string using the specified encoding type using the UNIVERSAL - /// tag corresponding to the encoding type. - /// - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// The writer has been Disposed. - /// - public void WriteCharacterString(UniversalTagNumber encodingType, ReadOnlySpan str) - { - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - - WriteCharacterStringCore(new Asn1Tag(encodingType), encoding, str); - } - - /// - /// Write the provided string using the specified encoding type using the specified - /// tag corresponding to the encoding type. - /// - /// The tag to write. - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// is null - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// The writer has been Disposed. - public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, string str) - { - if (str == null) - throw new ArgumentNullException(nameof(str)); - - WriteCharacterString(tag, encodingType, str.AsSpan()); - } - - /// - /// Write the provided string using the specified encoding type using the specified - /// tag corresponding to the encoding type. - /// - /// The tag to write. - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// The writer has been Disposed. - public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, ReadOnlySpan str) - { - CheckUniversalTag(tag, encodingType); - - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - WriteCharacterStringCore(tag, encoding, str); - } - - // T-REC-X.690-201508 sec 8.23 - private void WriteCharacterStringCore(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str) - { - int size = -1; - - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER) - { - // TODO: Split this for netstandard vs netcoreapp for span?. - unsafe - { - fixed (char* strPtr = &MemoryMarshal.GetReference(str)) - { - size = encoding.GetByteCount(strPtr, str.Length); - - // If it exceeds the primitive segment size, use the constructed encoding. - if (size > AsnReader.MaxCERSegmentSize) - { - WriteConstructedCerCharacterString(tag, encoding, str, size); - return; - } - } - } - } - - // TODO: Split this for netstandard vs netcoreapp for span?. - unsafe - { - fixed (char* strPtr = &MemoryMarshal.GetReference(str)) - { - if (size < 0) - { - size = encoding.GetByteCount(strPtr, str.Length); - } - - // Clear the constructed tag, if present. - WriteTag(tag.AsPrimitive()); - WriteLength(size); - Span dest = _buffer.AsSpan(_offset, size); - - fixed (byte* destPtr = &MemoryMarshal.GetReference(dest)) - { - int written = encoding.GetBytes(strPtr, str.Length, destPtr, dest.Length); - - if (written != size) - { - Debug.Fail($"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); - throw new InvalidOperationException(); - } - } - - _offset += size; - } - } - } - - private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str, int size) - { - Debug.Assert(size > AsnReader.MaxCERSegmentSize); - - byte[] tmp; - - unsafe - { - fixed (char* strPtr = &MemoryMarshal.GetReference(str)) - { - tmp = CryptoPool.Rent(size); - - fixed (byte* destPtr = tmp) - { - int written = encoding.GetBytes(strPtr, str.Length, destPtr, tmp.Length); - - if (written != size) - { - Debug.Fail( - $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); - throw new InvalidOperationException(); - } - } - } - } - - WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size)); - CryptoPool.Return(tmp, size); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems index f495ac7aeb3bb1..a1b13260aaad0a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems @@ -10,110 +10,8 @@ - - Common\System\Security\Cryptography\Asn1Reader\Asn1Tag.cs - - - Common\System\Security\Cryptography\Asn1Reader\Asn1Tag.Accelerators.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnCharacterStringEncodings.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnEncodingRules.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.BitString.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Boolean.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Enumerated.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.GeneralizedTime.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Integer.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.NamedBitList.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Null.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.OctetString.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Oid.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Sequence.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.SetOf.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.Text.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnReader.UtcTime.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.BitString.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Boolean.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Enumerated.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.GeneralizedTime.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Integer.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.NamedBitList.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Null.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.OctetString.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Oid.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Sequence.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.SetOf.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.Text.cs - - - Common\System\Security\Cryptography\Asn1Reader\AsnWriter.UtcTime.cs - - - Common\System\Security\Cryptography\Asn1Reader\SetOfValueComparer.cs - - - Common\System\Security\Cryptography\Asn1Reader\TagClass.cs - - - Common\System\Security\Cryptography\Asn1Reader\UniversalTagNumber.cs + + Common\System\Security\Cryptography\Asn1Reader\AsnValueReader.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs b/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs index 26022e2cb1d72e..22e592573c1053 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs @@ -5,6 +5,7 @@ #nullable enable using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; @@ -53,10 +54,8 @@ internal static byte[] ExportEncryptedPkcs8PrivateKey( return key.ExportEncryptedPkcs8PrivateKey(ReadOnlySpan.Empty, pbeParameters); } - using (AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, passwordBytes, pbeParameters)) - { - return writer.Encode(); - } + AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, passwordBytes, pbeParameters); + return writer.Encode(); } internal static bool TryExportEncryptedPkcs8PrivateKey( @@ -76,10 +75,8 @@ internal static bool TryExportEncryptedPkcs8PrivateKey( out bytesWritten); } - using (AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, passwordBytes, pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, passwordBytes, pbeParameters); + return writer.TryEncode(destination, out bytesWritten); } internal static byte[] ExportEncryptedPkcs8PrivateKey( @@ -87,10 +84,8 @@ internal static byte[] ExportEncryptedPkcs8PrivateKey( ReadOnlySpan password, PbeParameters pbeParameters) { - using (AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, password, pbeParameters)) - { - return writer.Encode(); - } + AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, password, pbeParameters); + return writer.Encode(); } internal static bool TryExportEncryptedPkcs8PrivateKey( @@ -100,23 +95,26 @@ internal static bool TryExportEncryptedPkcs8PrivateKey( Span destination, out int bytesWritten) { - using (AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, password, pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = RewriteEncryptedPkcs8PrivateKey(key, password, pbeParameters); + return writer.TryEncode(destination, out bytesWritten); } - internal static unsafe Pkcs8Response ImportPkcs8PrivateKey(ReadOnlySpan source, out int bytesRead) + internal static Pkcs8Response ImportPkcs8PrivateKey(ReadOnlySpan source, out int bytesRead) { int len; - fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) - { - AsnReader reader = new AsnReader(manager.Memory, AsnEncodingRules.BER); - len = reader.ReadEncodedValue().Length; - } + AsnDecoder.ReadEncodedValue( + source, + AsnEncodingRules.BER, + out _, + out _, + out len); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } bytesRead = len; @@ -135,8 +133,27 @@ internal static unsafe Pkcs8Response ImportPkcs8PrivateKey(ReadOnlySpan so throw; } - return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan()); + return ImportPkcs8(pkcs8ZeroPublicKey); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static Pkcs8Response ImportPkcs8(AsnWriter pkcs8Writer) + { + byte[] tmp = CryptoPool.Rent(pkcs8Writer.GetEncodedLength()); + + if (!pkcs8Writer.TryEncode(tmp, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new CryptographicException(); } + + Pkcs8Response ret = ImportPkcs8(tmp.AsSpan(0, written)); + CryptoPool.Return(tmp, written); + return ret; } internal static unsafe Pkcs8Response ImportEncryptedPkcs8PrivateKey( @@ -148,41 +165,48 @@ internal static unsafe Pkcs8Response ImportEncryptedPkcs8PrivateKey( { using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) { - // Since there's no bytes-based-password PKCS8 import in CNG, just do the decryption - // here and call the unencrypted PKCS8 import. - ArraySegment decrypted = KeyFormatHelper.DecryptPkcs8( - passwordBytes, - manager.Memory, - out bytesRead); - - Span decryptedSpan = decrypted; - try { - return ImportPkcs8(decryptedSpan); - } - catch (CryptographicException e) - { - AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan); + // Since there's no bytes-based-password PKCS8 import in CNG, just do the decryption + // here and call the unencrypted PKCS8 import. + ArraySegment decrypted = KeyFormatHelper.DecryptPkcs8( + passwordBytes, + manager.Memory, + out bytesRead); - if (pkcs8ZeroPublicKey == null) - { - throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); - } + Span decryptedSpan = decrypted; try { - return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan()); + return ImportPkcs8(decryptedSpan); } - catch (CryptographicException) + catch (CryptographicException e) + { + AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan); + + if (pkcs8ZeroPublicKey == null) + { + throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + } + + try + { + return ImportPkcs8(pkcs8ZeroPublicKey); + } + catch (CryptographicException) + { + throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + } + } + finally { - throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + CryptographicOperations.ZeroMemory(decryptedSpan); + CryptoPool.Return(decrypted.Array!); } } - finally + catch (AsnContentException e) { - CryptographicOperations.ZeroMemory(decryptedSpan); - CryptoPool.Return(decrypted.Array!); + throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); } } } @@ -193,66 +217,78 @@ internal static unsafe Pkcs8Response ImportEncryptedPkcs8PrivateKey( ReadOnlySpan source, out int bytesRead) { - fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) - { - AsnReader reader = new AsnReader(manager.Memory, AsnEncodingRules.BER); - int len = reader.ReadEncodedValue().Length; - source = source.Slice(0, len); - - try - { - bytesRead = len; - return ImportPkcs8(source, password); - } - catch (CryptographicException) - { - } - - ArraySegment decrypted = KeyFormatHelper.DecryptPkcs8( - password, - manager.Memory.Slice(0, len), - out int innerRead); + AsnDecoder.ReadEncodedValue( + source, + AsnEncodingRules.BER, + out _, + out _, + out int len); - Span decryptedSpan = decrypted; + source = source.Slice(0, len); - try + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + { + using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) { - if (innerRead != len) + try { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + bytesRead = len; + return ImportPkcs8(source, password); } - - bytesRead = len; - return ImportPkcs8(decryptedSpan); - } - catch (CryptographicException e) - { - AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan); - - if (pkcs8ZeroPublicKey == null) + catch (CryptographicException) { - throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); } + ArraySegment decrypted = KeyFormatHelper.DecryptPkcs8( + password, + manager.Memory.Slice(0, len), + out int innerRead); + + Span decryptedSpan = decrypted; + try { + if (innerRead != len) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + bytesRead = len; - return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan()); + return ImportPkcs8(decryptedSpan); } - catch (CryptographicException) + catch (CryptographicException e) + { + AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan); + + if (pkcs8ZeroPublicKey == null) + { + throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + } + + try + { + bytesRead = len; + return ImportPkcs8(pkcs8ZeroPublicKey); + } + catch (CryptographicException) + { + throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + } + } + finally { - throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + CryptographicOperations.ZeroMemory(decryptedSpan); + CryptoPool.Return(decrypted.Array!, clearSize: 0); } } - finally - { - CryptographicOperations.ZeroMemory(decryptedSpan); - CryptoPool.Return(decrypted.Array!, clearSize: 0); - } } } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } private static AsnWriter RewriteEncryptedPkcs8PrivateKey( @@ -369,7 +405,7 @@ private static AsnWriter RewriteEncryptedPkcs8PrivateKey( PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(manager.Memory, AsnEncodingRules.BER); AlgorithmIdentifierAsn privateAlgorithm = privateKeyInfo.PrivateKeyAlgorithm; - if (privateAlgorithm.Algorithm.Value != Oids.EcPublicKey) + if (privateAlgorithm.Algorithm != Oids.EcPublicKey) { return null; } diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs index 7de0284da5cf40..60e5d30d1cd325 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. #nullable enable -using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography @@ -39,11 +37,29 @@ internal static void ReadDsaPrivateKey( ret.G = parms.G.ExportKeyParameter(ret.P.Length); - AsnReader reader = new AsnReader(xBytes, AsnEncodingRules.DER); - // Force a positive interpretation because Windows sometimes writes negative numbers. - BigInteger x = new BigInteger(reader.ReadIntegerBytes().Span, isUnsigned: true, isBigEndian: true); + BigInteger x; + + try + { + ReadOnlySpan xSpan = AsnDecoder.ReadIntegerBytes( + xBytes.Span, + AsnEncodingRules.DER, + out int consumed); + + if (consumed != xBytes.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + // Force a positive interpretation because Windows sometimes writes negative numbers. + x = new BigInteger(xSpan, isUnsigned: true, isBigEndian: true); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + ret.X = x.ExportKeyParameter(ret.Q.Length); - reader.ThrowIfNotEmpty(); // The public key is not contained within the format, calculate it. BigInteger y = BigInteger.ModPow(parms.G, x, parms.P); @@ -55,9 +71,24 @@ internal static void ReadDsaPublicKey( in AlgorithmIdentifierAsn algId, out DSAParameters ret) { - AsnReader reader = new AsnReader(yBytes, AsnEncodingRules.DER); - BigInteger y = reader.ReadInteger(); - reader.ThrowIfNotEmpty(); + BigInteger y; + + try + { + y = AsnDecoder.ReadInteger( + yBytes.Span, + AsnEncodingRules.DER, + out int consumed); + + if (consumed != yBytes.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } if (!algId.Parameters.HasValue) { @@ -186,17 +217,27 @@ private static void WriteAlgorithmId(AsnWriter writer, in DSAParameters dsaParam private static void WriteKeyComponent(AsnWriter writer, byte[]? component, bool bitString) { - using (AsnWriter inner = new AsnWriter(AsnEncodingRules.DER)) + if (bitString) { + AsnWriter inner = new AsnWriter(AsnEncodingRules.DER); inner.WriteKeyParameterInteger(component); - if (bitString) + byte[] tmp = CryptoPool.Rent(inner.GetEncodedLength()); + + if (!inner.TryEncode(tmp, out int written)) { - writer.WriteBitString(inner.EncodeAsSpan()); + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new CryptographicException(); } - else + + writer.WriteBitString(tmp.AsSpan(0, written)); + CryptoPool.Return(tmp, written); + } + else + { + using (writer.PushOctetString()) { - writer.WriteOctetString(inner.EncodeAsSpan()); + writer.WriteKeyParameterInteger(component); } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs index 0744b81a6e3672..857cca00d6fb65 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs @@ -5,10 +5,10 @@ #nullable enable using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography.Apple; -using System.Security.Cryptography.Asn1; using Internal.Cryptography; namespace System.Security.Cryptography @@ -203,6 +203,9 @@ public override void ImportEncryptedPkcs8PrivateKey( private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters) { + AsnWriter keyWriter; + bool hasPrivateKey; + if (parameters.X != null) { // DSAPrivateKey ::= SEQUENCE( @@ -214,25 +217,44 @@ private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters) // x INTEGER, // ) - using (AsnWriter privateKeyWriter = new AsnWriter(AsnEncodingRules.DER)) + keyWriter = new AsnWriter(AsnEncodingRules.DER); + + using (keyWriter.PushSequence()) { - privateKeyWriter.PushSequence(); - privateKeyWriter.WriteInteger(0); - privateKeyWriter.WriteKeyParameterInteger(parameters.P); - privateKeyWriter.WriteKeyParameterInteger(parameters.Q); - privateKeyWriter.WriteKeyParameterInteger(parameters.G); - privateKeyWriter.WriteKeyParameterInteger(parameters.Y); - privateKeyWriter.WriteKeyParameterInteger(parameters.X); - privateKeyWriter.PopSequence(); - return Interop.AppleCrypto.ImportEphemeralKey(privateKeyWriter.EncodeAsSpan(), true); + keyWriter.WriteInteger(0); + keyWriter.WriteKeyParameterInteger(parameters.P); + keyWriter.WriteKeyParameterInteger(parameters.Q); + keyWriter.WriteKeyParameterInteger(parameters.G); + keyWriter.WriteKeyParameterInteger(parameters.Y); + keyWriter.WriteKeyParameterInteger(parameters.X); } + + hasPrivateKey = true; } else { - using (AsnWriter writer = DSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(writer.EncodeAsSpan(), false); - } + keyWriter = DSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); + hasPrivateKey = false; + } + + byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); + + if (!keyWriter.TryEncode(rented, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + // Explicitly clear the inner buffer + keyWriter.Reset(); + + try + { + return Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey); + } + finally + { + CryptoPool.Return(rented, written); } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs index 8452c502f70626..fe9a95677689d2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs @@ -7,6 +7,7 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; @@ -78,18 +79,30 @@ internal static void ReadEncryptedPkcs8( internal static unsafe ECParameters FromECPrivateKey(ReadOnlySpan key, out int bytesRead) { - fixed (byte* ptr = &MemoryMarshal.GetReference(key)) + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, key.Length)) + AsnDecoder.ReadEncodedValue( + key, + AsnEncodingRules.BER, + out _, + out _, + out int firstValueLength); + + fixed (byte* ptr = &MemoryMarshal.GetReference(key)) { - AsnReader reader = new AsnReader(manager.Memory, AsnEncodingRules.BER); - AlgorithmIdentifierAsn algId = default; - ReadOnlyMemory firstValue = reader.PeekEncodedValue(); - FromECPrivateKey(firstValue, algId, out ECParameters ret); - bytesRead = firstValue.Length; - return ret; + using (MemoryManager manager = new PointerMemoryManager(ptr, firstValueLength)) + { + AlgorithmIdentifierAsn algId = default; + FromECPrivateKey(manager.Memory, algId, out ECParameters ret); + bytesRead = firstValueLength; + return ret; + } } } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void FromECPrivateKey( @@ -234,14 +247,12 @@ private static void ValidateParameters(ECDomainParameters? keyParameters, in Alg // X.509 SubjectPublicKeyInfo specifies DER encoding. // RFC 5915 specifies DER encoding for EC Private Keys. // So we can compare as DER. - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - keyParameters.Value.Encode(writer); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + keyParameters.Value.Encode(writer); - if (!writer.ValueEquals(algIdParameters)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } + if (!writer.EncodedValueEquals(algIdParameters)) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } } } @@ -258,9 +269,9 @@ private static ECCurve GetCurve(ECDomainParameters domainParameters) throw new CryptographicException(SR.Cryptography_ECC_NamedCurvesOnly); } - Oid curveOid = domainParameters.Named; + Oid curveOid; - switch (curveOid.Value) + switch (domainParameters.Named) { case Oids.secp256r1: curveOid = new Oid(Oids.secp256r1, nameof(ECCurve.NamedCurves.nistP256)); @@ -271,12 +282,27 @@ private static ECCurve GetCurve(ECDomainParameters domainParameters) case Oids.secp521r1: curveOid = new Oid(Oids.secp521r1, nameof(ECCurve.NamedCurves.nistP521)); break; + default: + curveOid = new Oid(domainParameters.Named); + break; } return ECCurve.CreateFromOid(curveOid); } private static ECCurve GetSpecifiedECCurve(SpecifiedECDomain specifiedParameters) + { + try + { + return GetSpecifiedECCurveCore(specifiedParameters); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static ECCurve GetSpecifiedECCurveCore(SpecifiedECDomain specifiedParameters) { // sec1-v2 C.3: // @@ -302,6 +328,7 @@ private static ECCurve GetSpecifiedECCurve(SpecifiedECDomain specifiedParameters prime = true; AsnReader primeReader = new AsnReader(specifiedParameters.FieldID.Parameters, AsnEncodingRules.BER); ReadOnlySpan primeValue = primeReader.ReadIntegerBytes().Span; + primeReader.ThrowIfNotEmpty(); if (primeValue[0] == 0) { @@ -337,7 +364,7 @@ private static ECCurve GetSpecifiedECCurve(SpecifiedECDomain specifiedParameters int k2 = -1; int k3 = -1; - switch (innerReader.ReadObjectIdentifierAsString()) + switch (innerReader.ReadObjectIdentifier()) { case Oids.EcChar2TrinomialBasis: // Trinomial ::= INTEGER @@ -486,12 +513,11 @@ internal static AsnWriter WritePkcs8PrivateKey(ECParameters ecParameters, Attrib } // Don't need the domain parameters because they're contained in the algId. - using (AsnWriter ecPrivateKey = WriteEcPrivateKey(ecParameters, includeDomainParameters: false)) - using (AsnWriter algorithmIdentifier = WriteAlgorithmIdentifier(ecParameters)) - using (AsnWriter? attributeWriter = WritePrivateKeyInfoAttributes(attributes)) - { - return KeyFormatHelper.WritePkcs8(algorithmIdentifier, ecPrivateKey, attributeWriter); - } + AsnWriter ecPrivateKey = WriteEcPrivateKey(ecParameters, includeDomainParameters: false); + AsnWriter algorithmIdentifier = WriteAlgorithmIdentifier(ecParameters); + AsnWriter? attributeWriter = WritePrivateKeyInfoAttributes(attributes); + + return KeyFormatHelper.WritePkcs8(algorithmIdentifier, ecPrivateKey, attributeWriter); } [return: NotNullIfNotNull("attributes")] @@ -764,16 +790,12 @@ private static void WriteUncompressedBasePoint(in ECParameters ecParameters, Asn private static void WriteUncompressedPublicKey(in ECParameters ecParameters, AsnWriter writer) { int publicKeyLength = ecParameters.Q.X!.Length * 2 + 1; + byte[] publicKeyBytes = CryptoPool.Rent(publicKeyLength); + publicKeyBytes[0] = 0x04; + ecParameters.Q.X.AsSpan().CopyTo(publicKeyBytes.AsSpan(1)); + ecParameters.Q.Y.AsSpan().CopyTo(publicKeyBytes.AsSpan(1 + ecParameters.Q.X!.Length)); - writer.WriteBitString( - publicKeyLength, - ecParameters.Q, - (publicKeyBytes, point) => - { - publicKeyBytes[0] = 0x04; - point.X.AsSpan().CopyTo(publicKeyBytes.Slice(1)); - point.Y.AsSpan().CopyTo(publicKeyBytes.Slice(1 + point.X!.Length)); - }); + writer.WriteBitString(publicKeyBytes.AsSpan(0, publicKeyLength)); } internal static AsnWriter WriteECPrivateKey(in ECParameters ecParameters) @@ -783,54 +805,42 @@ internal static AsnWriter WriteECPrivateKey(in ECParameters ecParameters) private static AsnWriter WriteEcPrivateKey(in ECParameters ecParameters, bool includeDomainParameters) { - bool returning = false; AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); - try - { - // ECPrivateKey - writer.PushSequence(); - - // version 1 - writer.WriteInteger(1); + // ECPrivateKey + writer.PushSequence(); - // privateKey - writer.WriteOctetString(ecParameters.D); + // version 1 + writer.WriteInteger(1); - // domainParameters - if (includeDomainParameters) - { - Asn1Tag explicit0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); - writer.PushSequence(explicit0); + // privateKey + writer.WriteOctetString(ecParameters.D); - WriteEcParameters(ecParameters, writer); + // domainParameters + if (includeDomainParameters) + { + Asn1Tag explicit0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); + writer.PushSequence(explicit0); - writer.PopSequence(explicit0); - } + WriteEcParameters(ecParameters, writer); - // publicKey - if (ecParameters.Q.X != null) - { - Debug.Assert(ecParameters.Q.Y != null); - Asn1Tag explicit1 = new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true); - writer.PushSequence(explicit1); + writer.PopSequence(explicit0); + } - WriteUncompressedPublicKey(ecParameters, writer); + // publicKey + if (ecParameters.Q.X != null) + { + Debug.Assert(ecParameters.Q.Y != null); + Asn1Tag explicit1 = new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true); + writer.PushSequence(explicit1); - writer.PopSequence(explicit1); - } + WriteUncompressedPublicKey(ecParameters, writer); - writer.PopSequence(); - returning = true; - return writer; - } - finally - { - if (!returning) - { - writer.Dispose(); - } + writer.PopSequence(explicit1); } + + writer.PopSequence(); + return writer; } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs index 09c8502f212418..9c8987820a023c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs @@ -5,9 +5,9 @@ #nullable enable using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography.Apple; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography { @@ -234,19 +234,38 @@ private static int GetKeySize(SecKeyPair newKeys) private static SafeSecKeyRefHandle ImportKey(ECParameters parameters) { + AsnWriter keyWriter; + bool hasPrivateKey; + if (parameters.D != null) { - using (AsnWriter privateKey = EccKeyFormatHelper.WriteECPrivateKey(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(privateKey.EncodeAsSpan(), true); - } + keyWriter = EccKeyFormatHelper.WriteECPrivateKey(parameters); + hasPrivateKey = true; } else { - using (AsnWriter publicKey = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(publicKey.EncodeAsSpan(), false); - } + keyWriter = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); + hasPrivateKey = false; + } + + byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); + + if (!keyWriter.TryEncode(rented, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + // Explicitly clear the inner buffer + keyWriter.Reset(); + + try + { + return Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey); + } + finally + { + CryptoPool.Return(rented, written); } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs index b5aab14da28327..1df65b605729cd 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Formats.Asn1; using System.Numerics; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography { diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs index 49e058f91a50e8..69d930081bb897 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs @@ -5,8 +5,7 @@ #nullable enable using System.Buffers; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; @@ -37,12 +36,22 @@ internal static ReadOnlyMemory ReadSubjectPublicKeyInfo( ReadOnlyMemory source, out int bytesRead) { - // X.509 SubjectPublicKeyInfo is described as DER. - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER); - int read = reader.PeekEncodedValue().Length; - SubjectPublicKeyInfoAsn.Decode(ref reader, source, out SubjectPublicKeyInfoAsn spki); + SubjectPublicKeyInfoAsn spki; + int read; - if (Array.IndexOf(validOids, spki.Algorithm.Algorithm.Value) < 0) + try + { + // X.509 SubjectPublicKeyInfo is described as DER. + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER); + read = reader.PeekEncodedValue().Length; + SubjectPublicKeyInfoAsn.Decode(ref reader, source, out spki); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + + if (Array.IndexOf(validOids, spki.Algorithm.Algorithm) < 0) { throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); } @@ -58,12 +67,22 @@ private static void ReadSubjectPublicKeyInfo( out int bytesRead, out TRet ret) { - // X.509 SubjectPublicKeyInfo is described as DER. - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER); - int read = reader.PeekEncodedValue().Length; - SubjectPublicKeyInfoAsn.Decode(ref reader, source, out SubjectPublicKeyInfoAsn spki); + SubjectPublicKeyInfoAsn spki; + int read; + + try + { + // X.509 SubjectPublicKeyInfo is described as DER. + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.DER); + read = reader.PeekEncodedValue().Length; + SubjectPublicKeyInfoAsn.Decode(ref reader, source, out spki); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } - if (Array.IndexOf(validOids, spki.Algorithm.Algorithm.Value) < 0) + if (Array.IndexOf(validOids, spki.Algorithm.Algorithm) < 0) { throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); } @@ -93,17 +112,24 @@ internal static ReadOnlyMemory ReadPkcs8( ReadOnlyMemory source, out int bytesRead) { - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); - int read = reader.PeekEncodedValue().Length; - PrivateKeyInfoAsn.Decode(ref reader, source, out PrivateKeyInfoAsn privateKeyInfo); + try + { + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); + int read = reader.PeekEncodedValue().Length; + PrivateKeyInfoAsn.Decode(ref reader, source, out PrivateKeyInfoAsn privateKeyInfo); - if (Array.IndexOf(validOids, privateKeyInfo.PrivateKeyAlgorithm.Algorithm.Value) < 0) + if (Array.IndexOf(validOids, privateKeyInfo.PrivateKeyAlgorithm.Algorithm) < 0) + { + throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); + } + + bytesRead = read; + return privateKeyInfo.PrivateKey; + } + catch (AsnContentException e) { - throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - bytesRead = read; - return privateKeyInfo.PrivateKey; } private static void ReadPkcs8( @@ -113,18 +139,25 @@ private static void ReadPkcs8( out int bytesRead, out TRet ret) { - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); - int read = reader.PeekEncodedValue().Length; - PrivateKeyInfoAsn.Decode(ref reader, source, out PrivateKeyInfoAsn privateKeyInfo); + try + { + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); + int read = reader.PeekEncodedValue().Length; + PrivateKeyInfoAsn.Decode(ref reader, source, out PrivateKeyInfoAsn privateKeyInfo); + + if (Array.IndexOf(validOids, privateKeyInfo.PrivateKeyAlgorithm.Algorithm) < 0) + { + throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); + } - if (Array.IndexOf(validOids, privateKeyInfo.PrivateKeyAlgorithm.Algorithm.Value) < 0) + // Fails if there are unconsumed bytes. + keyReader(privateKeyInfo.PrivateKey, privateKeyInfo.PrivateKeyAlgorithm, out ret); + bytesRead = read; + } + catch (AsnContentException e) { - throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - // Fails if there are unconsumed bytes. - keyReader(privateKeyInfo.PrivateKey, privateKeyInfo.PrivateKeyAlgorithm, out ret); - bytesRead = read; } internal static unsafe void ReadEncryptedPkcs8( @@ -156,7 +189,13 @@ internal static unsafe void ReadEncryptedPkcs8( { using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) { - ReadEncryptedPkcs8(validOids, manager.Memory, passwordBytes, keyReader, out bytesRead, out ret); + ReadEncryptedPkcs8( + validOids, + manager.Memory, + passwordBytes, + keyReader, + out bytesRead, + out ret); } } } @@ -206,9 +245,19 @@ private static void ReadEncryptedPkcs8( out int bytesRead, out TRet ret) { - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); - int read = reader.PeekEncodedValue().Length; - EncryptedPrivateKeyInfoAsn.Decode(ref reader, source, out EncryptedPrivateKeyInfoAsn epki); + int read; + EncryptedPrivateKeyInfoAsn epki; + + try + { + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); + read = reader.PeekEncodedValue().Length; + EncryptedPrivateKeyInfoAsn.Decode(ref reader, source, out epki); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } // No supported encryption algorithms produce more bytes of decryption output than there // were of decryption input. @@ -258,12 +307,11 @@ internal static AsnWriter WritePkcs8( AsnWriter? attributesWriter = null) { // Ensure both algorithm identifier and key writers are balanced. - ReadOnlySpan algorithmIdentifier = algorithmIdentifierWriter.EncodeAsSpan(); - ReadOnlySpan privateKey = privateKeyWriter.EncodeAsSpan(); + int algorithmIdentifierLength = algorithmIdentifierWriter.GetEncodedLength(); + int privateKeyLength = privateKeyWriter.GetEncodedLength(); - Debug.Assert(algorithmIdentifier.Length > 0, "algorithmIdentifier was empty"); - Debug.Assert(algorithmIdentifier[0] == 0x30, "algorithmIdentifier is not a constructed sequence"); - Debug.Assert(privateKey.Length > 0, "privateKey was empty"); + Debug.Assert(algorithmIdentifierLength > 0, "algorithmIdentifier was empty"); + Debug.Assert(privateKeyLength > 0, "privateKey was empty"); // https://tools.ietf.org/html/rfc5208#section-5 // @@ -285,18 +333,17 @@ internal static AsnWriter WritePkcs8( writer.WriteInteger(0); // PKI.Algorithm (AlgorithmIdentifier) - writer.WriteEncodedValue(algorithmIdentifier); + algorithmIdentifierWriter.CopyTo(writer); // PKI.privateKey - writer.WriteOctetString(privateKey); - - // PKI.Attributes - if (attributesWriter != null) + using (writer.PushOctetString()) { - ReadOnlySpan attributes = attributesWriter.EncodeAsSpan(); - writer.WriteEncodedValue(attributes); + privateKeyWriter.CopyTo(writer); } + // PKI.Attributes + attributesWriter?.CopyTo(writer); + writer.PopSequence(); return writer; } @@ -331,8 +378,6 @@ private static unsafe AsnWriter WriteEncryptedPkcs8( AsnWriter pkcs8Writer, PbeParameters pbeParameters) { - ReadOnlySpan pkcs8Span = pkcs8Writer.EncodeAsSpan(); - PasswordBasedEncryption.InitiateEncryption( pbeParameters, out SymmetricAlgorithm cipher, @@ -352,7 +397,7 @@ private static unsafe AsnWriter WriteEncryptedPkcs8( // We need at least one block size beyond the input data size. encryptedRent = CryptoPool.Rent( - checked(pkcs8Span.Length + (cipher.BlockSize / 8))); + checked(pkcs8Writer.GetEncodedLength() + (cipher.BlockSize / 8))); RandomNumberGenerator.Fill(salt); @@ -361,7 +406,7 @@ private static unsafe AsnWriter WriteEncryptedPkcs8( passwordBytes, cipher, isPkcs12, - pkcs8Span, + pkcs8Writer, pbeParameters, salt, encryptedRent, @@ -388,17 +433,13 @@ private static unsafe AsnWriter WriteEncryptedPkcs8( writer.WriteOctetString(encryptedSpan); writer.PopSequence(); - AsnWriter ret = writer; - // Don't dispose writer on the way out. - writer = null; - return ret; + return writer; } finally { CryptographicOperations.ZeroMemory(encryptedSpan); CryptoPool.Return(encryptedRent!, clearSize: 0); - writer?.Dispose(); cipher.Dispose(); } } @@ -433,9 +474,19 @@ private static ArraySegment DecryptPkcs8( ReadOnlyMemory source, out int bytesRead) { - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); - int localRead = reader.PeekEncodedValue().Length; - EncryptedPrivateKeyInfoAsn.Decode(ref reader, source, out EncryptedPrivateKeyInfoAsn epki); + int localRead; + EncryptedPrivateKeyInfoAsn epki; + + try + { + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); + localRead = reader.PeekEncodedValue().Length; + EncryptedPrivateKeyInfoAsn.Decode(ref reader, source, out epki); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } // No supported encryption algorithms produce more bytes of decryption output than there // were of decryption input. @@ -479,15 +530,13 @@ internal static AsnWriter ReencryptPkcs8( throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - using (AsnWriter pkcs8Writer = new AsnWriter(AsnEncodingRules.BER)) - { - pkcs8Writer.WriteEncodedValue(decrypted); + AsnWriter pkcs8Writer = new AsnWriter(AsnEncodingRules.BER); + pkcs8Writer.WriteEncodedValueForCrypto(decrypted); - return WriteEncryptedPkcs8( - newPassword, - pkcs8Writer, - pbeParameters); - } + return WriteEncryptedPkcs8( + newPassword, + pkcs8Writer, + pbeParameters); } catch (CryptographicException e) { @@ -518,15 +567,13 @@ internal static AsnWriter ReencryptPkcs8( throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - using (AsnWriter pkcs8Writer = new AsnWriter(AsnEncodingRules.BER)) - { - pkcs8Writer.WriteEncodedValue(decrypted); + AsnWriter pkcs8Writer = new AsnWriter(AsnEncodingRules.BER); + pkcs8Writer.WriteEncodedValueForCrypto(decrypted); - return WriteEncryptedPkcs8( - newPasswordBytes, - pkcs8Writer, - pbeParameters); - } + return WriteEncryptedPkcs8( + newPasswordBytes, + pkcs8Writer, + pbeParameters); } catch (CryptographicException e) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs b/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs index 5780e28eb74db7..f250a06cacc377 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs @@ -5,6 +5,7 @@ #nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs; @@ -82,7 +83,7 @@ internal static unsafe int Decrypt( bool pkcs12 = false; - switch (algorithmIdentifier.Algorithm.Value) + switch (algorithmIdentifier.Algorithm) { case Oids.PbeWithMD5AndDESCBC: digestAlgorithmName = HashAlgorithmName.MD5; @@ -134,7 +135,7 @@ internal static unsafe int Decrypt( throw new CryptographicException( SR.Format( SR.Cryptography_UnknownAlgorithmIdentifier, - algorithmIdentifier.Algorithm.Value)); + algorithmIdentifier.Algorithm)); } Debug.Assert(digestAlgorithmName.Name != null); @@ -146,7 +147,7 @@ internal static unsafe int Decrypt( { if (password.IsEmpty && passwordBytes.Length > 0) { - throw AlgorithmKdfRequiresChars(algorithmIdentifier.Algorithm.Value); + throw AlgorithmKdfRequiresChars(algorithmIdentifier.Algorithm); } return Pkcs12PbeDecrypt( @@ -294,7 +295,7 @@ internal static unsafe int Encrypt( ReadOnlySpan passwordBytes, SymmetricAlgorithm cipher, bool isPkcs12, - ReadOnlySpan source, + AsnWriter source, PbeParameters pbeParameters, ReadOnlySpan salt, byte[] destination, @@ -304,7 +305,8 @@ internal static unsafe int Encrypt( byte[] derivedKey; byte[] iv = cipher.IV; - byte[] sourceRent = CryptoPool.Rent(source.Length); + int sourceLength = source.GetEncodedLength(); + byte[] sourceRent = CryptoPool.Rent(sourceLength); int keySizeBytes = cipher.KeySize / 8; int iterationCount = pbeParameters.IterationCount; HashAlgorithmName prf = pbeParameters.HashAlgorithm; @@ -392,12 +394,16 @@ internal static unsafe int Encrypt( Debug.Assert(encryptor.CanTransformMultipleBlocks); int blockSizeBytes = (cipher.BlockSize / 8); - int remaining = source.Length % blockSizeBytes; - int fullBlocksLength = source.Length - remaining; + int remaining = sourceLength % blockSizeBytes; + int fullBlocksLength = sourceLength - remaining; try { - source.CopyTo(sourceRent); + if (!source.TryEncode(sourceRent, out _)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new CryptographicException(); + } int written = 0; @@ -421,7 +427,7 @@ internal static unsafe int Encrypt( } finally { - CryptoPool.Return(sourceRent, source.Length); + CryptoPool.Return(sourceRent, sourceLength); } } } @@ -501,16 +507,16 @@ private static unsafe int Pbes2Decrypt( PBES2Params pbes2Params = PBES2Params.Decode(algorithmParameters.Value, AsnEncodingRules.BER); - if (pbes2Params.KeyDerivationFunc.Algorithm.Value != Oids.Pbkdf2) + if (pbes2Params.KeyDerivationFunc.Algorithm != Oids.Pbkdf2) { throw new CryptographicException( SR.Format( SR.Cryptography_UnknownAlgorithmIdentifier, - pbes2Params.EncryptionScheme.Algorithm.Value)); + pbes2Params.EncryptionScheme.Algorithm)); } Rfc2898DeriveBytes pbkdf2 = - OpenPbkdf2(password, pbes2Params.KeyDerivationFunc.Parameters, out byte? requestedKeyLength); + OpenPbkdf2(password, pbes2Params.KeyDerivationFunc.Parameters, out int? requestedKeyLength); using (pbkdf2) { @@ -545,10 +551,10 @@ private static unsafe int Pbes2Decrypt( [SuppressMessage("Microsoft.Security", "CA5351", Justification = "DES used when specified by the input data")] private static SymmetricAlgorithm OpenCipher( AlgorithmIdentifierAsn encryptionScheme, - byte? requestedKeyLength, + int? requestedKeyLength, ref Span iv) { - string? algId = encryptionScheme.Algorithm.Value; + string? algId = encryptionScheme.Algorithm; if (algId == Oids.Aes128Cbc || algId == Oids.Aes192Cbc || @@ -668,21 +674,34 @@ private static void ReadIvParameter( throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - AsnReader reader = new AsnReader(encryptionSchemeParameters.Value, AsnEncodingRules.BER); + try + { + ReadOnlySpan source = encryptionSchemeParameters.Value.Span; + + bool gotIv = AsnDecoder.TryReadOctetString( + source, + iv, + AsnEncodingRules.BER, + out int consumed, + out int bytesWritten); + + if (!gotIv || bytesWritten != length || consumed != source.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } - if (!reader.TryCopyOctetStringBytes(iv, out int bytesWritten) || bytesWritten != length) + iv = iv.Slice(0, bytesWritten); + } + catch (AsnContentException e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - reader.ThrowIfNotEmpty(); - iv = iv.Slice(0, bytesWritten); } private static unsafe Rfc2898DeriveBytes OpenPbkdf2( ReadOnlySpan password, ReadOnlyMemory? parameters, - out byte? requestedKeyLength) + out int? requestedKeyLength) { if (!parameters.HasValue) { @@ -707,7 +726,7 @@ private static unsafe Rfc2898DeriveBytes OpenPbkdf2( throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - HashAlgorithmName prf = pbkdf2Params.Prf.Algorithm.Value switch + HashAlgorithmName prf = pbkdf2Params.Prf.Algorithm switch { Oids.HmacWithSha1 => HashAlgorithmName.SHA1, Oids.HmacWithSha256 => HashAlgorithmName.SHA256, @@ -990,7 +1009,7 @@ internal static void WritePbeAlgorithmIdentifier( if (isPkcs12) { - writer.WriteObjectIdentifier(encryptionAlgorithmOid); + writer.WriteObjectIdentifierForCrypto(encryptionAlgorithmOid); // pkcs-12PbeParams { @@ -1002,7 +1021,7 @@ internal static void WritePbeAlgorithmIdentifier( } else { - writer.WriteObjectIdentifier(Oids.PasswordBasedEncryptionScheme2); + writer.WriteObjectIdentifierForCrypto(Oids.PasswordBasedEncryptionScheme2); // PBES2-params { @@ -1011,7 +1030,7 @@ internal static void WritePbeAlgorithmIdentifier( // keyDerivationFunc { writer.PushSequence(); - writer.WriteObjectIdentifier(Oids.Pbkdf2); + writer.WriteObjectIdentifierForCrypto(Oids.Pbkdf2); // PBKDF2-params { @@ -1024,7 +1043,7 @@ internal static void WritePbeAlgorithmIdentifier( if (hmacOid != Oids.HmacWithSha1) { writer.PushSequence(); - writer.WriteObjectIdentifier(hmacOid); + writer.WriteObjectIdentifierForCrypto(hmacOid); writer.WriteNull(); writer.PopSequence(); } @@ -1038,7 +1057,7 @@ internal static void WritePbeAlgorithmIdentifier( // encryptionScheme { writer.PushSequence(); - writer.WriteObjectIdentifier(encryptionAlgorithmOid); + writer.WriteObjectIdentifierForCrypto(encryptionAlgorithmOid); writer.WriteOctetString(iv); writer.PopSequence(); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs index 926cf94ee6b874..0319e2c3b3c696 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography @@ -148,61 +145,70 @@ internal static void ReadEncryptedPkcs8( out key); } - internal static AsnWriter WriteSubjectPublicKeyInfo(in ReadOnlySpan pkcs1PublicKey) + internal static AsnWriter WriteSubjectPublicKeyInfo(ReadOnlySpan pkcs1PublicKey) { AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); - try - { - writer.PushSequence(); - WriteAlgorithmIdentifier(writer); - writer.WriteBitString(pkcs1PublicKey); - writer.PopSequence(); - } - catch - { - writer.Dispose(); - throw; - } + writer.PushSequence(); + WriteAlgorithmIdentifier(writer); + writer.WriteBitString(pkcs1PublicKey); + writer.PopSequence(); return writer; } internal static AsnWriter WriteSubjectPublicKeyInfo(in RSAParameters rsaParameters) { - using (AsnWriter pkcs1PublicKey = WritePkcs1PublicKey(rsaParameters)) + AsnWriter pkcs1PublicKey = WritePkcs1PublicKey(rsaParameters); + byte[] rented = CryptoPool.Rent(pkcs1PublicKey.GetEncodedLength()); + + if (!pkcs1PublicKey.TryEncode(rented, out int written)) { - return WriteSubjectPublicKeyInfo(pkcs1PublicKey.EncodeAsSpan()); + Debug.Fail("TryEncode failed with a presized buffer"); + throw new CryptographicException(); } + + AsnWriter ret = WriteSubjectPublicKeyInfo(rented.AsSpan(0, written)); + + // Only public key data data + CryptoPool.Return(rented, clearSize: 0); + return ret; } - internal static AsnWriter WritePkcs8PrivateKey(in ReadOnlySpan pkcs1PrivateKey) + internal static AsnWriter WritePkcs8PrivateKey( + ReadOnlySpan pkcs1PrivateKey, + AsnWriter? copyFrom=null) { + Debug.Assert(copyFrom == null || pkcs1PrivateKey.IsEmpty); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); - try + using (writer.PushSequence()) { - writer.PushSequence(); // Version 0 format (no attributes) writer.WriteInteger(0); WriteAlgorithmIdentifier(writer); - writer.WriteOctetString(pkcs1PrivateKey); - writer.PopSequence(); - return writer; - } - catch - { - writer.Dispose(); - throw; + + if (copyFrom != null) + { + using (writer.PushOctetString()) + { + copyFrom.CopyTo(writer); + } + } + else + { + writer.WriteOctetString(pkcs1PrivateKey); + } } + + return writer; } internal static AsnWriter WritePkcs8PrivateKey(in RSAParameters rsaParameters) { - using (AsnWriter pkcs1PrivateKey = WritePkcs1PrivateKey(rsaParameters)) - { - return WritePkcs8PrivateKey(pkcs1PrivateKey.EncodeAsSpan()); - } + AsnWriter pkcs1PrivateKey = WritePkcs1PrivateKey(rsaParameters); + return WritePkcs8PrivateKey(ReadOnlySpan.Empty, pkcs1PrivateKey); } private static void WriteAlgorithmIdentifier(AsnWriter writer) diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs index fbe13834f94fd3..4d062eda485b48 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs @@ -5,9 +5,8 @@ #nullable enable using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; -using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Microsoft.Win32.SafeHandles; using Internal.Cryptography; @@ -442,27 +441,34 @@ public override unsafe void ImportRSAPublicKey(ReadOnlySpan source, out in { ThrowIfDisposed(); - fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + int read; + + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) - { - AsnReader reader = new AsnReader(manager.Memory, AsnEncodingRules.BER); - ReadOnlyMemory firstElement = reader.PeekEncodedValue(); + AsnDecoder.ReadEncodedValue( + source, + AsnEncodingRules.BER, + out _, + out _, + out read); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } - SafeRsaHandle key = Interop.Crypto.DecodeRsaPublicKey(firstElement.Span); + SafeRsaHandle key = Interop.Crypto.DecodeRsaPublicKey(source.Slice(0, read)); - Interop.Crypto.CheckValidOpenSslHandle(key); + Interop.Crypto.CheckValidOpenSslHandle(key); - FreeKey(); - _key = new Lazy(key); + FreeKey(); + _key = new Lazy(key); - // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere - // with the already loaded key. - ForceSetKeySize(BitsPerByte * Interop.Crypto.RsaSize(key)); + // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere + // with the already loaded key. + ForceSetKeySize(BitsPerByte * Interop.Crypto.RsaSize(key)); - bytesRead = firstElement.Length; - } - } + bytesRead = read; } public override void ImportEncryptedPkcs8PrivateKey( diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs index a21db3de01c22a..eb26d14bdc36b2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs @@ -5,6 +5,7 @@ #nullable enable using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Numerics; using System.Runtime.InteropServices; @@ -229,18 +230,16 @@ public override unsafe void ImportRSAPublicKey(ReadOnlySpan source, out in { Algorithm = new AlgorithmIdentifierAsn { - Algorithm = new Oid(Oids.Rsa), + Algorithm = Oids.Rsa, Parameters = AlgorithmIdentifierAsn.ExplicitDerNull, }, SubjectPublicKey = firstElement, }; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - spki.Encode(writer); - ImportSubjectPublicKeyInfo(writer.EncodeAsSpan(), out _); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + spki.Encode(writer); + ImportSubjectPublicKeyInfo(writer.Encode(), out _); bytesRead = firstElement.Length; } } @@ -819,19 +818,38 @@ private void SetKey(SecKeyPair newKeyPair) private static SafeSecKeyRefHandle ImportKey(RSAParameters parameters) { + AsnWriter keyWriter; + bool hasPrivateKey; + if (parameters.D != null) { - using (AsnWriter pkcs1PrivateKey = RSAKeyFormatHelper.WritePkcs1PrivateKey(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(pkcs1PrivateKey.EncodeAsSpan(), true); - } + keyWriter = RSAKeyFormatHelper.WritePkcs1PrivateKey(parameters); + hasPrivateKey = true; } else { - using (AsnWriter pkcs1PublicKey = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(pkcs1PublicKey.EncodeAsSpan(), false); - } + keyWriter = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); + hasPrivateKey = false; + } + + byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); + + if (!keyWriter.TryEncode(rented, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + // Explicitly clear the inner buffer + keyWriter.Reset(); + + try + { + return Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey); + } + finally + { + CryptoPool.Return(rented, written); } } diff --git a/src/libraries/System.Formats.Asn1/Directory.Build.props b/src/libraries/System.Formats.Asn1/Directory.Build.props new file mode 100644 index 00000000000000..749d7fc1c6b56d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/Directory.Build.props @@ -0,0 +1,7 @@ + + + + Open + true + + diff --git a/src/libraries/System.Formats.Asn1/System.Formats.Asn1.sln b/src/libraries/System.Formats.Asn1/System.Formats.Asn1.sln new file mode 100644 index 00000000000000..dfd2a9e9ae1be2 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/System.Formats.Asn1.sln @@ -0,0 +1,53 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27213.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Asn1.Tests", "tests\System.Formats.Asn1.Tests.csproj", "{8E5CC092-64B6-40BD-B422-3072EBDA79C0}" + ProjectSection(ProjectDependencies) = postProject + {E1F62374-FC25-4C34-8399-887A0F785F69} = {E1F62374-FC25-4C34-8399-887A0F785F69} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Asn1", "src\System.Formats.Asn1.csproj", "{E1F62374-FC25-4C34-8399-887A0F785F69}" + ProjectSection(ProjectDependencies) = postProject + {6CE58FC1-0579-4E54-A815-774942CA1E6F} = {6CE58FC1-0579-4E54-A815-774942CA1E6F} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Asn1", "ref\System.Formats.Asn1.csproj", "{6CE58FC1-0579-4E54-A815-774942CA1E6F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2E2E9476-9161-47DF-A34A-FBDB066D0EE7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8C13471F-2F38-4AFE-802C-78074B8670E5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{7F15ECA1-2FD6-4EF7-A554-31197519A1F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8E5CC092-64B6-40BD-B422-3072EBDA79C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E5CC092-64B6-40BD-B422-3072EBDA79C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E5CC092-64B6-40BD-B422-3072EBDA79C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E5CC092-64B6-40BD-B422-3072EBDA79C0}.Release|Any CPU.Build.0 = Release|Any CPU + {E1F62374-FC25-4C34-8399-887A0F785F69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F62374-FC25-4C34-8399-887A0F785F69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F62374-FC25-4C34-8399-887A0F785F69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1F62374-FC25-4C34-8399-887A0F785F69}.Release|Any CPU.Build.0 = Release|Any CPU + {6CE58FC1-0579-4E54-A815-774942CA1E6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6CE58FC1-0579-4E54-A815-774942CA1E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6CE58FC1-0579-4E54-A815-774942CA1E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6CE58FC1-0579-4E54-A815-774942CA1E6F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8E5CC092-64B6-40BD-B422-3072EBDA79C0} = {2E2E9476-9161-47DF-A34A-FBDB066D0EE7} + {E1F62374-FC25-4C34-8399-887A0F785F69} = {8C13471F-2F38-4AFE-802C-78074B8670E5} + {6CE58FC1-0579-4E54-A815-774942CA1E6F} = {7F15ECA1-2FD6-4EF7-A554-31197519A1F2} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6EB2470F-A46B-4E53-953C-470334FA8541} + EndGlobalSection +EndGlobal diff --git a/src/libraries/System.Formats.Asn1/pkg/System.Formats.Asn1.pkgproj b/src/libraries/System.Formats.Asn1/pkg/System.Formats.Asn1.pkgproj new file mode 100644 index 00000000000000..5dbc975172585f --- /dev/null +++ b/src/libraries/System.Formats.Asn1/pkg/System.Formats.Asn1.pkgproj @@ -0,0 +1,9 @@ + + + + + net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks) + + + + diff --git a/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs new file mode 100644 index 00000000000000..921b832ac44220 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs @@ -0,0 +1,243 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Formats.Asn1 +{ + public readonly partial struct Asn1Tag : System.IEquatable + { + private readonly int _dummyPrimitive; + public static readonly System.Formats.Asn1.Asn1Tag Boolean; + public static readonly System.Formats.Asn1.Asn1Tag ConstructedBitString; + public static readonly System.Formats.Asn1.Asn1Tag ConstructedOctetString; + public static readonly System.Formats.Asn1.Asn1Tag Enumerated; + public static readonly System.Formats.Asn1.Asn1Tag GeneralizedTime; + public static readonly System.Formats.Asn1.Asn1Tag Integer; + public static readonly System.Formats.Asn1.Asn1Tag Null; + public static readonly System.Formats.Asn1.Asn1Tag ObjectIdentifier; + public static readonly System.Formats.Asn1.Asn1Tag PrimitiveBitString; + public static readonly System.Formats.Asn1.Asn1Tag PrimitiveOctetString; + public static readonly System.Formats.Asn1.Asn1Tag Sequence; + public static readonly System.Formats.Asn1.Asn1Tag SetOf; + public static readonly System.Formats.Asn1.Asn1Tag UtcTime; + public Asn1Tag(System.Formats.Asn1.TagClass tagClass, int tagValue, bool isConstructed = false) { throw null; } + public Asn1Tag(System.Formats.Asn1.UniversalTagNumber universalTagNumber, bool isConstructed = false) { throw null; } + public bool IsConstructed { get { throw null; } } + public System.Formats.Asn1.TagClass TagClass { get { throw null; } } + public int TagValue { get { throw null; } } + public System.Formats.Asn1.Asn1Tag AsConstructed() { throw null; } + public System.Formats.Asn1.Asn1Tag AsPrimitive() { throw null; } + public int CalculateEncodedSize() { throw null; } + public static System.Formats.Asn1.Asn1Tag Decode(System.ReadOnlySpan source, out int bytesConsumed) { throw null; } + public int Encode(System.Span destination) { throw null; } + public bool Equals(System.Formats.Asn1.Asn1Tag other) { throw null; } + public override bool Equals(object? obj) { throw null; } + public override int GetHashCode() { throw null; } + public bool HasSameClassAndValue(System.Formats.Asn1.Asn1Tag other) { throw null; } + public static bool operator ==(System.Formats.Asn1.Asn1Tag left, System.Formats.Asn1.Asn1Tag right) { throw null; } + public static bool operator !=(System.Formats.Asn1.Asn1Tag left, System.Formats.Asn1.Asn1Tag right) { throw null; } + public override string ToString() { throw null; } + public static bool TryDecode(System.ReadOnlySpan source, out System.Formats.Asn1.Asn1Tag tag, out int bytesConsumed) { throw null; } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + } + public partial class AsnContentException : System.Exception + { + public AsnContentException() { } + protected AsnContentException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public AsnContentException(string? message) { } + public AsnContentException(string? message, System.Exception? inner) { } + } + public static partial class AsnDecoder + { + public static byte[] ReadBitString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int unusedBitCount, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool ReadBoolean(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static string ReadCharacterString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.UniversalTagNumber encodingType, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Formats.Asn1.Asn1Tag ReadEncodedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed) { throw null; } + public static System.ReadOnlySpan ReadEnumeratedBytes(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Enum ReadEnumeratedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Type enumType, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static TEnum ReadEnumeratedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { throw null; } + public static System.DateTimeOffset ReadGeneralizedTime(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Numerics.BigInteger ReadInteger(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.ReadOnlySpan ReadIntegerBytes(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Collections.BitArray ReadNamedBitList(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Enum ReadNamedBitListValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Type flagsEnumType, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static TFlagsEnum ReadNamedBitListValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TFlagsEnum : System.Enum { throw null; } + public static void ReadNull(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static string ReadObjectIdentifier(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static byte[] ReadOctetString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static void ReadSequence(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static void ReadSetOf(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, bool skipSortOrderValidation = false, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.DateTimeOffset ReadUtcTime(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, int twoDigitYearMax = 2049, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadBitString(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, out int unusedBitCount, out int bytesConsumed, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadCharacterString(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.UniversalTagNumber encodingType, out int bytesConsumed, out int charsWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadCharacterStringBytes(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.Asn1Tag expectedTag, out int bytesConsumed, out int bytesWritten) { throw null; } + public static bool TryReadEncodedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out System.Formats.Asn1.Asn1Tag tag, out int contentOffset, out int contentLength, out int bytesConsumed) { throw null; } + public static bool TryReadInt32(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadInt64(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out long value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadOctetString(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadPrimitiveBitString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int unusedBitCount, out System.ReadOnlySpan value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadPrimitiveCharacterStringBytes(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.Asn1Tag expectedTag, out System.ReadOnlySpan value, out int bytesConsumed) { throw null; } + public static bool TryReadPrimitiveOctetString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out System.ReadOnlySpan value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt32(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out uint value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt64(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out ulong value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + } + public enum AsnEncodingRules + { + BER = 0, + CER = 1, + DER = 2, + } + public partial class AsnReader + { + public AsnReader(System.ReadOnlyMemory data, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.AsnReaderOptions options = default(System.Formats.Asn1.AsnReaderOptions)) { } + public bool HasData { get { throw null; } } + public System.Formats.Asn1.AsnEncodingRules RuleSet { get { throw null; } } + public System.ReadOnlyMemory PeekContentBytes() { throw null; } + public System.ReadOnlyMemory PeekEncodedValue() { throw null; } + public System.Formats.Asn1.Asn1Tag PeekTag() { throw null; } + public byte[] ReadBitString(out int unusedBitCount, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool ReadBoolean(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public string ReadCharacterString(System.Formats.Asn1.UniversalTagNumber encodingType, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.ReadOnlyMemory ReadEncodedValue() { throw null; } + public System.ReadOnlyMemory ReadEnumeratedBytes(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Enum ReadEnumeratedValue(System.Type enumType, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public TEnum ReadEnumeratedValue(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { throw null; } + public System.DateTimeOffset ReadGeneralizedTime(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Numerics.BigInteger ReadInteger(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.ReadOnlyMemory ReadIntegerBytes(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Collections.BitArray ReadNamedBitList(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Enum ReadNamedBitListValue(System.Type flagsEnumType, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public TFlagsEnum ReadNamedBitListValue(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TFlagsEnum : System.Enum { throw null; } + public void ReadNull(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { } + public string ReadObjectIdentifier(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public byte[] ReadOctetString(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnReader ReadSequence(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnReader ReadSetOf(bool skipSortOrderValidation, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnReader ReadSetOf(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.DateTimeOffset ReadUtcTime(int twoDigitYearMax, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.DateTimeOffset ReadUtcTime(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public void ThrowIfNotEmpty() { } + public bool TryReadBitString(System.Span destination, out int unusedBitCount, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadCharacterString(System.Span destination, System.Formats.Asn1.UniversalTagNumber encodingType, out int charsWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadCharacterStringBytes(System.Span destination, System.Formats.Asn1.Asn1Tag expectedTag, out int bytesWritten) { throw null; } + public bool TryReadInt32(out int value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadInt64(out long value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadOctetString(System.Span destination, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadPrimitiveBitString(out int unusedBitCount, out System.ReadOnlyMemory value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadPrimitiveCharacterStringBytes(System.Formats.Asn1.Asn1Tag expectedTag, out System.ReadOnlyMemory contents) { throw null; } + public bool TryReadPrimitiveOctetString(out System.ReadOnlyMemory contents, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool TryReadUInt32(out uint value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool TryReadUInt64(out ulong value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + } + public partial struct AsnReaderOptions + { + private int _dummyPrimitive; + public bool SkipSetSortOrderVerification { readonly get { throw null; } set { } } + public int UtcTimeTwoDigitYearMax { get { throw null; } set { } } + } + public sealed partial class AsnWriter + { + public AsnWriter(System.Formats.Asn1.AsnEncodingRules ruleSet) { } + public System.Formats.Asn1.AsnEncodingRules RuleSet { get { throw null; } } + public void CopyTo(System.Formats.Asn1.AsnWriter destination) { } + public byte[] Encode() { throw null; } + public bool EncodedValueEquals(System.Formats.Asn1.AsnWriter other) { throw null; } + public bool EncodedValueEquals(System.ReadOnlySpan other) { throw null; } + public int GetEncodedLength() { throw null; } + public void PopOctetString(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void PopSequence(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void PopSetOf(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public System.Formats.Asn1.AsnWriter.Scope PushOctetString(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnWriter.Scope PushSequence(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnWriter.Scope PushSetOf(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public void Reset() { } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + public void WriteBitString(System.ReadOnlySpan value, int unusedBitCount = 0, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteBoolean(bool value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteCharacterString(System.Formats.Asn1.UniversalTagNumber encodingType, System.ReadOnlySpan str, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteCharacterString(System.Formats.Asn1.UniversalTagNumber encodingType, string value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteEncodedValue(System.ReadOnlySpan value) { } + public void WriteEnumeratedValue(System.Enum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteEnumeratedValue(TEnum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { } + public void WriteGeneralizedTime(System.DateTimeOffset value, bool omitFractionalSeconds = false, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteInteger(long value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteInteger(System.Numerics.BigInteger value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteInteger(System.ReadOnlySpan value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + [System.CLSCompliantAttribute(false)] + public void WriteInteger(ulong value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteIntegerUnsigned(System.ReadOnlySpan value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteNamedBitList(System.Collections.BitArray value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteNamedBitList(System.Enum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteNamedBitList(TEnum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { } + public void WriteNull(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteObjectIdentifier(System.ReadOnlySpan oidValue, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteObjectIdentifier(string oidValue, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteOctetString(System.ReadOnlySpan value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteUtcTime(System.DateTimeOffset value, int twoDigitYearMax, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteUtcTime(System.DateTimeOffset value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public readonly partial struct Scope : System.IDisposable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public void Dispose() { } + } + } + public enum TagClass + { + Universal = 0, + Application = 64, + ContextSpecific = 128, + Private = 192, + } + public enum UniversalTagNumber + { + EndOfContents = 0, + Boolean = 1, + Integer = 2, + BitString = 3, + OctetString = 4, + Null = 5, + ObjectIdentifier = 6, + ObjectDescriptor = 7, + External = 8, + InstanceOf = 8, + Real = 9, + Enumerated = 10, + Embedded = 11, + UTF8String = 12, + RelativeObjectIdentifier = 13, + Time = 14, + Sequence = 16, + SequenceOf = 16, + Set = 17, + SetOf = 17, + NumericString = 18, + PrintableString = 19, + T61String = 20, + TeletexString = 20, + VideotexString = 21, + IA5String = 22, + UtcTime = 23, + GeneralizedTime = 24, + GraphicString = 25, + ISO646String = 26, + VisibleString = 26, + GeneralString = 27, + UniversalString = 28, + UnrestrictedCharacterString = 29, + BMPString = 30, + Date = 31, + TimeOfDay = 32, + DateTime = 33, + Duration = 34, + ObjectIdentifierIRI = 35, + RelativeObjectIdentifierIRI = 36, + } +} diff --git a/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj new file mode 100644 index 00000000000000..7715f692f9c04b --- /dev/null +++ b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj @@ -0,0 +1,12 @@ + + + netstandard2.0 + enable + + + + + + + + diff --git a/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx new file mode 100644 index 00000000000000..18ac1f416a1f66 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The destination is too small to hold the encoded value. + + + ASN.1 Enumerated values only apply to enum types without the [Flags] attribute. + + + Enumerations with a backing type of '{0}' are not supported for ReadEnumeratedValue. + + + The OID value is invalid. + + + Named bit list operations require an enum with the [Flags] attribute. + + + The destination buffer overlaps the source buffer. + + + The specified tag has the Universal TagClass, but the TagValue does not correspond with a known character string type. + + + An integer value cannot be empty. + + + The first 9 bits of the integer value all have the same value. Ensure the input is in big-endian byte order and that all redundant leading bytes have been removed. + + + Tags with TagClass Universal must have the appropriate TagValue value for the data type being read or written. + + + Unused bit count must be 0 when the bit string is empty. + + + Unused bit count must be between 0 and 7, inclusive. + + + One or more of the bits covered by the provided unusedBitCount value is set. All unused bits must be cleared. + + + The input to WriteEncodedValue must represent a single encoded value with no trailing data. + + + Encode cannot be called while a Sequence, Set-Of, or Octet String is still open. + + + Cannot pop the requested tag as it is not currently in progress. + + + A constructed tag used a definite length encoding, which is invalid for CER data. The input may be encoded with BER or DER. + + + The encoded value uses a primitive encoding, which is invalid for '{0}' values. + + + The ASN.1 value is invalid. + + + The encoded enumerated value is larger than the value size of the '{0}' enum. + + + The encoded value is not valid under the selected encoding, but it may be valid under the BER or DER encoding. + + + The encoded value is not valid under the selected encoding, but it may be valid under the BER encoding. + + + The encoded value is not valid under the selected encoding, but it may be valid under the BER or CER encoding. + + + The provided data does not represent a valid tag. + + + The encoded length exceeds the number of bytes remaining in the input buffer. + + + The encoded length is not valid under the requested encoding rules, the value may be valid under the BER encoding. + + + The encoded length exceeds the maximum supported by this library (Int32.MaxValue). + + + The encoded named bit list value is larger than the value size of the '{0}' enum. + + + The encoded value uses a constructed encoding, which is invalid for '{0}' values. + + + The encoded set is not sorted as required by the current encoding rules. The value may be valid under the BER encoding, or you can ignore the sort validation by specifying skipSortValidation=true. + + + The last expected value has been read, but the reader still has pending data. This value may be from a newer schema, or is corrupt. + + + The provided data is tagged with '{0}' class value '{1}', but it should have been '{2}' class value '{3}'. + + diff --git a/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj new file mode 100644 index 00000000000000..ddd7a52c3e5f41 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj @@ -0,0 +1,53 @@ + + + true + enable + netstandard2.0 + + + + Common\System\Security\Cryptography\CryptoPool.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.Accelerators.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs similarity index 94% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.Accelerators.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs index f34f6d99e2f332..82c6305f2448de 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.Accelerators.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal partial struct Asn1Tag + public partial struct Asn1Tag { /// /// Represents the End-of-Contents meta-tag. /// - public static readonly Asn1Tag EndOfContents = new Asn1Tag(0, (int)UniversalTagNumber.EndOfContents); + internal static readonly Asn1Tag EndOfContents = new Asn1Tag(0, (int)UniversalTagNumber.EndOfContents); /// /// Represents the universal class tag for a Boolean value. diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs similarity index 72% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs index daefd32116442b..3ecf79dd79ddc8 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs @@ -4,14 +4,13 @@ using System.Diagnostics; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// This type represents an ASN.1 tag, as described in ITU-T Recommendation X.680. /// // T-REC-X.690-201508 sec 8.1.2 - internal partial struct Asn1Tag : IEquatable + public readonly partial struct Asn1Tag : IEquatable { private const byte ClassMask = 0b1100_0000; private const byte ConstructedMask = 0b0010_0000; @@ -26,8 +25,8 @@ internal partial struct Asn1Tag : IEquatable public TagClass TagClass => (TagClass)(_controlFlags & ClassMask); /// - /// Indicates if the tag represents a constructed encoding (true), or - /// a primitive encoding (false). + /// Indicates if the tag represents a constructed encoding (), or + /// a primitive encoding (). /// public bool IsConstructed => (_controlFlags & ConstructedMask) != 0; @@ -38,7 +37,7 @@ internal partial struct Asn1Tag : IEquatable /// If is , this value can /// be interpreted as a . /// - public int TagValue { get; private set; } + public int TagValue { get; } private Asn1Tag(byte controlFlags, int tagValue) { @@ -50,10 +49,10 @@ private Asn1Tag(byte controlFlags, int tagValue) /// Create an for a tag from the UNIVERSAL class. /// /// - /// The value to represent as a tag. + /// One of the enumeration values that specifies the semantic type for this tag. /// /// - /// true for a constructed tag, false for a primitive tag. + /// for a constructed tag, for a primitive tag. /// /// /// is not a known value. @@ -76,16 +75,19 @@ public Asn1Tag(UniversalTagNumber universalTagNumber, bool isConstructed = false /// Create an for a specified value within a specified tag class. /// /// - /// The for this tag. + /// The tag class for this tag. /// /// /// The numeric value for this tag. /// /// - /// true for a constructed tag, false for a primitive tag. + /// for a constructed tag, for a primitive tag. /// /// - /// is not a known value --OR-- + /// is not a known value. + /// + /// -or- + /// /// is negative. /// /// @@ -94,9 +96,15 @@ public Asn1Tag(UniversalTagNumber universalTagNumber, bool isConstructed = false public Asn1Tag(TagClass tagClass, int tagValue, bool isConstructed = false) : this((byte)((byte)tagClass | (isConstructed ? ConstructedMask : 0)), tagValue) { - if (tagClass < TagClass.Universal || tagClass > TagClass.Private) + switch (tagClass) { - throw new ArgumentOutOfRangeException(nameof(tagClass)); + case TagClass.Universal: + case TagClass.ContextSpecific: + case TagClass.Application: + case TagClass.Private: + break; + default: + throw new ArgumentOutOfRangeException(nameof(tagClass)); } if (tagValue < 0) @@ -106,12 +114,12 @@ public Asn1Tag(TagClass tagClass, int tagValue, bool isConstructed = false) } /// - /// Produce an with the same and - /// values, but whose is true. + /// Produces a tag with the same and + /// values, but whose is . /// /// - /// An with the same and - /// values, but whose is true. + /// A tag with the same and + /// values, but whose is . /// public Asn1Tag AsConstructed() { @@ -119,12 +127,12 @@ public Asn1Tag AsConstructed() } /// - /// Produce an with the same and - /// values, but whose is false. + /// Produces a tag with the same and + /// values, but whose is . /// /// - /// An with the same and - /// values, but whose is false. + /// A tag with the same and + /// values, but whose is . /// public Asn1Tag AsPrimitive() { @@ -132,35 +140,20 @@ public Asn1Tag AsPrimitive() } /// - /// Read a BER-encoded tag which starts at . - /// - /// - /// The read only byte sequence from which to read. - /// - /// - /// The decoded . - /// - /// - /// - /// true if a tag was correctly decoded, false otherwise. - /// - public static bool TryDecode(ArraySegment source, out Asn1Tag tag, out int bytesConsumed) - { - return TryDecode(source.AsSpan(), out tag, out bytesConsumed); - } - - /// - /// Read a BER-encoded tag which starts at . + /// Attempts to read a BER-encoded tag which starts at . /// /// /// The read only byte sequence whose beginning is a BER-encoded tag. /// /// - /// The decoded . + /// The decoded tag. + /// + /// + /// When this method returns, contains the number of bytes that contributed + /// to the encoded tag, 0 on failure. This parameter is treated as uninitialized. /// - /// /// - /// true if a tag was correctly decoded, false otherwise. + /// if a tag was correctly decoded; otherwise, . /// public static bool TryDecode(ReadOnlySpan source, out Asn1Tag tag, out int bytesConsumed) { @@ -242,7 +235,33 @@ public static bool TryDecode(ReadOnlySpan source, out Asn1Tag tag, out int } /// - /// Report the number of bytes required for the BER-encoding of this tag. + /// Reads a BER-encoded tag which starts at . + /// + /// + /// The read only byte sequence whose beginning is a BER-encoded tag. + /// + /// + /// When this method returns, contains the number of bytes that contributed + /// to the encoded tag. This parameter is treated as uninitialized. + /// + /// + /// The decoded tag. + /// + /// + /// The provided data does not decode to a tag. + /// + public static Asn1Tag Decode(ReadOnlySpan source, out int bytesConsumed) + { + if (TryDecode(source, out Asn1Tag tag, out bytesConsumed)) + { + return tag; + } + + throw new AsnContentException(SR.ContentException_InvalidTag); + } + + /// + /// Reports the number of bytes required for the BER-encoding of this tag. /// /// /// The number of bytes required for the BER-encoding of this tag. @@ -270,7 +289,7 @@ public int CalculateEncodedSize() } /// - /// Write the BER-encoded form of this tag to . + /// Attempts to write the BER-encoded form of this tag to . /// /// /// The start of where the encoded tag should be written. @@ -279,8 +298,8 @@ public int CalculateEncodedSize() /// Receives the value from on success, 0 on failure. /// /// - /// false if . < - /// (), true otherwise. + /// if . < + /// (), otherwise. /// public bool TryEncode(Span destination, out int bytesWritten) { @@ -328,25 +347,7 @@ public bool TryEncode(Span destination, out int bytesWritten) } /// - /// Write the BER-encoded form of this tag to . - /// - /// - /// The start of where the encoded tag should be written. - /// - /// - /// Receives the value from on success, 0 on failure. - /// - /// - /// false if . < - /// (), true otherwise. - /// - public bool TryEncode(ArraySegment destination, out int bytesWritten) - { - return TryEncode(destination.AsSpan(), out bytesWritten); - } - - /// - /// Write the BER-encoded form of this tag to . + /// Writes the BER-encoded form of this tag to . /// /// /// The start of where the encoded tag should be written. @@ -355,7 +356,7 @@ public bool TryEncode(ArraySegment destination, out int bytesWritten) /// The number of bytes written to . /// /// - /// + /// /// . < . /// public int Encode(Span destination) @@ -365,25 +366,7 @@ public int Encode(Span destination) return bytesWritten; } - throw new CryptographicException(SR.Argument_EncodeDestinationTooSmall); - } - - /// - /// Write the BER-encoded form of this tag to . - /// - /// - /// The start of where the encoded tag should be written. - /// - /// - /// The number of bytes written to . - /// - /// - /// - /// . < . - /// - public int Encode(ArraySegment destination) - { - return Encode(destination.AsSpan()); + throw new ArgumentException(SR.Argument_EncodeDestinationTooSmall, nameof(destination)); } /// @@ -393,9 +376,9 @@ public int Encode(ArraySegment destination) /// Tag to test for equality. /// /// - /// true if has the same values for + /// if has the same values for /// , , and ; - /// false otherwise. + /// otherwise. /// public bool Equals(Asn1Tag other) { @@ -408,12 +391,11 @@ public bool Equals(Asn1Tag other) /// /// Object to test for value equality /// - /// false if is not an , - /// otherwise. + /// if is not an , + /// otherwise. /// public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) return false; return obj is Asn1Tag tag && Equals(tag); } @@ -437,8 +419,8 @@ public override int GetHashCode() /// The first value to compare. /// The second value to compare. /// - /// true if and have the same - /// BER encoding, false otherwise. + /// if and have the same + /// BER encoding, otherwise. /// public static bool operator ==(Asn1Tag left, Asn1Tag right) { @@ -451,8 +433,8 @@ public override int GetHashCode() /// The first value to compare. /// The second value to compare. /// - /// true if and have a different - /// BER encoding, false otherwise. + /// if and have a different + /// BER encoding, otherwise. /// public static bool operator !=(Asn1Tag left, Asn1Tag right) { @@ -465,8 +447,8 @@ public override int GetHashCode() /// /// Tag to test for concept equality. /// - /// true if has the same and - /// as this tag, false otherwise. + /// if has the same and + /// as this tag, otherwise. /// public bool HasSameClassAndValue(Asn1Tag other) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnCharacterStringEncodings.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs similarity index 82% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnCharacterStringEncodings.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs index c8d24878a15066..a9d24cf0de6543 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnCharacterStringEncodings.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs @@ -4,21 +4,24 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { internal static class AsnCharacterStringEncodings { - private static readonly Text.Encoding s_utf8Encoding = new UTF8Encoding(false, throwOnInvalidBytes: true); - private static readonly Text.Encoding s_bmpEncoding = new BMPEncoding(); - private static readonly Text.Encoding s_ia5Encoding = new IA5Encoding(); - private static readonly Text.Encoding s_visibleStringEncoding = new VisibleStringEncoding(); - private static readonly Text.Encoding s_numericStringEncoding = new NumericStringEncoding(); - private static readonly Text.Encoding s_printableStringEncoding = new PrintableStringEncoding(); - private static readonly Text.Encoding s_t61Encoding = new T61Encoding(); - - internal static Text.Encoding GetEncoding(UniversalTagNumber encodingType) => + private static readonly Encoding s_utf8Encoding = + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + + private static readonly Encoding s_bmpEncoding = new BMPEncoding(); + private static readonly Encoding s_ia5Encoding = new IA5Encoding(); + private static readonly Encoding s_visibleStringEncoding = new VisibleStringEncoding(); + private static readonly Encoding s_numericStringEncoding = new NumericStringEncoding(); + private static readonly Encoding s_printableStringEncoding = new PrintableStringEncoding(); + private static readonly Encoding s_t61Encoding = new T61Encoding(); + + internal static Encoding GetEncoding(UniversalTagNumber encodingType) => encodingType switch { UniversalTagNumber.UTF8String => s_utf8Encoding, @@ -30,9 +33,50 @@ internal static Text.Encoding GetEncoding(UniversalTagNumber encodingType) => UniversalTagNumber.T61String => s_t61Encoding, _ => throw new ArgumentOutOfRangeException(nameof(encodingType), encodingType, null), }; + + internal static int GetByteCount(this Encoding encoding, ReadOnlySpan str) + { + if (str.IsEmpty) + { + // Ensure a non-null pointer is obtained, even though the expected answer is 0. + str = string.Empty.AsSpan(); + } + + unsafe + { + fixed (char* strPtr = &MemoryMarshal.GetReference(str)) + { + return encoding.GetByteCount(strPtr, str.Length); + } + } + } + + internal static int GetBytes(this Encoding encoding, ReadOnlySpan chars, Span bytes) + { + if (chars.IsEmpty) + { + // Ensure a non-null pointer is obtained. + chars = string.Empty.AsSpan(); + } + + if (bytes.IsEmpty) + { + // Ensure a non-null pointer is obtained. + bytes = Array.Empty(); + } + + unsafe + { + fixed (char* charsPtr = &MemoryMarshal.GetReference(chars)) + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) + { + return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length); + } + } + } } - internal abstract class SpanBasedEncoding : Text.Encoding + internal abstract class SpanBasedEncoding : Encoding { protected SpanBasedEncoding() : base(0, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback) @@ -118,7 +162,7 @@ public override unsafe int GetChars(byte* bytes, int byteCount, char* chars, int } } - internal class IA5Encoding : RestrictedAsciiStringEncoding + internal sealed class IA5Encoding : RestrictedAsciiStringEncoding { // T-REC-X.680-201508 sec 41, Table 8. // ISO International Register of Coded Character Sets to be used with Escape Sequences 001 @@ -134,7 +178,7 @@ internal IA5Encoding() } } - internal class VisibleStringEncoding : RestrictedAsciiStringEncoding + internal sealed class VisibleStringEncoding : RestrictedAsciiStringEncoding { // T-REC-X.680-201508 sec 41, Table 8. // ISO International Register of Coded Character Sets to be used with Escape Sequences 006 @@ -146,7 +190,7 @@ internal VisibleStringEncoding() } } - internal class NumericStringEncoding : RestrictedAsciiStringEncoding + internal sealed class NumericStringEncoding : RestrictedAsciiStringEncoding { // T-REC-X.680-201508 sec 41.2 (Table 9) // 0, 1, ... 9 + space @@ -156,7 +200,7 @@ internal NumericStringEncoding() } } - internal class PrintableStringEncoding : RestrictedAsciiStringEncoding + internal sealed class PrintableStringEncoding : RestrictedAsciiStringEncoding { // T-REC-X.680-201508 sec 41.4 internal PrintableStringEncoding() @@ -228,7 +272,7 @@ protected override int GetBytes(ReadOnlySpan chars, Span bytes, bool EncoderFallback.CreateFallbackBuffer().Fallback(c, i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } if (write) @@ -258,7 +302,7 @@ protected override int GetChars(ReadOnlySpan bytes, Span chars, bool i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } if (write) @@ -277,7 +321,7 @@ protected override int GetChars(ReadOnlySpan bytes, Span chars, bool // T-REC-X.690-201508 sec 8.23.8 says to see ISO/IEC 10646:2003 section 13.1. // ISO/IEC 10646:2003 sec 13.1 says each character is represented by "two octets". // ISO/IEC 10646:2003 sec 6.3 says that when serialized as octets to use big endian. - internal class BMPEncoding : SpanBasedEncoding + internal sealed class BMPEncoding : SpanBasedEncoding { protected override int GetBytes(ReadOnlySpan chars, Span bytes, bool write) { @@ -297,7 +341,7 @@ protected override int GetBytes(ReadOnlySpan chars, Span bytes, bool EncoderFallback.CreateFallbackBuffer().Fallback(c, i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } ushort val16 = c; @@ -328,7 +372,7 @@ protected override int GetChars(ReadOnlySpan bytes, Span chars, bool bytes.Length - 1); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } int writeIdx = 0; @@ -345,7 +389,7 @@ protected override int GetChars(ReadOnlySpan bytes, Span chars, bool i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } if (write) @@ -377,10 +421,10 @@ public override int GetMaxCharCount(int byteCount) /// Compatibility encoding for T61Strings. Interprets the characters as UTF-8 or /// ISO-8859-1 as a fallback. /// - internal class T61Encoding : Text.Encoding + internal sealed class T61Encoding : Encoding { - private static readonly Text.Encoding s_utf8Encoding = new UTF8Encoding(false, throwOnInvalidBytes: true); - private static readonly Text.Encoding s_latin1Encoding = System.Text.Encoding.GetEncoding("iso-8859-1"); + private static readonly Encoding s_utf8Encoding = new UTF8Encoding(false, throwOnInvalidBytes: true); + private static readonly Encoding s_latin1Encoding = GetEncoding("iso-8859-1"); public override int GetByteCount(char[] chars, int index, int count) { diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnContentException.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnContentException.cs new file mode 100644 index 00000000000000..3f7fa488f7700e --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnContentException.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; + +namespace System.Formats.Asn1 +{ + [Serializable] + public class AsnContentException : Exception + { + public AsnContentException() + : base(SR.ContentException_DefaultMessage) + { + } + + public AsnContentException(string? message) + : base(message ?? SR.ContentException_DefaultMessage) + { + } + + public AsnContentException(string? message, Exception? inner) + : base(message ?? SR.ContentException_DefaultMessage, inner) + { + } + + protected AsnContentException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs new file mode 100644 index 00000000000000..f87776fd54d63d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs @@ -0,0 +1,854 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Attempts to get a Bit String value from with a specified tag under + /// the specified encoding rules, if the value is contained in a single (primitive) encoding. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// This parameter is treated as uninitialized. + /// + /// + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Bit String. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// if the Bit String value has a primitive encoding and all of the bits + /// reported as unused are set to 0; + /// otherwise, . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public static bool TryReadPrimitiveBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out ReadOnlySpan value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + contentsLength: out _, + headerLength: out _, + out int localUbc, + out ReadOnlySpan localValue, + out int consumed, + out byte normalizedLastByte)) + { + // Check that this isn't a BER reader which encountered a situation where + // an "unused" bit was not set to 0. + if (localValue.Length == 0 || normalizedLastByte == localValue[localValue.Length - 1]) + { + unusedBitCount = localUbc; + value = localValue; + bytesConsumed = consumed; + return true; + } + } + + unusedBitCount = 0; + value = default; + bytesConsumed = 0; + return false; + } + + /// + /// Attempts to copy a Bit String value from with a specified tag under + /// the specified encoding rules into . + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes written to . + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// if is large enough to receive the + /// value of the Bit String; + /// otherwise, . + /// + /// + /// The least significant bits in the last byte which are reported as "unused" by the + /// value will be copied into + /// as unset bits, irrespective of their value in the encoded representation. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// overlaps . + /// + /// + /// + public static bool TryReadBitString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out int bytesConsumed, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } + + int localUbc; + byte normalizedLastByte; + int consumed; + int? contentsLength; + int headerLength; + + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + out contentsLength, + out headerLength, + out localUbc, + out ReadOnlySpan value, + out consumed, + out normalizedLastByte)) + { + if (value.Length > destination.Length) + { + bytesConsumed = 0; + bytesWritten = 0; + unusedBitCount = 0; + return false; + } + + CopyBitStringValue(value, normalizedLastByte, destination); + + bytesWritten = value.Length; + bytesConsumed = consumed; + unusedBitCount = localUbc; + return true; + } + + // If we get here, the tag was appropriate, but the encoding was constructed. + + if (TryCopyConstructedBitStringValue( + Slice(source, headerLength, contentsLength), + ruleSet, + destination, + contentsLength == null, + out localUbc, + out int bytesRead, + out int written)) + { + unusedBitCount = localUbc; + bytesConsumed = headerLength + bytesRead; + bytesWritten = written; + return true; + } + + bytesWritten = bytesConsumed = unusedBitCount = 0; + return false; + } + + /// + /// Reads a Bit String value from with a specified tag under + /// the specified encoding rules, returning the contents in a new array. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// An array containing the contents of the Bit String value. + /// + /// + /// The least significant bits in the last byte which are reported as "unused" by the + /// value will be copied into the return value + /// as unset bits, irrespective of their value in the encoded representation. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public static byte[] ReadBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + out int? contentsLength, + out int headerLength, + out int localUbc, + out ReadOnlySpan localValue, + out int consumed, + out byte normalizedLastByte)) + { + byte[] ret = localValue.ToArray(); + + // Update the last byte in case it's a non-canonical byte in a BER encoding. + if (localValue.Length > 0) + { + ret[ret.Length - 1] = normalizedLastByte; + } + + unusedBitCount = localUbc; + bytesConsumed = consumed; + return ret; + } + + // If we get here, the tag was appropriate, but the encoding was constructed. + + // Guaranteed long enough + int tooBig = contentsLength ?? SeekEndOfContents(source.Slice(headerLength), ruleSet); + + byte[] rented = CryptoPool.Rent(tooBig); + + if (TryCopyConstructedBitStringValue( + Slice(source, headerLength, contentsLength), + ruleSet, + rented, + contentsLength == null, + out localUbc, + out int bytesRead, + out int written)) + { + byte[] ret = rented.AsSpan(0, written).ToArray(); + CryptoPool.Return(rented, written); + unusedBitCount = localUbc; + bytesConsumed = headerLength + bytesRead; + return ret; + } + + Debug.Fail("TryCopyConstructedBitStringValue failed with a pre-allocated buffer"); + throw new AsnContentException(); + } + + private static void ParsePrimitiveBitStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out ReadOnlySpan value, + out byte normalizedLastByte) + { + // T-REC-X.690-201508 sec 9.2 + if (ruleSet == AsnEncodingRules.CER && source.Length > MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); + } + + // T-REC-X.690-201508 sec 8.6.2.3 + if (source.Length == 0) + { + throw new AsnContentException(); + } + + unusedBitCount = source[0]; + + // T-REC-X.690-201508 sec 8.6.2.2 + if (unusedBitCount > 7) + { + throw new AsnContentException(); + } + + if (source.Length == 1) + { + // T-REC-X.690-201508 sec 8.6.2.4 + if (unusedBitCount > 0) + { + throw new AsnContentException(); + } + + Debug.Assert(unusedBitCount == 0); + value = ReadOnlySpan.Empty; + normalizedLastByte = 0; + return; + } + + // Build a mask for the bits that are used so the normalized value can be computed + // + // If 3 bits are "unused" then build a mask for them to check for 0. + // -1 << 3 => 0b1111_1111 << 3 => 0b1111_1000 + int mask = -1 << unusedBitCount; + byte lastByte = source[source.Length - 1]; + byte maskedByte = (byte)(lastByte & mask); + + if (maskedByte != lastByte) + { + // T-REC-X.690-201508 sec 11.2.1 + if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + } + + normalizedLastByte = maskedByte; + value = source.Slice(1); + } + + private delegate void BitStringCopyAction( + ReadOnlySpan value, + byte normalizedLastByte, + Span destination); + + private static void CopyBitStringValue( + ReadOnlySpan value, + byte normalizedLastByte, + Span destination) + { + if (value.Length == 0) + { + return; + } + + value.CopyTo(destination); + // Replace the last byte with the normalized answer. + destination[value.Length - 1] = normalizedLastByte; + } + + private static int CountConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + bool isIndefinite) + { + Span destination = Span.Empty; + + return ProcessConstructedBitString( + source, + ruleSet, + destination, + null, + isIndefinite, + out _, + out _); + } + + private static void CopyConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + bool isIndefinite, + out int unusedBitCount, + out int bytesRead, + out int bytesWritten) + { + Span tmpDest = destination; + + bytesWritten = ProcessConstructedBitString( + source, + ruleSet, + tmpDest, + (value, lastByte, dest) => CopyBitStringValue(value, lastByte, dest), + isIndefinite, + out unusedBitCount, + out bytesRead); + } + + private static int ProcessConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + BitStringCopyAction? copyAction, + bool isIndefinite, + out int lastUnusedBitCount, + out int bytesRead) + { + lastUnusedBitCount = 0; + bytesRead = 0; + int lastSegmentLength = MaxCERSegmentSize; + + ReadOnlySpan cur = source; + Stack<(int Offset, int Length, bool Indefinite, int BytesRead)>? readerStack = null; + int totalLength = 0; + Asn1Tag tag = Asn1Tag.ConstructedBitString; + Span curDest = destination; + + while (true) + { + while (!cur.IsEmpty) + { + tag = ReadTagAndLength(cur, ruleSet, out int? length, out int headerLength); + + if (tag == Asn1Tag.PrimitiveBitString) + { + if (lastUnusedBitCount != 0) + { + // T-REC-X.690-201508 sec 8.6.4, only the last segment may have + // a number of bits not a multiple of 8. + throw new AsnContentException(); + } + + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); + } + + Debug.Assert(length != null); + ReadOnlySpan encodedValue = Slice(cur, headerLength, length.Value); + + ParsePrimitiveBitStringContents( + encodedValue, + ruleSet, + out lastUnusedBitCount, + out ReadOnlySpan contents, + out byte normalizedLastByte); + + int localLen = headerLength + encodedValue.Length; + cur = cur.Slice(localLen); + + bytesRead += localLen; + totalLength += contents.Length; + lastSegmentLength = encodedValue.Length; + + if (copyAction != null) + { + copyAction(contents, normalizedLastByte, curDest); + curDest = curDest.Slice(contents.Length); + } + } + else if (tag == Asn1Tag.EndOfContents && isIndefinite) + { + ValidateEndOfContents(tag, length, headerLength); + + bytesRead += headerLength; + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + cur = topSpan.Slice(bytesRead); + + bytesRead += pushedBytesRead; + isIndefinite = wasIndefinite; + } + else + { + // We have matched the EndOfContents that brought us here. + break; + } + } + else if (tag == Asn1Tag.ConstructedBitString) + { + if (ruleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (readerStack == null) + { + readerStack = new Stack<(int, int, bool, int)>(); + } + + if (!source.Overlaps(cur, out int curOffset)) + { + Debug.Fail("Non-overlapping data encountered..."); + throw new AsnContentException(); + } + + readerStack.Push((curOffset, cur.Length, isIndefinite, bytesRead)); + + cur = Slice(cur, headerLength, length); + bytesRead = headerLength; + isIndefinite = (length == null); + } + else + { + // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) + throw new AsnContentException(); + } + } + + if (isIndefinite && tag != Asn1Tag.EndOfContents) + { + throw new AsnContentException(); + } + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + + ReadOnlySpan tmpSpan = source.Slice(topOffset, topLength); + cur = tmpSpan.Slice(bytesRead); + + isIndefinite = wasIndefinite; + bytesRead += pushedBytesRead; + } + else + { + return totalLength; + } + } + } + + private static bool TryCopyConstructedBitStringValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span dest, + bool isIndefinite, + out int unusedBitCount, + out int bytesRead, + out int bytesWritten) + { + // Call CountConstructedBitString to get the required byte and to verify that the + // data is well-formed before copying into dest. + int contentLength = CountConstructedBitString(source, ruleSet, isIndefinite); + + // Since the unused bits byte from the segments don't count, only one segment + // returns 999 (or less), the second segment bumps the count to 1000, and is legal. + // + // T-REC-X.690-201508 sec 9.2 + if (ruleSet == AsnEncodingRules.CER && contentLength < MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (dest.Length < contentLength) + { + unusedBitCount = 0; + bytesRead = 0; + bytesWritten = 0; + return false; + } + + CopyConstructedBitString( + source, + ruleSet, + dest, + isIndefinite, + out unusedBitCount, + out bytesRead, + out bytesWritten); + + Debug.Assert(bytesWritten == contentLength); + return true; + } + + private static bool TryReadPrimitiveBitStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + out int? contentsLength, + out int headerLength, + out int unusedBitCount, + out ReadOnlySpan value, + out int bytesConsumed, + out byte normalizedLastByte) + { + Asn1Tag actualTag = + ReadTagAndLength(source, ruleSet, out contentsLength, out headerLength); + + CheckExpectedTag(actualTag, expectedTag, UniversalTagNumber.BitString); + + // Ensure the length made sense. + ReadOnlySpan encodedValue = Slice(source, headerLength, contentsLength); + + if (actualTag.IsConstructed) + { + if (ruleSet == AsnEncodingRules.DER) + { + throw new AsnContentException(SR.ContentException_InvalidUnderDer_TryBerOrCer); + } + + unusedBitCount = 0; + value = default; + normalizedLastByte = 0; + bytesConsumed = 0; + return false; + } + + Debug.Assert(contentsLength.HasValue); + + ParsePrimitiveBitStringContents( + encodedValue, + ruleSet, + out unusedBitCount, + out value, + out normalizedLastByte); + + bytesConsumed = headerLength + encodedValue.Length; + return true; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a BIT STRING with a specified tag, returning the contents + /// as a over the original data. + /// + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// + /// + /// On success, receives a over the original data + /// corresponding to the value of the BIT STRING. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// and advances the reader if the BIT STRING value had a primitive encoding, + /// and does not advance the reader if it had a constructed encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public bool TryReadPrimitiveBitString( + out int unusedBitCount, + out ReadOnlyMemory value, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadPrimitiveBitString( + _data.Span, + RuleSet, + out unusedBitCount, + out ReadOnlySpan span, + out int consumed, + expectedTag); + + if (ret) + { + value = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + value = default; + } + + return ret; + } + + /// + /// Reads the next value as a BIT STRING with a specified tag, copying the value + /// into a provided destination buffer. + /// + /// The buffer in which to write. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// + /// + /// On success, receives the number of bytes written to . + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public bool TryReadBitString( + Span destination, + out int unusedBitCount, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadBitString( + _data.Span, + destination, + RuleSet, + out unusedBitCount, + out int consumed, + out bytesWritten, + expectedTag); + + if (ret) + { + _data = _data.Slice(consumed); + } + + return ret; + } + + /// + /// Reads the next value as a BIT STRING with a specified tag, returning the value + /// in a byte array. + /// + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// A copy of the value in a newly allocated, precisely sized, array. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = null) + { + byte[] ret = AsnDecoder.ReadBitString( + _data.Span, + RuleSet, + out unusedBitCount, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs new file mode 100644 index 00000000000000..dc2b113924ede6 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Boolean value from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// The decoded value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static bool ReadBoolean( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + // T-REC-X.690-201508 sec 8.2.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag ?? Asn1Tag.Boolean, + UniversalTagNumber.Boolean, + out int consumed); + + // T-REC-X.690-201508 sec 8.2.1 + if (contents.Length != 1) + { + throw new AsnContentException(); + } + + byte val = contents[0]; + + // T-REC-X.690-201508 sec 8.2.2 + if (val == 0) + { + bytesConsumed = consumed; + return false; + } + + // T-REC-X.690-201508 sec 11.1 + if (val != 0xFF && (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER)) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + bytesConsumed = consumed; + return true; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a Boolean with a specified tag. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// The decoded value. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public bool ReadBoolean(Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.ReadBoolean(_data.Span, RuleSet, out int bytesConsumed, expectedTag); + _data = _data.Slice(bytesConsumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs new file mode 100644 index 00000000000000..605d44b35153ab --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs @@ -0,0 +1,412 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads an Enumerated value from with a specified tag under + /// the specified encoding rules, returning the contents as a slice of the buffer. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The slice of the buffer containing the bytes of the Enumerated value, + /// in signed big-endian form. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static ReadOnlySpan ReadEnumeratedBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return GetIntegerContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.Enumerated, + UniversalTagNumber.Enumerated, + out bytesConsumed); + } + + /// + /// Reads an Enumerated from with a specified tag under + /// the specified encoding rules, converting it to the + /// non-[] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// Destination enum type + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static TEnum ReadEnumeratedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + where TEnum : Enum + { + Type tEnum = typeof(TEnum); + + return (TEnum)Enum.ToObject( + tEnum, + ReadEnumeratedValue( + source, + ruleSet, + tEnum, + out bytesConsumed, + expectedTag)); + } + + /// + /// Reads an Enumerated from with a specified tag under + /// the specified encoding rules, converting it to the + /// non-[] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// Type object representing the destination type. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is . + /// + public static Enum ReadEnumeratedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Type enumType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (enumType == null) + throw new ArgumentNullException(nameof(enumType)); + + const UniversalTagNumber TagNumber = UniversalTagNumber.Enumerated; + Asn1Tag localTag = expectedTag ?? Asn1Tag.Enumerated; + + // This will throw an ArgumentException if TEnum isn't an enum type, + // so we don't need to validate it. + Type backingType = enumType.GetEnumUnderlyingType(); + + if (enumType.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_EnumeratedValueRequiresNonFlagsEnum, + nameof(enumType)); + } + + // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. + int sizeLimit = Marshal.SizeOf(backingType); + + if (backingType == typeof(int) || + backingType == typeof(long) || + backingType == typeof(short) || + backingType == typeof(sbyte)) + { + if (!TryReadSignedInteger( + source, + ruleSet, + sizeLimit, + localTag, + TagNumber, + out long value, + out int consumed)) + { + throw new AsnContentException(SR.ContentException_EnumeratedValueTooBig); + } + + bytesConsumed = consumed; + return (Enum)Enum.ToObject(enumType, value); + } + + if (backingType == typeof(uint) || + backingType == typeof(ulong) || + backingType == typeof(ushort) || + backingType == typeof(byte)) + { + if (!TryReadUnsignedInteger( + source, + ruleSet, + sizeLimit, + localTag, + TagNumber, + out ulong value, + out int consumed)) + { + throw new AsnContentException(SR.ContentException_EnumeratedValueTooBig); + } + + bytesConsumed = consumed; + return (Enum)Enum.ToObject(enumType, value); + } + + throw new AsnContentException( + SR.Format( + SR.Argument_EnumeratedValueBackingTypeNotSupported, + backingType.FullName)); + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a Enumerated with a specified tag, returning the contents + /// as a over the original data. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The bytes of the Enumerated value, in signed big-endian form. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public ReadOnlyMemory ReadEnumeratedBytes(Asn1Tag? expectedTag = null) + { + ReadOnlySpan bytes = + AsnDecoder.ReadEnumeratedBytes(_data.Span, RuleSet, out int consumed, expectedTag); + + ReadOnlyMemory memory = AsnDecoder.Slice(_data, bytes); + + _data = _data.Slice(consumed); + return memory; + } + + /// + /// Reads the next value as an Enumerated with a specified tag, converting it to the + /// non-[] enum specified by . + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// Destination enum type + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public TEnum ReadEnumeratedValue(Asn1Tag? expectedTag = null) where TEnum : Enum + { + TEnum ret = AsnDecoder.ReadEnumeratedValue(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as an Enumerated with a specified tag, converting it to the + /// non-[] enum specified by . + /// + /// Type object representing the destination type. + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is . + /// + public Enum ReadEnumeratedValue(Type enumType, Asn1Tag? expectedTag = null) + { + Enum ret = AsnDecoder.ReadEnumeratedValue(_data.Span, RuleSet, enumType, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs similarity index 67% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs index 9c55f218db1dc0..1962f185d26ce6 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs @@ -2,93 +2,86 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Buffers.Text; using System.Diagnostics; +using System.Security.Cryptography; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a GeneralizedTime with tag UNIVERSAL 24. + /// Reads a GeneralizedTime value from with a specified tag under + /// the specified encoding rules. /// - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public DateTimeOffset ReadGeneralizedTime(bool disallowFractions = false) => - ReadGeneralizedTime(Asn1Tag.GeneralizedTime, disallowFractions); - - /// - /// Reads the next value as a GeneralizedTime with a specified tag. - /// - /// The tag to check for before reading. - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. + /// + /// The tag to check for before reading, or for the default tag (Universal 24). /// /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public DateTimeOffset ReadGeneralizedTime(Asn1Tag expectedTag, bool disallowFractions = false) + public static DateTimeOffset ReadGeneralizedTime( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { byte[]? rented = null; - Span tmpSpace; - unsafe - { - // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or - // BER specified offset. - const int StackBufSize = 64; - byte* stackBuf = stackalloc byte[StackBufSize]; - tmpSpace = new Span(stackBuf, StackBufSize); - } + // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or + // BER specified offset. + const int StackBufSize = 64; + Span tmpSpace = stackalloc byte[StackBufSize]; ReadOnlySpan contents = GetOctetStringContents( - expectedTag, + source, + ruleSet, + expectedTag ?? Asn1Tag.GeneralizedTime, UniversalTagNumber.GeneralizedTime, out int bytesRead, ref rented, tmpSpace); - DateTimeOffset value = ParseGeneralizedTime(RuleSet, contents, disallowFractions); + DateTimeOffset value = ParseGeneralizedTime(ruleSet, contents); if (rented != null) { CryptoPool.Return(rented, contents.Length); } - _data = _data.Slice(bytesRead); + bytesConsumed = bytesRead; return value; } private static DateTimeOffset ParseGeneralizedTime( AsnEncodingRules ruleSet, - ReadOnlySpan contentOctets, - bool disallowFractions) + ReadOnlySpan contentOctets) { // T-REC-X.680-201510 sec 46 defines a lot of formats for GeneralizedTime. // @@ -125,12 +118,12 @@ private static DateTimeOffset ParseGeneralizedTime( if (strict && contentOctets.Length < 15) { // yyyyMMddHHmmssZ - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } else if (contentOctets.Length < 10) { // yyyyMMddHH - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } ReadOnlySpan contents = contentOctets; @@ -186,7 +179,7 @@ private static DateTimeOffset ParseGeneralizedTime( } else { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } } else @@ -197,11 +190,6 @@ private static DateTimeOffset ParseGeneralizedTime( if (state == FracState) { - if (disallowFractions) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - Debug.Assert(!contents.IsEmpty); byte octet = contents[0]; Debug.Assert(state == GetNextState(octet)); @@ -216,20 +204,20 @@ private static DateTimeOffset ParseGeneralizedTime( // T-REC-X.690-201510 sec 11.7.4 if (strict) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } } else { Debug.Fail($"Unhandled value '{octet:X2}' in {nameof(FracState)}"); - throw new CryptographicException(); + throw new AsnContentException(); } contents = contents.Slice(1); if (contents.IsEmpty) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } // There are 36,000,000,000 ticks per hour, and hour is our largest scale. @@ -239,7 +227,7 @@ private static DateTimeOffset ParseGeneralizedTime( if (!Utf8Parser.TryParse(SliceAtMost(contents, 12), out fraction, out int fracLength) || fracLength == 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } lastFracDigit = (byte)(fraction % 10); @@ -267,7 +255,7 @@ private static DateTimeOffset ParseGeneralizedTime( if (nextState == null) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } // If this produces FracState we'll finish with a non-empty contents, and still throw. @@ -302,12 +290,12 @@ private static DateTimeOffset ParseGeneralizedTime( else { Debug.Fail($"Unhandled value '{octet:X2}' in {nameof(SuffixState)}"); - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } if (contents.IsEmpty) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } int offsetHour = ParseNonNegativeIntAndSlice(ref contents, 2); @@ -323,7 +311,7 @@ private static DateTimeOffset ParseGeneralizedTime( // is bound to [00,14] by DateTimeOffset, so no additional check is required here. if (offsetMinute > 59) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } TimeSpan tmp = new TimeSpan(offsetHour, offsetMinute, 0); @@ -340,7 +328,7 @@ private static DateTimeOffset ParseGeneralizedTime( // Was there data after a suffix, or fracstate went re-entrant? if (!contents.IsEmpty) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } // T-REC-X.690-201510 sec 11.7 @@ -348,12 +336,12 @@ private static DateTimeOffset ParseGeneralizedTime( { if (!isZulu || !second.HasValue) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } if (lastFracDigit == 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } } @@ -408,60 +396,43 @@ private static DateTimeOffset ParseGeneralizedTime( } catch (Exception e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + throw new AsnContentException(SR.ContentException_DefaultMessage, e); } } } - internal partial class AsnReader + public partial class AsnReader { - /// - /// Reads the next value as a GeneralizedTime with tag UNIVERSAL 24. - /// - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. - /// - /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public DateTimeOffset ReadGeneralizedTime(bool disallowFractions = false) => - ReadGeneralizedTime(Asn1Tag.GeneralizedTime, disallowFractions); - /// /// Reads the next value as a GeneralizedTime with a specified tag. /// - /// The tag to check for before reading. - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. + /// + /// The tag to check for before reading, or for the default tag (Universal 24). /// /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public DateTimeOffset ReadGeneralizedTime(Asn1Tag expectedTag, bool disallowFractions = false) + public DateTimeOffset ReadGeneralizedTime(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - DateTimeOffset ret = valueReader.ReadGeneralizedTime(expectedTag, disallowFractions); - valueReader.MatchSlice(ref _data); + DateTimeOffset ret = AsnDecoder.ReadGeneralizedTime(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); return ret; } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs new file mode 100644 index 00000000000000..5e6e084ab9d4ba --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs @@ -0,0 +1,747 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Binary; +using System.Diagnostics; +using System.Numerics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads an Integer value from with a specified tag under + /// the specified encoding rules, returning the contents as a slice of the buffer. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The slice of the buffer containing the bytes of the Integer value, in signed big-endian form. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static ReadOnlySpan ReadIntegerBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return GetIntegerContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out bytesConsumed); + } + + /// + /// Reads an Integer value from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The decoded numeric value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static BigInteger ReadInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + ReadOnlySpan contents = ReadIntegerBytes(source, ruleSet, out int consumed, expectedTag); + + // TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing + byte[] tmp = CryptoPool.Rent(contents.Length); + BigInteger value; + + try + { + byte fill = (contents[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF; + // Fill the unused portions of tmp with positive or negative padding. + new Span(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill); + contents.CopyTo(tmp); + // Convert to Little-Endian. + AsnWriter.Reverse(new Span(tmp, 0, contents.Length)); + value = new BigInteger(tmp); + } + finally + { + // Let CryptoPool.Return clear the whole tmp so that not even the sign bit + // is returned to the array pool. + CryptoPool.Return(tmp); + } + + bytesConsumed = consumed; + return value; + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as a signed 32-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static bool TryReadInt32( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadSignedInteger( + source, + ruleSet, + sizeof(int), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out long longValue, + out bytesConsumed)) + { + value = (int)longValue; + return true; + } + + value = 0; + return false; + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as an unsigned 32-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public static bool TryReadUInt32( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out uint value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadUnsignedInteger( + source, + ruleSet, + sizeof(uint), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out ulong ulongValue, + out bytesConsumed)) + { + value = (uint)ulongValue; + return true; + } + + value = 0; + return false; + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as a signed 64-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static bool TryReadInt64( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out long value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return TryReadSignedInteger( + source, + ruleSet, + sizeof(long), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out value, + out bytesConsumed); + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as an unsigned 64-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public static bool TryReadUInt64( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out ulong value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return TryReadUnsignedInteger( + source, + ruleSet, + sizeof(ulong), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out value, + out bytesConsumed); + } + + private static ReadOnlySpan GetIntegerContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out int bytesConsumed) + { + // T-REC-X.690-201508 sec 8.3.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); + + // T-REC-X.690-201508 sec 8.3.1 + if (contents.IsEmpty) + { + throw new AsnContentException(); + } + + // T-REC-X.690-201508 sec 8.3.2 + if (BinaryPrimitives.TryReadUInt16BigEndian(contents, out ushort bigEndianValue)) + { + const ushort RedundancyMask = 0b1111_1111_1000_0000; + ushort masked = (ushort)(bigEndianValue & RedundancyMask); + + // If the first 9 bits are all 0 or are all 1, the value is invalid. + if (masked == 0 || masked == RedundancyMask) + { + throw new AsnContentException(); + } + } + + bytesConsumed = consumed; + return contents; + } + + private static bool TryReadSignedInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + int sizeLimit, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out long value, + out int bytesConsumed) + { + Debug.Assert(sizeLimit <= sizeof(long)); + + ReadOnlySpan contents = GetIntegerContents( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); + + if (contents.Length > sizeLimit) + { + value = 0; + bytesConsumed = 0; + return false; + } + + bool isNegative = (contents[0] & 0x80) != 0; + long accum = isNegative ? -1 : 0; + + for (int i = 0; i < contents.Length; i++) + { + accum <<= 8; + accum |= contents[i]; + } + + bytesConsumed = consumed; + value = accum; + return true; + } + + private static bool TryReadUnsignedInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + int sizeLimit, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out ulong value, + out int bytesConsumed) + { + Debug.Assert(sizeLimit <= sizeof(ulong)); + + ReadOnlySpan contents = GetIntegerContents( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); + + bool isNegative = (contents[0] & 0x80) != 0; + + if (isNegative) + { + bytesConsumed = 0; + value = 0; + return false; + } + + // Ignore any padding zeros. + if (contents.Length > 1 && contents[0] == 0) + { + contents = contents.Slice(1); + } + + if (contents.Length > sizeLimit) + { + bytesConsumed = 0; + value = 0; + return false; + } + + ulong accum = 0; + + for (int i = 0; i < contents.Length; i++) + { + accum <<= 8; + accum |= contents[i]; + } + + bytesConsumed = consumed; + value = accum; + return true; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a Integer with a specified tag, returning the contents + /// as a over the original data. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The bytes of the Integer value, in signed big-endian form. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public ReadOnlyMemory ReadIntegerBytes(Asn1Tag? expectedTag = null) + { + ReadOnlySpan bytes = + AsnDecoder.ReadIntegerBytes(_data.Span, RuleSet, out int consumed, expectedTag); + + ReadOnlyMemory ret = AsnDecoder.Slice(_data, bytes); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as an Integer with a specified tag. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The decoded value. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public BigInteger ReadInteger(Asn1Tag? expectedTag = null) + { + BigInteger ret = AsnDecoder.ReadInteger(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as a signed 32-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public bool TryReadInt32(out int value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadInt32(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as an unsigned 32-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public bool TryReadUInt32(out uint value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadUInt32(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as a signed 64-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public bool TryReadInt64(out long value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadInt64(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as an unsigned 64-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public bool TryReadUInt64(out ulong value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadUInt64(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs new file mode 100644 index 00000000000000..3000f8817eb486 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs @@ -0,0 +1,593 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules, converting it to the + /// [] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// Destination enum type + /// + /// The NamedBitList value converted to a . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// The bit alignment performed by this method is to interpret the most significant bit + /// in the first byte of the value as the least significant bit in , + /// with bits increasing in value until the least significant bit of the first byte, proceeding + /// with the most significant bit of the second byte, and so on. Under this scheme, the following + /// ASN.1 type declaration and C# enumeration can be used together: + /// + /// + /// KeyUsage ::= BIT STRING { + /// digitalSignature (0), + /// nonRepudiation (1), + /// keyEncipherment (2), + /// dataEncipherment (3), + /// keyAgreement (4), + /// keyCertSign (5), + /// cRLSign (6), + /// encipherOnly (7), + /// decipherOnly (8) } + /// + /// + /// + /// [Flags] + /// enum KeyUsage + /// { + /// None = 0, + /// DigitalSignature = 1 << (0), + /// NonRepudiation = 1 << (1), + /// KeyEncipherment = 1 << (2), + /// DataEncipherment = 1 << (3), + /// KeyAgreement = 1 << (4), + /// KeyCertSign = 1 << (5), + /// CrlSign = 1 << (6), + /// EncipherOnly = 1 << (7), + /// DecipherOnly = 1 << (8), + /// } + /// + /// + /// Note that while the example here uses the KeyUsage NamedBitList from + /// RFC 3280 (4.2.1.3), + /// the example enum uses values thar are different from + /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. + /// + public static TFlagsEnum ReadNamedBitListValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + where TFlagsEnum : Enum + { + Type tFlagsEnum = typeof(TFlagsEnum); + + TFlagsEnum ret = (TFlagsEnum)Enum.ToObject( + tFlagsEnum, + ReadNamedBitListValue(source, ruleSet, tFlagsEnum, out int consumed, expectedTag)); + + // Now that there's nothing left to throw, assign bytesConsumed. + bytesConsumed = consumed; + return ret; + } + + /// + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules, converting it to the + /// [] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// Type object representing the destination type. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// The NamedBitList value converted to a . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + ///- + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is + /// + /// + public static Enum ReadNamedBitListValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Type flagsEnumType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (flagsEnumType == null) + throw new ArgumentNullException(nameof(flagsEnumType)); + + // This will throw an ArgumentException if TEnum isn't an enum type, + // so we don't need to validate it. + Type backingType = flagsEnumType.GetEnumUnderlyingType(); + + if (!flagsEnumType.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_NamedBitListRequiresFlagsEnum, + nameof(flagsEnumType)); + } + + Span stackSpan = stackalloc byte[sizeof(ulong)]; + int sizeLimit = Marshal.SizeOf(backingType); + stackSpan = stackSpan.Slice(0, sizeLimit); + + bool read = TryReadBitString( + source, + stackSpan, + ruleSet, + out int unusedBitCount, + out int consumed, + out int bytesWritten, + expectedTag); + + if (!read) + { + throw new AsnContentException( + SR.Format(SR.ContentException_NamedBitListValueTooBig, flagsEnumType.Name)); + } + + Enum ret; + + if (bytesWritten == 0) + { + // The mode isn't relevant, zero is always zero. + ret = (Enum)Enum.ToObject(flagsEnumType, 0); + bytesConsumed = consumed; + return ret; + } + + ReadOnlySpan valueSpan = stackSpan.Slice(0, bytesWritten); + + // Now that the 0-bounds check is out of the way: + // + // T-REC-X.690-201508 sec 11.2.2 + if (ruleSet == AsnEncodingRules.DER || + ruleSet == AsnEncodingRules.CER) + { + byte lastByte = valueSpan[bytesWritten - 1]; + + // No unused bits tests 0x01, 1 is 0x02, 2 is 0x04, etc. + // We already know that TryCopyBitStringBytes checked that the + // declared unused bits were 0, this checks that the last "used" bit + // isn't also zero. + byte testBit = (byte)(1 << unusedBitCount); + + if ((lastByte & testBit) == 0) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + } + + // Consider a NamedBitList defined as + // + // SomeList ::= BIT STRING { + // a(0), b(1), c(2), d(3), e(4), f(5), g(6), h(7), i(8), j(9), k(10) + // } + // + // The BIT STRING encoding of (a | j) is + // unusedBitCount = 6, + // contents: 0x80 0x40 (0b10000000_01000000) + // + // A the C# exposure of this structure we adhere to is + // + // [Flags] + // enum SomeList + // { + // A = 1, + // B = 1 << 1, + // C = 1 << 2, + // ... + // } + // + // Which happens to be exactly backwards from how the bits are encoded, but the complexity + // only needs to live here. + ret = (Enum)Enum.ToObject(flagsEnumType, InterpretNamedBitListReversed(valueSpan)); + bytesConsumed = consumed; + return ret; + } + + /// + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// The bits from the encoded value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// The bit alignment performed by this method is to interpret the most significant bit + /// in the first byte of the value as bit 0, + /// with bits increasing in value until the least significant bit of the first byte, proceeding + /// with the most significant bit of the second byte, and so on. + /// This means that the number used in an ASN.1 NamedBitList construction is the index in the + /// return value. + /// + public static BitArray ReadNamedBitList( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + Asn1Tag actualTag = ReadEncodedValue(source, ruleSet, out _, out int contentLength, out _); + + // Get the last ArgumentException out of the way before we rent arrays + if (expectedTag != null) + { + CheckExpectedTag(actualTag, expectedTag.Value, UniversalTagNumber.BitString); + } + + // The number of interpreted bytes is at most contentLength - 1, just ask for contentLength. + byte[] rented = CryptoPool.Rent(contentLength); + + if (!TryReadBitString( + source, + rented, + ruleSet, + out int unusedBitCount, + out int consumed, + out int written, + expectedTag)) + { + Debug.Fail("TryReadBitString failed with an over-allocated buffer"); + throw new InvalidOperationException(); + } + + int validBitCount = checked(written * 8 - unusedBitCount); + + Span valueSpan = rented.AsSpan(0, written); + ReverseBitsPerByte(valueSpan); + + BitArray ret = new BitArray(rented); + CryptoPool.Return(rented, written); + + // Trim off all of the unnecessary parts. + ret.Length = validBitCount; + + bytesConsumed = consumed; + return ret; + } + + private static long InterpretNamedBitListReversed(ReadOnlySpan valueSpan) + { + Debug.Assert(valueSpan.Length <= sizeof(long)); + + long accum = 0; + long currentBitValue = 1; + + for (int byteIdx = 0; byteIdx < valueSpan.Length; byteIdx++) + { + byte byteVal = valueSpan[byteIdx]; + + for (int bitIndex = 7; bitIndex >= 0; bitIndex--) + { + int test = 1 << bitIndex; + + if ((byteVal & test) != 0) + { + accum |= currentBitValue; + } + + currentBitValue <<= 1; + } + } + + return accum; + } + + internal static void ReverseBitsPerByte(Span value) + { + for (int byteIdx = 0; byteIdx < value.Length; byteIdx++) + { + byte cur = value[byteIdx]; + byte mask = 0b1000_0000; + byte next = 0; + + for (; cur != 0; cur >>= 1, mask >>= 1) + { + next |= (byte)((cur & 1) * mask); + } + + value[byteIdx] = next; + } + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a NamedBitList with a specified tag, converting it to the + /// [] enum specified by . + /// + /// The tag to check for before reading. + /// Destination enum type + /// + /// The NamedBitList value converted to a . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// The bit alignment performed by this method is to interpret the most significant bit + /// in the first byte of the value as the least significant bit in , + /// with bits increasing in value until the least significant bit of the first byte, proceeding + /// with the most significant bit of the second byte, and so on. Under this scheme, the following + /// ASN.1 type declaration and C# enumeration can be used together: + /// + /// + /// KeyUsage ::= BIT STRING { + /// digitalSignature (0), + /// nonRepudiation (1), + /// keyEncipherment (2), + /// dataEncipherment (3), + /// keyAgreement (4), + /// keyCertSign (5), + /// cRLSign (6), + /// encipherOnly (7), + /// decipherOnly (8) } + /// + /// + /// + /// [Flags] + /// enum KeyUsage + /// { + /// None = 0, + /// DigitalSignature = 1 << (0), + /// NonRepudiation = 1 << (1), + /// KeyEncipherment = 1 << (2), + /// DataEncipherment = 1 << (3), + /// KeyAgreement = 1 << (4), + /// KeyCertSign = 1 << (5), + /// CrlSign = 1 << (6), + /// EncipherOnly = 1 << (7), + /// DecipherOnly = 1 << (8), + /// } + /// + /// + /// Note that while the example here uses the KeyUsage NamedBitList from + /// RFC 3280 (4.2.1.3), + /// the example enum uses values thar are different from + /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. + /// + public TFlagsEnum ReadNamedBitListValue(Asn1Tag? expectedTag = null) where TFlagsEnum : Enum + { + TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue( + _data.Span, + RuleSet, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as a NamedBitList with a specified tag, converting it to the + /// [] enum specified by . + /// + /// The tag to check for before reading. + /// Type object representing the destination type. + /// + /// The NamedBitList value converted to a . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is + /// + /// + public Enum ReadNamedBitListValue(Type flagsEnumType, Asn1Tag? expectedTag = null) + { + Enum ret = AsnDecoder.ReadNamedBitListValue( + _data.Span, + RuleSet, + flagsEnumType, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as a NamedBitList with a specified tag. + /// + /// The tag to check for before reading. + /// + /// The bits from the encoded value. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public BitArray ReadNamedBitList(Asn1Tag? expectedTag = null) + { + BitArray ret = AsnDecoder.ReadNamedBitList(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs new file mode 100644 index 00000000000000..f8c91c2b4cab46 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Null value from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 5). + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static void ReadNull( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + // T-REC-X.690-201508 sec 8.8.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag ?? Asn1Tag.Null, + UniversalTagNumber.Null, + out int consumed); + + // T-REC-X.690-201508 sec 8.8.2 + if (contents.Length != 0) + { + throw new AsnContentException(); + } + + bytesConsumed = consumed; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a NULL with a specified tag. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 5). + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public void ReadNull(Asn1Tag? expectedTag = null) + { + AsnDecoder.ReadNull(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs new file mode 100644 index 00000000000000..2f7422d5baacbc --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs @@ -0,0 +1,686 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Attempts to get an Octet String value from with a specified tag under + /// the specified encoding rules, copying the value into the provided destination buffer. + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes written to . + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// if is large enough to receive the + /// value of the Octet String; + /// otherwise, . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// overlaps . + /// + /// + /// + public static bool TryReadOctetString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int bytesConsumed, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } + + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out int consumed)) + { + if (contents.Length > destination.Length) + { + bytesWritten = 0; + bytesConsumed = 0; + return false; + } + + contents.CopyTo(destination); + bytesWritten = contents.Length; + bytesConsumed = consumed; + return true; + } + + bool copied = TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, + destination, + contentLength == null, + out int bytesRead, + out bytesWritten); + + if (copied) + { + bytesConsumed = headerLength + bytesRead; + } + else + { + bytesConsumed = 0; + } + + return copied; + } + + /// + /// Reads an Octet String value from with a specified tag under + /// the specified encoding rules, returning the contents in a new array. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// An array containing the contents of the Octet String value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public static byte[] ReadOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + byte[]? rented = null; + + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + out int consumed, + ref rented); + + byte[] ret = contents.ToArray(); + + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } + + bytesConsumed = consumed; + return ret; + } + + private static bool TryReadPrimitiveOctetStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out int bytesConsumed) + { + Asn1Tag actualTag = ReadTagAndLength(source, ruleSet, out contentLength, out headerLength); + CheckExpectedTag(actualTag, expectedTag, universalTagNumber); + + // Ensure the length makes sense. + ReadOnlySpan encodedValue = Slice(source, headerLength, contentLength); + + if (actualTag.IsConstructed) + { + if (ruleSet == AsnEncodingRules.DER) + { + throw new AsnContentException(SR.ContentException_InvalidUnderDer_TryBerOrCer); + } + + contents = default; + bytesConsumed = 0; + return false; + } + + Debug.Assert(contentLength.HasValue); + + if (ruleSet == AsnEncodingRules.CER && encodedValue.Length > MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); + } + + contents = encodedValue; + bytesConsumed = headerLength + encodedValue.Length; + return true; + } + + /// + /// Attempts to get an Octet String value from with a specified tag under + /// the specified encoding rules, if the value is contained in a single (primitive) encoding. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Octet String. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// if the Octet String value has a primitive encoding; + /// otherwise, . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public static bool TryReadPrimitiveOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out ReadOnlySpan value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + contentLength: out _, + headerLength: out _, + out value, + out bytesConsumed); + } + + private static int CountConstructedOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + bool isIndefinite) + { + int contentLength = CopyConstructedOctetString( + source, + ruleSet, + Span.Empty, + false, + isIndefinite, + out _); + + // T-REC-X.690-201508 sec 9.2 + if (ruleSet == AsnEncodingRules.CER && contentLength <= MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + return contentLength; + } + + private static void CopyConstructedOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + bool isIndefinite, + out int bytesRead, + out int bytesWritten) + { + bytesWritten = CopyConstructedOctetString( + source, + ruleSet, + destination, + true, + isIndefinite, + out bytesRead); + } + + private static int CopyConstructedOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + bool write, + bool isIndefinite, + out int bytesRead) + { + bytesRead = 0; + int lastSegmentLength = MaxCERSegmentSize; + + ReadOnlySpan cur = source; + Stack<(int Offset, int Length, bool IsIndefinite, int BytesRead)>? readerStack = null; + int totalLength = 0; + Asn1Tag tag = Asn1Tag.ConstructedBitString; + Span curDest = destination; + + while (true) + { + while (!cur.IsEmpty) + { + tag = ReadTagAndLength(cur, ruleSet, out int? length, out int headerLength); + + if (tag == Asn1Tag.PrimitiveOctetString) + { + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + Debug.Assert(length != null); + + // The call to Slice here sanity checks the data bounds, length.Value is not + // reliable unless this call has succeeded. + ReadOnlySpan contents = Slice(cur, headerLength, length.Value); + + int localLen = headerLength + contents.Length; + cur = cur.Slice(localLen); + + bytesRead += localLen; + totalLength += contents.Length; + lastSegmentLength = contents.Length; + + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength > MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (write) + { + contents.CopyTo(curDest); + curDest = curDest.Slice(contents.Length); + } + } + else if (tag == Asn1Tag.EndOfContents && isIndefinite) + { + ValidateEndOfContents(tag, length, headerLength); + + bytesRead += headerLength; + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + cur = topSpan.Slice(bytesRead); + + bytesRead += pushedBytesRead; + isIndefinite = wasIndefinite; + } + else + { + // We have matched the EndOfContents that brought us here. + break; + } + } + else if (tag == Asn1Tag.ConstructedOctetString) + { + if (ruleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (readerStack == null) + { + readerStack = new Stack<(int, int, bool, int)>(); + } + + if (!source.Overlaps(cur, out int curOffset)) + { + Debug.Fail("Non-overlapping data encountered..."); + throw new AsnContentException(); + } + + readerStack.Push((curOffset, cur.Length, isIndefinite, bytesRead)); + + cur = Slice(cur, headerLength, length); + bytesRead = headerLength; + isIndefinite = (length == null); + } + else + { + // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) + throw new AsnContentException(); + } + } + + if (isIndefinite && tag != Asn1Tag.EndOfContents) + { + throw new AsnContentException(); + } + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + + cur = topSpan.Slice(bytesRead); + + isIndefinite = wasIndefinite; + bytesRead += pushedBytesRead; + } + else + { + return totalLength; + } + } + } + + private static bool TryCopyConstructedOctetStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span dest, + bool isIndefinite, + out int bytesRead, + out int bytesWritten) + { + bytesRead = 0; + + int contentLength = CountConstructedOctetString(source, ruleSet, isIndefinite); + + if (dest.Length < contentLength) + { + bytesWritten = 0; + return false; + } + + CopyConstructedOctetString(source, ruleSet, dest, isIndefinite, out bytesRead, out bytesWritten); + + Debug.Assert(bytesWritten == contentLength); + return true; + } + + private static ReadOnlySpan GetOctetStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + out int bytesConsumed, + ref byte[]? rented, + Span tmpSpace = default) + { + Debug.Assert(rented == null); + + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out bytesConsumed)) + { + return contents; + } + + // If we get here, the tag was appropriate, but the encoding was constructed. + + // Guaranteed long enough + contents = source.Slice(headerLength); + int tooBig = contentLength ?? SeekEndOfContents(contents, ruleSet); + + if (tmpSpace.Length > 0 && tooBig > tmpSpace.Length) + { + bool isIndefinite = contentLength == null; + tooBig = CountConstructedOctetString(contents, ruleSet, isIndefinite); + } + + if (tooBig > tmpSpace.Length) + { + rented = CryptoPool.Rent(tooBig); + tmpSpace = rented; + } + + if (TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, + tmpSpace, + contentLength == null, + out int bytesRead, + out int bytesWritten)) + { + bytesConsumed = headerLength + bytesRead; + return tmpSpace.Slice(0, bytesWritten); + } + + Debug.Fail("TryCopyConstructedOctetStringContents failed with a pre-allocated buffer"); + throw new AsnContentException(); + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as an OCTET STRING with a specified tag, copying the value + /// into a provided destination buffer. + /// + /// The buffer in which to write. + /// + /// On success, receives the number of bytes written to . + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public bool TryReadOctetString( + Span destination, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadOctetString( + _data.Span, + destination, + RuleSet, + out int bytesConsumed, + out bytesWritten, + expectedTag); + + if (ret) + { + _data = _data.Slice(bytesConsumed); + } + + return ret; + } + + /// + /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value + /// in a byte array. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// A copy of the value in a newly allocated, precisely sized, array. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public byte[] ReadOctetString(Asn1Tag? expectedTag = null) + { + byte[] ret = AsnDecoder.ReadOctetString(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Attempts to read the next value as an OCTET STRING with a specified tag, returning the contents + /// as a over the original data. + /// + /// The tag to check for before reading. + /// + /// On success, receives a over the original data + /// corresponding to the value of the OCTET STRING. + /// + /// + /// and advances the reader if the OCTET STRING value had a primitive encoding, + /// and does not advance the reader if it had a constructed encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public bool TryReadPrimitiveOctetString(out ReadOnlyMemory contents, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadPrimitiveOctetString( + _data.Span, + RuleSet, + out ReadOnlySpan span, + out int consumed, + expectedTag); + + if (ret) + { + contents = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + contents = default; + } + + return ret; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs similarity index 56% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs index 75692397e97a80..5f3b3b39275c50 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs @@ -2,93 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; using System.Numerics; +using System.Security.Cryptography; using System.Text; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value in a dotted decimal format string. + /// Reads an Object Identifier value from with a specified tag under + /// the specified encoding rules. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 6). + /// + /// + /// The decoded object identifier, in dotted-decimal notation. + /// + /// + /// is not defined. /// - public string ReadObjectIdentifierAsString() => - ReadObjectIdentifierAsString(Asn1Tag.ObjectIdentifier); - - /// - /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning - /// the value in a dotted decimal format string. - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public string ReadObjectIdentifierAsString(Asn1Tag expectedTag) - { - string oidValue = ReadObjectIdentifierAsString(expectedTag, out int bytesRead); - - _data = _data.Slice(bytesRead); - - return oidValue; - } - - /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value as an . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public Oid ReadObjectIdentifier() => - ReadObjectIdentifier(Asn1Tag.ObjectIdentifier); - - /// - /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning - /// the value as an . - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Oid ReadObjectIdentifier(Asn1Tag expectedTag) + public static string ReadObjectIdentifier( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - string oidValue = ReadObjectIdentifierAsString(expectedTag, out int bytesRead); - // Specifying null for friendly name makes the lookup deferred until first read - // of the Oid.FriendlyName property. - Oid oid = new Oid(oidValue, null); - - // Don't slice until the return object has been created. - _data = _data.Slice(bytesRead); - - return oid; + // TODO: Inline this call when it won't cause a PR/diff problem. + return ReadObjectIdentifier(source, ruleSet, expectedTag, out bytesConsumed); } private static void ReadSubIdentifier( @@ -102,7 +69,7 @@ private static void ReadSubIdentifier( // T-REC-X.690-201508 sec 8.19.2 (last sentence) if (source[0] == 0x80) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } // First, see how long the segment is @@ -123,7 +90,7 @@ private static void ReadSubIdentifier( if (end < 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } bytesRead = end + 1; @@ -206,20 +173,26 @@ private static void ReadSubIdentifier( CryptoPool.Return(tmpBytes, bytesWritten); } - private string ReadObjectIdentifierAsString(Asn1Tag expectedTag, out int totalBytesRead) + private static string ReadObjectIdentifier( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag? expectedTag, + out int totalBytesRead) { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.ObjectIdentifier); - // T-REC-X.690-201508 sec 8.19.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag ?? Asn1Tag.ObjectIdentifier, + UniversalTagNumber.ObjectIdentifier, + out int consumed); + // T-REC-X.690-201508 sec 8.19.2 says the minimum length is 1 - if (tag.IsConstructed || length < 1) + if (contents.Length < 1) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } - ReadOnlySpan contents = Slice(_data, headerLength, length!.Value); - // Each byte can contribute a 3 digit value and a '.' (e.g. "126."), but usually // they convey one digit and a separator. // @@ -306,83 +279,44 @@ private string ReadObjectIdentifierAsString(Asn1Tag expectedTag, out int totalBy contents = contents.Slice(bytesRead); } - totalBytesRead = headerLength + length.Value; + totalBytesRead = consumed; return builder.ToString(); } } - internal partial class AsnReader + public partial class AsnReader { - /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value in a dotted decimal format string. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public string ReadObjectIdentifierAsString() => - ReadObjectIdentifierAsString(Asn1Tag.ObjectIdentifier); - /// /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning /// the value in a dotted decimal format string. /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// The tag to check for before reading, or for the default tag (Universal 6). + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public string ReadObjectIdentifierAsString(Asn1Tag expectedTag) + public string ReadObjectIdentifier(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - string oidValue = valueReader.ReadObjectIdentifierAsString(expectedTag); - valueReader.MatchSlice(ref _data); - return oidValue; - } + string oidValue = + AsnDecoder.ReadObjectIdentifier(_data.Span, RuleSet, out int consumed, expectedTag); - /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value as an . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public Oid ReadObjectIdentifier() => - ReadObjectIdentifier(Asn1Tag.ObjectIdentifier); - - /// - /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning - /// the value as an . - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Oid ReadObjectIdentifier(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - Oid oid = valueReader.ReadObjectIdentifier(expectedTag); - valueReader.MatchSlice(ref _data); - return oid; + _data = _data.Slice(consumed); + return oidValue; } } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs new file mode 100644 index 00000000000000..dde93f9348a99b --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Sequence or Sequence-Of value from with a specified tag + /// under the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 16). + /// + /// + /// The nested content is not evaluated by this method, except for minimal processing to + /// determine the location of an end-of-contents marker. + /// Therefore, the contents may contain data which is not valid under the current encoding rules. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static void ReadSequence( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + Asn1Tag? expectedTag = default) + { + Asn1Tag tag = ReadTagAndLength(source, ruleSet, out int? length, out int headerLength); + CheckExpectedTag(tag, expectedTag ?? Asn1Tag.Sequence, UniversalTagNumber.Sequence); + + // T-REC-X.690-201508 sec 8.9.1 + // T-REC-X.690-201508 sec 8.10.1 + if (!tag.IsConstructed) + { + throw new AsnContentException( + SR.Format( + SR.ContentException_ConstructedEncodingRequired, + UniversalTagNumber.Sequence)); + } + + if (length.HasValue) + { + if (length.Value + headerLength > source.Length) + { + throw GetValidityException(LengthValidity.LengthExceedsInput); + } + + contentLength = length.Value; + contentOffset = headerLength; + bytesConsumed = contentLength + headerLength; + } + else + { + int len = SeekEndOfContents(source.Slice(headerLength), ruleSet); + + contentLength = len; + contentOffset = headerLength; + bytesConsumed = len + headerLength + EndOfContentsEncodedLength; + } + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a SEQUENCE or SEQUENCE-OF with the specified tag + /// and returns the result as a new reader positioned at the first + /// value in the sequence (or with == ). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 16). + /// + /// + /// A new reader positioned at the first + /// value in the sequence (or with == ). + /// + /// + /// the nested content is not evaluated by this method, and may contain data + /// which is not valid under the current encoding rules. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public AsnReader ReadSequence(Asn1Tag? expectedTag = null) + { + AsnDecoder.ReadSequence( + _data.Span, + RuleSet, + out int contentStart, + out int contentLength, + out int bytesConsumed, + expectedTag); + + AsnReader ret = CloneAtSlice(contentStart, contentLength); + _data = _data.Slice(bytesConsumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs new file mode 100644 index 00000000000000..ee91de368eea31 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs @@ -0,0 +1,227 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Set-Of value from with a specified tag + /// under the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// to always accept the data in the order it is presented, + /// to verify that the data is sorted correctly when the + /// encoding rules say sorting was required (CER and DER). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// + /// + /// The nested content is not evaluated by this method, except for minimal processing to + /// determine the location of an end-of-contents marker or verification of the content + /// sort order. + /// Therefore, the contents may contain data which is not valid under the current encoding rules. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static void ReadSetOf( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + bool skipSortOrderValidation = false, + Asn1Tag? expectedTag = null) + { + Asn1Tag tag = ReadTagAndLength(source, ruleSet, out int? length, out int headerLength); + CheckExpectedTag(tag, expectedTag ?? Asn1Tag.SetOf, UniversalTagNumber.SetOf); + + // T-REC-X.690-201508 sec 8.12.1 + if (!tag.IsConstructed) + { + throw new AsnContentException( + SR.Format( + SR.ContentException_ConstructedEncodingRequired, + UniversalTagNumber.SetOf)); + } + + int suffix; + ReadOnlySpan contents; + + if (length.HasValue) + { + suffix = 0; + contents = Slice(source, headerLength, length.Value); + } + else + { + int actualLength = SeekEndOfContents(source.Slice(headerLength), ruleSet); + contents = Slice(source, headerLength, actualLength); + suffix = EndOfContentsEncodedLength; + } + + if (!skipSortOrderValidation) + { + // T-REC-X.690-201508 sec 11.6 + // BER data is not required to be sorted. + if (ruleSet == AsnEncodingRules.DER || + ruleSet == AsnEncodingRules.CER) + { + ReadOnlySpan remaining = contents; + ReadOnlySpan previous = default; + + while (!remaining.IsEmpty) + { + ReadEncodedValue(remaining, ruleSet, out _, out _, out int consumed); + + ReadOnlySpan current = remaining.Slice(0, consumed); + remaining = remaining.Slice(consumed); + + if (SetOfValueComparer.Compare(current, previous) < 0) + { + throw new AsnContentException(SR.ContentException_SetOfNotSorted); + } + + previous = current; + } + } + } + + contentOffset = headerLength; + contentLength = contents.Length; + bytesConsumed = headerLength + contents.Length + suffix; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a SET-OF with the specified tag + /// and returns the result as a new reader positioned at the first + /// value in the set-of (or with == ), + /// using the value + /// from the constructor (default ). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// + /// + /// A new reader positioned at the first + /// value in the set-of (or with == ). + /// + /// + /// the nested content is not evaluated by this method (aside from sort order, when + /// required), and may contain data which is not valid under the current encoding rules. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public AsnReader ReadSetOf(Asn1Tag? expectedTag = null) + { + return ReadSetOf(_options.SkipSetSortOrderVerification, expectedTag); + } + + /// + /// Reads the next value as a SET-OF with the specified tag + /// and returns the result as a new reader positioned at the first + /// value in the set-of (or with == ). + /// + /// + /// to always accept the data in the order it is presented, + /// to verify that the data is sorted correctly when the + /// encoding rules say sorting was required (CER and DER). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// + /// + /// A new reader positioned at the first + /// value in the set-of (or with == ). + /// + /// + /// the nested content is not evaluated by this method (aside from sort order, when + /// required), and may contain data which is not valid under the current encoding rules. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public AsnReader ReadSetOf(bool skipSortOrderValidation, Asn1Tag? expectedTag = null) + { + AsnDecoder.ReadSetOf( + _data.Span, + RuleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + skipSortOrderValidation, + expectedTag); + + AsnReader ret = CloneAtSlice(contentOffset, contentLength); + _data = _data.Slice(bytesConsumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs new file mode 100644 index 00000000000000..1ac341a83634ba --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs @@ -0,0 +1,784 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Attempts to get an unprocessed character string value from with a + /// specified tag under the specified encoding rules, if the value is contained in a single + /// (primitive) encoding. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// The tag to check for before reading. + /// + /// + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Bit String. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// if the character string value has a primitive encoding; + /// otherwise, . + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// + public static bool TryReadPrimitiveCharacterStringBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + out ReadOnlySpan value, + out int bytesConsumed) + { + // This doesn't matter, except for universal tags. It's eventually used to check that + // we're not expecting the wrong universal tag; but we'll remove the need for that by + // IsCharacterStringEncodingType. + UniversalTagNumber universalTagNumber = UniversalTagNumber.IA5String; + + if (expectedTag.TagClass == TagClass.Universal) + { + universalTagNumber = (UniversalTagNumber)expectedTag.TagValue; + + if (!IsCharacterStringEncodingType(universalTagNumber)) + { + throw new ArgumentException(SR.Argument_Tag_NotCharacterString, nameof(expectedTag)); + } + } + + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + return TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + contentLength: out _, + headerLength: out _, + out value, + out bytesConsumed); + } + + /// + /// Attempts to read a character string value from with a + /// specified tag under the specified encoding rules, + /// copying the unprocessed bytes into the provided destination buffer. + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// The tag to check for before reading. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// On success, receives the number of bytes written to . + /// + /// + /// if is large enough to receive the + /// value of the unprocessed character string; + /// otherwise, . + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// -or- + /// + /// overlaps . + /// + /// + /// + /// + public static bool TryReadCharacterStringBytes( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + out int bytesConsumed, + out int bytesWritten) + { + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } + + // This doesn't matter, except for universal tags. It's eventually used to check that + // we're not expecting the wrong universal tag; but we'll remove the need for that by + // IsCharacterStringEncodingType. + UniversalTagNumber universalTagNumber = UniversalTagNumber.IA5String; + + if (expectedTag.TagClass == TagClass.Universal) + { + universalTagNumber = (UniversalTagNumber)expectedTag.TagValue; + + if (!IsCharacterStringEncodingType(universalTagNumber)) + { + throw new ArgumentException(SR.Argument_Tag_NotCharacterString, nameof(expectedTag)); + } + } + + return TryReadCharacterStringBytesCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + destination, + out bytesConsumed, + out bytesWritten); + } + + /// + /// Reads a character string value from with a specified tag under + /// the specified encoding rules, copying the decoded string into a a provided destination buffer. + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// + /// One of the enumeration values which represents the value type to process. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of chars written to . + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// is not defined. + /// + /// -or- + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + public static bool TryReadCharacterString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + UniversalTagNumber encodingType, + out int bytesConsumed, + out int charsWritten, + Asn1Tag? expectedTag = null) + { + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + + return TryReadCharacterStringCore( + source, + ruleSet, + expectedTag ?? new Asn1Tag(encodingType), + encodingType, + encoding, + destination, + out bytesConsumed, + out charsWritten); + } + + /// + /// Reads the next value as character string with the specified tag and + /// encoding type, returning the decoded string. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// One of the enumeration values which represents the value type to process. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// The decoded value. + /// + /// + /// is not defined. + /// + /// -or- + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + /// + public static string ReadCharacterString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + UniversalTagNumber encodingType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + + return ReadCharacterStringCore( + source, + ruleSet, + expectedTag ?? new Asn1Tag(encodingType), + encodingType, + encoding, + out bytesConsumed); + } + + // T-REC-X.690-201508 sec 8.23 + private static bool TryReadCharacterStringBytesCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + Span destination, + out int bytesConsumed, + out int bytesWritten) + { + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out int consumed)) + { + if (contents.Length > destination.Length) + { + bytesWritten = 0; + bytesConsumed = 0; + return false; + } + + contents.CopyTo(destination); + bytesWritten = contents.Length; + bytesConsumed = consumed; + return true; + } + + bool copied = TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, + destination, + contentLength == null, + out int bytesRead, + out bytesWritten); + + if (copied) + { + bytesConsumed = headerLength + bytesRead; + } + else + { + bytesConsumed = 0; + } + + return copied; + } + + private static unsafe bool TryReadCharacterStringCore( + ReadOnlySpan source, + Span destination, + Text.Encoding encoding, + out int charsWritten) + { + if (source.Length == 0) + { + charsWritten = 0; + return true; + } + + fixed (byte* bytePtr = &MemoryMarshal.GetReference(source)) + fixed (char* charPtr = &MemoryMarshal.GetReference(destination)) + { + try + { + int charCount = encoding.GetCharCount(bytePtr, source.Length); + + if (charCount > destination.Length) + { + charsWritten = 0; + return false; + } + + charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length); + Debug.Assert(charCount == charsWritten); + } + catch (DecoderFallbackException e) + { + throw new AsnContentException(SR.ContentException_DefaultMessage, e); + } + + return true; + } + } + + private static string ReadCharacterStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + Text.Encoding encoding, + out int bytesConsumed) + { + byte[]? rented = null; + + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int bytesRead, + ref rented); + + string str; + + if (contents.Length == 0) + { + str = string.Empty; + } + else + { + unsafe + { + fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents)) + { + try + { + str = encoding.GetString(bytePtr, contents.Length); + } + catch (DecoderFallbackException e) + { + throw new AsnContentException(SR.ContentException_DefaultMessage, e); + } + } + } + } + + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } + + bytesConsumed = bytesRead; + return str; + } + + private static bool TryReadCharacterStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + Text.Encoding encoding, + Span destination, + out int bytesConsumed, + out int charsWritten) + { + byte[]? rented = null; + + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int bytesRead, + ref rented); + + bool copied = TryReadCharacterStringCore( + contents, + destination, + encoding, + out charsWritten); + + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } + + if (copied) + { + bytesConsumed = bytesRead; + } + else + { + bytesConsumed = 0; + } + + return copied; + } + + private static bool IsCharacterStringEncodingType(UniversalTagNumber encodingType) + { + // T-REC-X.680-201508 sec 41 + switch (encodingType) + { + case UniversalTagNumber.BMPString: + case UniversalTagNumber.GeneralString: + case UniversalTagNumber.GraphicString: + case UniversalTagNumber.IA5String: + case UniversalTagNumber.ISO646String: + case UniversalTagNumber.NumericString: + case UniversalTagNumber.PrintableString: + case UniversalTagNumber.TeletexString: + // T61String is an alias for TeletexString (already listed) + case UniversalTagNumber.UniversalString: + case UniversalTagNumber.UTF8String: + case UniversalTagNumber.VideotexString: + // VisibleString is an alias for ISO646String (already listed) + return true; + } + + return false; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a character with a specified tag, returning the contents + /// as an unprocessed over the original data. + /// + /// The tag to check for before reading. + /// + /// On success, receives a over the original data + /// corresponding to the value of the character string. + /// + /// + /// and advances the reader if the character string value had a primitive encoding, + /// and does not advance the reader if it had a constructed encoding. + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// + public bool TryReadPrimitiveCharacterStringBytes( + Asn1Tag expectedTag, + out ReadOnlyMemory contents) + { + bool ret = AsnDecoder.TryReadPrimitiveCharacterStringBytes( + _data.Span, + RuleSet, + expectedTag, + out ReadOnlySpan span, + out int consumed); + + if (ret) + { + contents = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + contents = default; + } + + return ret; + } + + /// + /// Reads the next value as character string with the specified tag, + /// copying the unprocessed bytes into a provided destination buffer. + /// + /// The buffer in which to write. + /// The tag to check for before reading. + /// + /// On success, receives the number of bytes written to . + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// + /// + /// + public bool TryReadCharacterStringBytes( + Span destination, + Asn1Tag expectedTag, + out int bytesWritten) + { + bool ret = AsnDecoder.TryReadCharacterStringBytes( + _data.Span, + destination, + RuleSet, + expectedTag, + out int consumed, + out bytesWritten); + + if (ret) + { + _data = _data.Slice(consumed); + } + + return ret; + } + + /// + /// Reads the next value as character string with the specified tag and + /// encoding type, copying the decoded value into a provided destination buffer. + /// + /// + /// One of the enumeration values representing the value type to process. + /// + /// The buffer in which to write. + /// + /// On success, receives the number of chars written to . + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + /// + public bool TryReadCharacterString( + Span destination, + UniversalTagNumber encodingType, + out int charsWritten, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadCharacterString( + _data.Span, + destination, + RuleSet, + encodingType, + out int consumed, + out charsWritten, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as character string with the specified tag and + /// encoding type, returning the decoded value as a string. + /// + /// + /// One of the enumeration values representing the value type to process. + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// The decoded value. + /// + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + /// + public string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = null) + { + string ret = AsnDecoder.ReadCharacterString( + _data.Span, + RuleSet, + encodingType, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs similarity index 60% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs index 4760ebf6944707..adf1781ace9d38 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs @@ -2,59 +2,70 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Diagnostics; +using System.Security.Cryptography; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a UTCTime with tag UNIVERSAL 23. + /// Reads a UtcTime value from with a specified tag under + /// the specified encoding rules. /// - /// - /// The largest year to represent with this value. - /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// a DateTimeOffset representing the value encoded in the UTCTime. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public DateTimeOffset ReadUtcTime(int twoDigitYearMax = 2049) => - ReadUtcTime(Asn1Tag.UtcTime, twoDigitYearMax); - - /// - /// Reads the next value as a UTCTime with a specified tag. - /// - /// The tag to check for before reading. /// /// The largest year to represent with this value. /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. /// + /// + /// The tag to check for before reading, or for the default tag (Universal 24). + /// /// - /// a DateTimeOffset representing the value encoded in the UTCTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// is not defined. + /// + /// -or- + /// + /// is not in the range [99, 9999]. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// - public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 2049) + public static DateTimeOffset ReadUtcTime( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + int twoDigitYearMax = 2049, + Asn1Tag? expectedTag = null) { + if (twoDigitYearMax < 1 || twoDigitYearMax > 9999) + { + throw new ArgumentOutOfRangeException(nameof(twoDigitYearMax)); + } + // T-REC-X.680-201510 sec 47.3 says it is IMPLICIT VisibleString, which means // that BER is allowed to do complex constructed forms. @@ -69,25 +80,20 @@ public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 204 // CER and DER are restricted to YYMMDDhhmmssZ // T-REC-X.690-201510 sec 11.8 + // The longest format is 17 bytes. + Span tmpSpace = stackalloc byte[17]; byte[]? rented = null; - Span tmpSpace; - - unsafe - { - // The longest format is 17 bytes. - const int StackBufSize = 17; - byte* stackBuf = stackalloc byte[StackBufSize]; - tmpSpace = new Span(stackBuf, StackBufSize); - } ReadOnlySpan contents = GetOctetStringContents( - expectedTag, + source, + ruleSet, + expectedTag ?? Asn1Tag.UtcTime, UniversalTagNumber.UtcTime, out int bytesRead, ref rented, tmpSpace); - DateTimeOffset value = ParseUtcTime(contents, twoDigitYearMax); + DateTimeOffset value = ParseUtcTime(contents, ruleSet, twoDigitYearMax); if (rented != null) { @@ -95,11 +101,14 @@ public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 204 CryptoPool.Return(rented, contents.Length); } - _data = _data.Slice(bytesRead); + bytesConsumed = bytesRead; return value; } - private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDigitYearMax) + private static DateTimeOffset ParseUtcTime( + ReadOnlySpan contentOctets, + AsnEncodingRules ruleSet, + int twoDigitYearMax) { // The full allowed formats (T-REC-X.680-201510 sec 47.3) // a) YYMMDD @@ -121,11 +130,11 @@ private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDig const int HasSecondsOffset = 17; // T-REC-X.690-201510 sec 11.8 - if (RuleSet == AsnEncodingRules.DER || RuleSet == AsnEncodingRules.CER) + if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) { if (contentOctets.Length != HasSecondsZulu) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } } @@ -135,7 +144,7 @@ private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDig contentOctets.Length > HasSecondsOffset || (contentOctets.Length & 1) != 1) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } ReadOnlySpan contents = contentOctets; @@ -161,7 +170,7 @@ private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDig { if (contents[0] != 'Z') { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } } else @@ -176,7 +185,7 @@ private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDig } else if (contents[0] != '+') { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } contents = contents.Slice(1); @@ -190,7 +199,7 @@ private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDig // is bound to [00,14] by DateTimeOffset, so no additional check is required here. if (offsetMinute > 59) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } TimeSpan offset = new TimeSpan(offsetHour, offsetMinute, 0); @@ -225,61 +234,92 @@ private DateTimeOffset ParseUtcTime(ReadOnlySpan contentOctets, int twoDig } catch (Exception e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + throw new AsnContentException(SR.ContentException_DefaultMessage, e); } } } - internal partial class AsnReader + public partial class AsnReader { /// - /// Reads the next value as a UTCTime with tag UNIVERSAL 23. + /// Reads the next value as a UTCTime with a specified tag using the + /// value from options passed to + /// the constructor (with a default of 2049). /// - /// - /// The largest year to represent with this value. - /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. + /// + /// The tag to check for before reading, or for the default tag (Universal 23). /// /// - /// a DateTimeOffset representing the value encoded in the UTCTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// - /// - /// - public DateTimeOffset ReadUtcTime(int twoDigitYearMax = 2049) => - ReadUtcTime(Asn1Tag.UtcTime, twoDigitYearMax); + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public DateTimeOffset ReadUtcTime(Asn1Tag? expectedTag = null) + { + DateTimeOffset ret = AsnDecoder.ReadUtcTime( + _data.Span, + RuleSet, + out int consumed, + _options.UtcTimeTwoDigitYearMax, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } /// /// Reads the next value as a UTCTime with a specified tag. /// - /// The tag to check for before reading. /// /// The largest year to represent with this value. - /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 23). /// /// - /// a DateTimeOffset representing the value encoded in the UTCTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// + /// /// - public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 2049) + public DateTimeOffset ReadUtcTime(int twoDigitYearMax, Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - DateTimeOffset ret = valueReader.ReadUtcTime(expectedTag, twoDigitYearMax); - valueReader.MatchSlice(ref _data); + DateTimeOffset ret = + AsnDecoder.ReadUtcTime(_data.Span, RuleSet, out int consumed, twoDigitYearMax, expectedTag); + + _data = _data.Slice(consumed); return ret; } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs new file mode 100644 index 00000000000000..6a3f34bb26a439 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs @@ -0,0 +1,803 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Text; +using System.Diagnostics; + +namespace System.Formats.Asn1 +{ + /// + /// Provides stateless methods for decoding BER-, CER-, or DER-encoded ASN.1 data. + /// + public static partial class AsnDecoder + { + // T-REC-X.690-201508 sec 9.2 + internal const int MaxCERSegmentSize = 1000; + + // T-REC-X.690-201508 sec 8.1.5 says only 0000 is legal. + internal const int EndOfContentsEncodedLength = 2; + + /// + /// Attempts locate the contents range for the encoded value at the beginning of the + /// buffer using the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the tag identifying the content. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// if represents a valid structural + /// encoding for the specified encoding rules; otherwise, . + /// + /// + /// + /// This method performs very little validation on the contents. + /// If the encoded value uses a definite length, the contents are not inspected at all. + /// If the encoded value uses an indefinite length, the contents are only inspected + /// as necessary to determine the location of the relevant end-of-contents marker. + /// + /// + /// When the encoded value uses an indefinite length, the + /// value will be larger than the sum of and + /// to account for the end-of-contents marker. + /// + /// + /// + /// is not defined. + /// + public static bool TryReadEncodedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out Asn1Tag tag, + out int contentOffset, + out int contentLength, + out int bytesConsumed) + { + CheckEncodingRules(ruleSet); + + if (Asn1Tag.TryDecode(source, out Asn1Tag localTag, out int tagLength) && + TryReadLength(source.Slice(tagLength), ruleSet, out int? encodedLength, out int lengthLength)) + { + int headerLength = tagLength + lengthLength; + + LengthValidity validity = ValidateLength( + source.Slice(headerLength), + ruleSet, + localTag, + encodedLength, + out int len, + out int consumed); + + if (validity == LengthValidity.Valid) + { + tag = localTag; + contentOffset = headerLength; + contentLength = len; + bytesConsumed = headerLength + consumed; + return true; + } + } + + tag = default; + contentOffset = contentLength = bytesConsumed = 0; + return false; + } + + /// + /// Locates the contents range for the encoded value at the beginning of the + /// buffer using the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag identifying the content. + /// + /// + /// + /// This method performs very little validation on the contents. + /// If the encoded value uses a definite length, the contents are not inspected at all. + /// If the encoded value uses an indefinite length, the contents are only inspected + /// as necessary to determine the location of the relevant end-of-contents marker. + /// + /// + /// When the encoded value uses an indefinite length, the + /// value will be larger than the sum of and + /// to account for the end-of-contents marker. + /// + /// + /// + /// is not defined. + /// + /// + /// does not represent a value encoded under the specified + /// encoding rules. + /// + public static Asn1Tag ReadEncodedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed) + { + CheckEncodingRules(ruleSet); + + Asn1Tag tag = Asn1Tag.Decode(source, out int tagLength); + int? encodedLength = ReadLength(source.Slice(tagLength), ruleSet, out int lengthLength); + int headerLength = tagLength + lengthLength; + + LengthValidity validity = ValidateLength( + source.Slice(headerLength), + ruleSet, + tag, + encodedLength, + out int len, + out int consumed); + + if (validity == LengthValidity.Valid) + { + contentOffset = headerLength; + contentLength = len; + bytesConsumed = headerLength + consumed; + return tag; + } + + throw GetValidityException(validity); + } + + private static ReadOnlySpan GetPrimitiveContentSpan( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out int bytesConsumed) + { + CheckEncodingRules(ruleSet); + + Asn1Tag localTag = Asn1Tag.Decode(source, out int tagLength); + int? encodedLength = ReadLength(source.Slice(tagLength), ruleSet, out int lengthLength); + int headerLength = tagLength + lengthLength; + + // Get caller(-of-my-caller) errors out of the way, first. + CheckExpectedTag(localTag, expectedTag, tagNumber); + + // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form, + // and the caller says they only want primitive values. + if (localTag.IsConstructed) + { + throw new AsnContentException( + SR.Format(SR.ContentException_PrimitiveEncodingRequired, tagNumber)); + } + + if (encodedLength == null) + { + throw new AsnContentException(); + } + + ReadOnlySpan ret = Slice(source, headerLength, encodedLength.Value); + bytesConsumed = headerLength + ret.Length; + return ret; + } + + private static bool TryReadLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? length, + out int bytesRead) + { + return DecodeLength(source, ruleSet, out length, out bytesRead) == LengthDecodeStatus.Success; + } + + private static int? ReadLength(ReadOnlySpan source, AsnEncodingRules ruleSet, out int bytesConsumed) + { + LengthDecodeStatus status = DecodeLength(source, ruleSet, out int? length, out bytesConsumed); + + switch (status) + { + case LengthDecodeStatus.Success: + return length; + case LengthDecodeStatus.LengthTooBig: + throw new AsnContentException(SR.ContentException_LengthTooBig); + case LengthDecodeStatus.LaxEncodingProhibited: + case LengthDecodeStatus.DerIndefinite: + throw new AsnContentException(SR.ContentException_LengthRuleSetConstraint); + case LengthDecodeStatus.NeedMoreData: + case LengthDecodeStatus.ReservedValue: + throw new AsnContentException(); + default: + Debug.Fail($"No handler is present for status {status}."); + goto case LengthDecodeStatus.NeedMoreData; + } + } + + private static LengthDecodeStatus DecodeLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? length, + out int bytesRead) + { + length = null; + bytesRead = 0; + + AssertEncodingRules(ruleSet); + + if (source.IsEmpty) + { + return LengthDecodeStatus.NeedMoreData; + } + + // T-REC-X.690-201508 sec 8.1.3 + + byte lengthOrLengthLength = source[bytesRead]; + bytesRead++; + const byte MultiByteMarker = 0x80; + + // 0x00-0x7F are direct length values. + // 0x80 is BER/CER indefinite length. + // 0x81-0xFE says that the length takes the next 1-126 bytes. + // 0xFF is forbidden. + if (lengthOrLengthLength == MultiByteMarker) + { + // T-REC-X.690-201508 sec 10.1 (DER: Length forms) + if (ruleSet == AsnEncodingRules.DER) + { + bytesRead = 0; + return LengthDecodeStatus.DerIndefinite; + } + + // Null length == indefinite. + return LengthDecodeStatus.Success; + } + + if (lengthOrLengthLength < MultiByteMarker) + { + length = lengthOrLengthLength; + return LengthDecodeStatus.Success; + } + + if (lengthOrLengthLength == 0xFF) + { + bytesRead = 0; + return LengthDecodeStatus.ReservedValue; + } + + byte lengthLength = (byte)(lengthOrLengthLength & ~MultiByteMarker); + + // +1 for lengthOrLengthLength + if (lengthLength + 1 > source.Length) + { + bytesRead = 0; + return LengthDecodeStatus.NeedMoreData; + } + + // T-REC-X.690-201508 sec 9.1 (CER: Length forms) + // T-REC-X.690-201508 sec 10.1 (DER: Length forms) + bool minimalRepresentation = + ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER; + + // The ITU-T specifications technically allow lengths up to ((2^128) - 1), but + // since Span's length is a signed Int32 we're limited to identifying memory + // that is within ((2^31) - 1) bytes of the tag start. + if (minimalRepresentation && lengthLength > sizeof(int)) + { + bytesRead = 0; + return LengthDecodeStatus.LengthTooBig; + } + + uint parsedLength = 0; + + for (int i = 0; i < lengthLength; i++) + { + byte current = source[bytesRead]; + bytesRead++; + + if (parsedLength == 0) + { + if (minimalRepresentation && current == 0) + { + bytesRead = 0; + return LengthDecodeStatus.LaxEncodingProhibited; + } + + if (!minimalRepresentation && current != 0) + { + // Under BER rules we could have had padding zeros, so + // once the first data bits come in check that we fit within + // sizeof(int) due to Span bounds. + + if (lengthLength - i > sizeof(int)) + { + bytesRead = 0; + return LengthDecodeStatus.LengthTooBig; + } + } + } + + parsedLength <<= 8; + parsedLength |= current; + } + + // This value cannot be represented as a Span length. + if (parsedLength > int.MaxValue) + { + bytesRead = 0; + return LengthDecodeStatus.LengthTooBig; + } + + if (minimalRepresentation && parsedLength < MultiByteMarker) + { + bytesRead = 0; + return LengthDecodeStatus.LaxEncodingProhibited; + } + + Debug.Assert(bytesRead > 0); + length = (int)parsedLength; + return LengthDecodeStatus.Success; + } + + private static Asn1Tag ReadTagAndLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? contentsLength, + out int bytesRead) + { + Asn1Tag tag = Asn1Tag.Decode(source, out int tagBytesRead); + int? length = ReadLength(source.Slice(tagBytesRead), ruleSet, out int lengthBytesRead); + + int allBytesRead = tagBytesRead + lengthBytesRead; + + if (tag.IsConstructed) + { + // T-REC-X.690-201508 sec 9.1 (CER: Length forms) says constructed is always indefinite. + if (ruleSet == AsnEncodingRules.CER && length != null) + { + throw GetValidityException(LengthValidity.CerRequiresIndefinite); + } + } + else if (length == null) + { + // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form. + throw GetValidityException(LengthValidity.PrimitiveEncodingRequiresDefinite); + } + + bytesRead = allBytesRead; + contentsLength = length; + return tag; + } + + private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLength) + { + // T-REC-X.690-201508 sec 8.1.5 excludes the BER 8100 length form for 0. + if (tag.IsConstructed || length != 0 || headerLength != EndOfContentsEncodedLength) + { + throw new AsnContentException(); + } + } + + private static LengthValidity ValidateLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag localTag, + int? encodedLength, + out int actualLength, + out int bytesConsumed) + { + if (localTag.IsConstructed) + { + // T-REC-X.690-201508 sec 9.1 (CER: Length forms) says constructed is always indefinite. + if (ruleSet == AsnEncodingRules.CER && encodedLength != null) + { + actualLength = bytesConsumed = 0; + return LengthValidity.CerRequiresIndefinite; + } + } + else if (encodedLength == null) + { + // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form. + actualLength = bytesConsumed = 0; + return LengthValidity.PrimitiveEncodingRequiresDefinite; + } + + if (encodedLength != null) + { + int len = encodedLength.Value; + int totalLength = len; + + if (totalLength > source.Length) + { + actualLength = bytesConsumed = 0; + return LengthValidity.LengthExceedsInput; + } + + actualLength = len; + bytesConsumed = len; + return LengthValidity.Valid; + } + + // Assign actualLength first, so no assignments leak if SeekEndOfContents + // throws an exception. + actualLength = SeekEndOfContents(source, ruleSet); + bytesConsumed = actualLength + EndOfContentsEncodedLength; + return LengthValidity.Valid; + } + + private static AsnContentException GetValidityException(LengthValidity validity) + { + Debug.Assert(validity != LengthValidity.Valid); + + switch (validity) + { + case LengthValidity.CerRequiresIndefinite: + return new AsnContentException(SR.ContentException_CerRequiresIndefiniteLength); + case LengthValidity.LengthExceedsInput: + return new AsnContentException(SR.ContentException_LengthExceedsPayload); + case LengthValidity.PrimitiveEncodingRequiresDefinite: + return new AsnContentException(); + default: + Debug.Fail($"No handler for validity {validity}."); + goto case LengthValidity.PrimitiveEncodingRequiresDefinite; + } + } + + /// + /// Get the number of bytes between the start of and + /// the End-of-Contents marker + /// + private static int SeekEndOfContents(ReadOnlySpan source, AsnEncodingRules ruleSet) + { + ReadOnlySpan cur = source; + int totalLen = 0; + + // Our reader is bounded by int.MaxValue. + // The most aggressive data input would be a one-byte tag followed by + // indefinite length "ad infinitum", which would be half the input. + // So the depth marker can never overflow the signed integer space. + int depth = 1; + + while (!cur.IsEmpty) + { + Asn1Tag tag = ReadTagAndLength(cur, ruleSet, out int? length, out int bytesRead); + + if (tag == Asn1Tag.EndOfContents) + { + ValidateEndOfContents(tag, length, bytesRead); + + depth--; + + if (depth == 0) + { + // T-REC-X.690-201508 sec 8.1.1.1 / 8.1.1.3 indicate that the + // End-of-Contents octets are "after" the contents octets, not + // "at the end" of them, so we don't include these bytes in the + // accumulator. + return totalLen; + } + } + + // We found another indefinite length, that means we need to find another + // EndOfContents marker to balance it out. + if (length == null) + { + depth++; + cur = cur.Slice(bytesRead); + totalLen += bytesRead; + } + else + { + // This will throw an AsnContentException if the length exceeds our bounds. + ReadOnlySpan tlv = Slice(cur, 0, bytesRead + length.Value); + + // No exception? Then slice the data and continue. + cur = cur.Slice(tlv.Length); + totalLen += tlv.Length; + } + } + + throw new AsnContentException(); + } + + private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan data, int bytesToRead) + { + int value = ParseNonNegativeInt(Slice(data, 0, bytesToRead)); + data = data.Slice(bytesToRead); + + return value; + } + + private static int ParseNonNegativeInt(ReadOnlySpan data) + { + if (Utf8Parser.TryParse(data, out uint value, out int consumed) && + value <= int.MaxValue && + consumed == data.Length) + { + return (int)value; + } + + throw new AsnContentException(); + } + + private static ReadOnlySpan SliceAtMost(ReadOnlySpan source, int longestPermitted) + { + int len = Math.Min(longestPermitted, source.Length); + return source.Slice(0, len); + } + + private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int length) + { + Debug.Assert(offset >= 0); + + if (length < 0 || source.Length - offset < length) + { + throw new AsnContentException(SR.ContentException_LengthExceedsPayload); + } + + return source.Slice(offset, length); + } + + private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int? length) + { + Debug.Assert(offset >= 0); + + if (length == null) + { + return source.Slice(offset); + } + + int lengthVal = length.Value; + + if (lengthVal < 0 || source.Length - offset < lengthVal) + { + throw new AsnContentException(SR.ContentException_LengthExceedsPayload); + } + + return source.Slice(offset, lengthVal); + } + + internal static ReadOnlyMemory Slice(ReadOnlyMemory bigger, ReadOnlySpan smaller) + { + if (smaller.IsEmpty) + { + return default; + } + + if (bigger.Span.Overlaps(smaller, out int offset)) + { + return bigger.Slice(offset, smaller.Length); + } + + Debug.Fail("AsnReader asked for a matching slice from a non-overlapping input"); + throw new AsnContentException(); + } + + [Conditional("DEBUG")] + private static void AssertEncodingRules(AsnEncodingRules ruleSet) + { + Debug.Assert(ruleSet >= AsnEncodingRules.BER && ruleSet <= AsnEncodingRules.DER); + } + + internal static void CheckEncodingRules(AsnEncodingRules ruleSet) + { + if (ruleSet != AsnEncodingRules.BER && + ruleSet != AsnEncodingRules.CER && + ruleSet != AsnEncodingRules.DER) + { + throw new ArgumentOutOfRangeException(nameof(ruleSet)); + } + } + + private static void CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber) + { + if (expectedTag.TagClass == TagClass.Universal && expectedTag.TagValue != (int)tagNumber) + { + throw new ArgumentException( + SR.Argument_UniversalValueIsFixed, + nameof(expectedTag)); + } + + if (expectedTag.TagClass != tag.TagClass || expectedTag.TagValue != tag.TagValue) + { + throw new AsnContentException( + SR.Format( + SR.ContentException_WrongTag, + tag.TagClass, + tag.TagValue, + expectedTag.TagClass, + expectedTag.TagValue)); + } + } + + private enum LengthDecodeStatus + { + NeedMoreData, + DerIndefinite, + ReservedValue, + LengthTooBig, + LaxEncodingProhibited, + Success, + } + + private enum LengthValidity + { + CerRequiresIndefinite, + PrimitiveEncodingRequiresDefinite, + LengthExceedsInput, + Valid, + } + } + + /// + /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. + /// + public partial class AsnReader + { + internal const int MaxCERSegmentSize = AsnDecoder.MaxCERSegmentSize; + + private ReadOnlyMemory _data; + private readonly AsnReaderOptions _options; + + /// + /// Gets the encoding rules in use by this reader. + /// + /// + /// The encoding rules in use by this reader. + /// + public AsnEncodingRules RuleSet { get; } + + /// + /// Gets an indication of whether the reader has remaining data available to process. + /// + /// + /// if there is more data available for the reader to process; + /// otherwise, . + /// + public bool HasData => !_data.IsEmpty; + + /// + /// Construct an over with a given ruleset. + /// + /// The data to read. + /// The encoding constraints for the reader. + /// Additional options for the reader. + /// + /// This constructor does not evaluate for correctness, + /// any correctness checks are done as part of member methods. + /// + /// This constructor does not copy . The caller is responsible for + /// ensuring that the values do not change until the reader is finished. + /// + /// + /// is not defined. + /// + public AsnReader(ReadOnlyMemory data, AsnEncodingRules ruleSet, AsnReaderOptions options = default) + { + AsnDecoder.CheckEncodingRules(ruleSet); + + _data = data; + RuleSet = ruleSet; + _options = options; + } + + /// + /// Throws a standardized if the reader has remaining + /// data, performs no function if returns . + /// + /// + /// This method provides a standardized target and standardized exception for reading a + /// "closed" structure, such as the nested content for an explicitly tagged value. + /// + public void ThrowIfNotEmpty() + { + if (HasData) + { + throw new AsnContentException(SR.ContentException_TooMuchData); + } + } + + /// + /// Read the encoded tag at the next data position, without advancing the reader. + /// + /// + /// The decoded tag value. + /// + /// + /// a tag could not be decoded at the reader's current position. + /// + public Asn1Tag PeekTag() + { + return Asn1Tag.Decode(_data.Span, out _); + } + + /// + /// Get a view of the next encoded value without + /// advancing the reader. For indefinite length encodings this includes the + /// End of Contents marker. + /// + /// + /// The bytes of the next encoded value. + /// + /// + /// The reader is positioned at a point where the tag or length is invalid + /// under the current encoding rules. + /// + /// + /// + public ReadOnlyMemory PeekEncodedValue() + { + AsnDecoder.ReadEncodedValue(_data.Span, RuleSet, out _, out _, out int bytesConsumed); + return _data.Slice(0, bytesConsumed); + } + + /// + /// Get a view of the content octets (bytes) of the + /// next encoded value without advancing the reader. + /// + /// + /// The bytes of the contents octets of the next encoded value. + /// + /// + /// The reader is positioned at a point where the tag or length is invalid + /// under the current encoding rules. + /// + /// + public ReadOnlyMemory PeekContentBytes() + { + AsnDecoder.ReadEncodedValue( + _data.Span, + RuleSet, + out int contentOffset, + out int contentLength, + out _); + + return _data.Slice(contentOffset, contentLength); + } + + /// + /// Get a view of the next encoded value, + /// and advance the reader past it. For an indefinite length encoding this includes + /// the End of Contents marker. + /// + /// + /// A view of the next encoded value. + /// + /// + public ReadOnlyMemory ReadEncodedValue() + { + ReadOnlyMemory encodedValue = PeekEncodedValue(); + _data = _data.Slice(encodedValue.Length); + return encodedValue; + } + + private AsnReader CloneAtSlice(int start, int length) + { + return new AsnReader(_data.Slice(start, length), RuleSet, _options); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnEncodingRules.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs similarity index 89% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnEncodingRules.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs index 50628400f5e564..c0d87eb5678394 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnEncodingRules.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// The encoding ruleset for an or . /// // ITU-T-REC.X.680-201508 sec 4. - internal enum AsnEncodingRules + public enum AsnEncodingRules { /// /// ITU-T X.690 Basic Encoding Rules diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs new file mode 100644 index 00000000000000..f96b62a1406131 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + /// + /// Specifies options that modify the behavior of an . + /// + public struct AsnReaderOptions + { + private const int DefaultTwoDigitMax = 2049; + + private ushort _twoDigitYearMax; + + /// + /// Gets or sets the largest year to represent with a UtcTime value. + /// + /// The largest year to represent with a UtcTime value. The default is 2049. + public int UtcTimeTwoDigitYearMax + { + get + { + if (_twoDigitYearMax == 0) + { + return DefaultTwoDigitMax; + } + + return _twoDigitYearMax; + } + set + { + if (value < 1 || value > 9999) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _twoDigitYearMax = (ushort)value; + } + } + + /// + /// Gets or sets a value that indicates whether the reader should bypass sort ordering + /// on a Set or Set-Of value. + /// + /// + /// if the reader should not validate that a Set or Set-Of value + /// is sorted correctly for the current encoding rules; otherwise . + /// The default is . + /// + public bool SkipSetSortOrderVerification { get; set; } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs new file mode 100644 index 00000000000000..1be9f1eddcef7d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write a Bit String value with a specified tag. + /// + /// The value to write. + /// + /// The number of trailing bits which are not semantic. + /// + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// has length 0 and is not 0 + /// + /// -or- + /// + /// is not empty and any of the bits identified by + /// is set. + /// + /// + /// is not in the range [0,7]. + /// + public void WriteBitString(ReadOnlySpan value, int unusedBitCount = 0, Asn1Tag? tag = null) + { + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + // Primitive or constructed, doesn't matter. + WriteBitStringCore(tag ?? Asn1Tag.PrimitiveBitString, value, unusedBitCount); + } + + // T-REC-X.690-201508 sec 8.6 + private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount) + { + // T-REC-X.690-201508 sec 8.6.2.2 + if (unusedBitCount < 0 || unusedBitCount > 7) + { + throw new ArgumentOutOfRangeException( + nameof(unusedBitCount), + unusedBitCount, + SR.Argument_UnusedBitCountRange); + } + + // T-REC-X.690-201508 sec 8.6.2.3 + if (bitString.Length == 0 && unusedBitCount != 0) + { + throw new ArgumentException(SR.Argument_UnusedBitCountMustBeZero, nameof(unusedBitCount)); + } + + byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1]; + + // T-REC-X.690-201508 sec 11.2 + // + // This could be ignored for BER, but since DER is more common and + // it likely suggests a program error on the caller, leave it enabled for + // BER for now. + if (!CheckValidLastByte(lastByte, unusedBitCount)) + { + throw new ArgumentException(SR.Argument_UnusedBitWasSet, nameof(unusedBitCount)); + } + + if (RuleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + // + // If it's not within a primitive segment, use the constructed encoding. + // (>= instead of > because of the unused bit count byte) + if (bitString.Length >= AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerBitString(tag, bitString, unusedBitCount); + return; + } + } + + // Clear the constructed flag, if present. + WriteTag(tag.AsPrimitive()); + // The unused bits byte requires +1. + WriteLength(bitString.Length + 1); + _buffer[_offset] = (byte)unusedBitCount; + _offset++; + bitString.CopyTo(_buffer.AsSpan(_offset)); + _offset += bitString.Length; + } + + private static bool CheckValidLastByte(byte lastByte, int unusedBitCount) + { + // If 3 bits are "unused" then build a mask for them to check for 0. + // 1 << 3 => 0b0000_1000 + // subtract 1 => 0b000_0111 + int mask = (1 << unusedBitCount) - 1; + return ((lastByte & mask) == 0); + } + + private static int DetermineCerBitStringTotalLength(Asn1Tag tag, int contentLength) + { + const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; + // Every segment has an "unused bit count" byte. + const int MaxCERContentSize = MaxCERSegmentSize - 1; + Debug.Assert(contentLength > MaxCERContentSize); + + int fullSegments = Math.DivRem(contentLength, MaxCERContentSize, out int lastContentSize); + + // The tag size is 1 byte. + // The length will always be encoded as 82 03 E8 (3 bytes) + // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) + const int FullSegmentEncodedSize = 1004; + Debug.Assert( + FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); + + int remainingEncodedSize; + + if (lastContentSize == 0) + { + remainingEncodedSize = 0; + } + else + { + // One byte of tag, minimum one byte of length, and one byte of unused bit count. + remainingEncodedSize = 3 + lastContentSize + GetEncodedLengthSubsequentByteCount(lastContentSize); + } + + // Reduce the number of copies by pre-calculating the size. + // +2 for End-Of-Contents + // +1 for 0x80 indefinite length + // +tag length + return fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 3 + tag.CalculateEncodedSize(); + } + + // T-REC-X.690-201508 sec 9.2, 8.6 + private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan payload, int unusedBitCount) + { + const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; + // Every segment has an "unused bit count" byte. + const int MaxCERContentSize = MaxCERSegmentSize - 1; + Debug.Assert(payload.Length > MaxCERContentSize); + + int expectedSize = DetermineCerBitStringTotalLength(tag, payload.Length); + EnsureWriteCapacity(expectedSize); + int savedOffset = _offset; + + WriteTag(tag.AsConstructed()); + // T-REC-X.690-201508 sec 9.1 + // Constructed CER uses the indefinite form. + WriteLength(-1); + + byte[] ensureNoExtraCopy = _buffer; + + ReadOnlySpan remainingData = payload; + Span dest; + Asn1Tag primitiveBitString = Asn1Tag.PrimitiveBitString; + + while (remainingData.Length > MaxCERContentSize) + { + // T-REC-X.690-201508 sec 8.6.4.1 + WriteTag(primitiveBitString); + WriteLength(MaxCERSegmentSize); + // 0 unused bits in this segment. + _buffer[_offset] = 0; + _offset++; + + dest = _buffer.AsSpan(_offset); + remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); + + remainingData = remainingData.Slice(MaxCERContentSize); + _offset += MaxCERContentSize; + } + + WriteTag(primitiveBitString); + WriteLength(remainingData.Length + 1); + + _buffer[_offset] = (byte)unusedBitCount; + _offset++; + + dest = _buffer.AsSpan(_offset); + remainingData.CopyTo(dest); + _offset += remainingData.Length; + + WriteEndOfContents(); + + Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); + Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerBitString)}"); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Boolean.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs similarity index 65% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Boolean.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs index f65a8a7eef2b1d..9197b9095359ad 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Boolean.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs @@ -4,36 +4,26 @@ using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { - /// - /// Write a Boolean value with tag UNIVERSAL 1. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteBoolean(bool value) - { - WriteBooleanCore(Asn1Tag.Boolean, value); - } - /// /// Write a Boolean value with a specified tag. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 1). /// /// . is /// , but /// . is not correct for /// the method /// - public void WriteBoolean(Asn1Tag tag, bool value) + public void WriteBoolean(bool value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Boolean); - WriteBooleanCore(tag.AsPrimitive(), value); + WriteBooleanCore(tag?.AsPrimitive() ?? Asn1Tag.Boolean, value); } // T-REC-X.690-201508 sec 11.1, 8.2 diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs new file mode 100644 index 00000000000000..abf7e77be9f94c --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write a non-[] enum value as an Enumerated with + /// tag UNIVERSAL 10. + /// + /// The boxed enumeration value to write. + /// The tag to write, or for the default tag (Universal 10). + /// + /// is . + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not a boxed enum value. + /// + /// -or- + /// + /// the unboxed type of is declared []. + /// + /// + /// + public void WriteEnumeratedValue(Enum value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + WriteEnumeratedValue(tag?.AsPrimitive() ?? Asn1Tag.Enumerated, value.GetType(), value); + } + + /// + /// Write a non-[] enum value as an Enumerated with + /// tag UNIVERSAL 10. + /// + /// The tag to write. + /// The boxed enumeration value to write. + /// + /// is . + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not an enum. + /// + /// -or- + /// + /// is declared []. + /// + /// + public void WriteEnumeratedValue(TEnum value, Asn1Tag? tag = null) where TEnum : Enum + { + WriteEnumeratedValue(tag?.AsPrimitive() ?? Asn1Tag.Enumerated, typeof(TEnum), value); + } + + // T-REC-X.690-201508 sec 8.4 + private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object value) + { + CheckUniversalTag(tag, UniversalTagNumber.Enumerated); + + Type backingType = tEnum.GetEnumUnderlyingType(); + + if (tEnum.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_EnumeratedValueRequiresNonFlagsEnum, + nameof(tEnum)); + } + + if (backingType == typeof(ulong)) + { + ulong numericValue = Convert.ToUInt64(value); + // T-REC-X.690-201508 sec 8.4 + WriteNonNegativeIntegerCore(tag, numericValue); + } + else + { + // All other types fit in a (signed) long. + long numericValue = Convert.ToInt64(value); + // T-REC-X.690-201508 sec 8.4 + WriteIntegerCore(tag, numericValue); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.GeneralizedTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs similarity index 75% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.GeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs index d0600d28213c5c..4fc3c6ef887458 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.GeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs @@ -6,55 +6,46 @@ using System.Buffers.Text; using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { - /// - /// Write the provided as a GeneralizedTime with tag - /// UNIVERSAL 24, optionally excluding the fractional seconds. - /// - /// The value to write. - /// - /// true to treat the fractional seconds in as 0 even if - /// a non-zero value is present. - /// - /// The writer has been Disposed. - /// - public void WriteGeneralizedTime(DateTimeOffset value, bool omitFractionalSeconds = false) - { - WriteGeneralizedTimeCore(Asn1Tag.GeneralizedTime, value, omitFractionalSeconds); - } - /// /// Write the provided as a GeneralizedTime with a specified /// UNIVERSAL 24, optionally excluding the fractional seconds. /// - /// The tagto write. /// The value to write. /// - /// true to treat the fractional seconds in as 0 even if + /// to treat the fractional seconds in as 0 even if /// a non-zero value is present. /// + /// The tag to write, or for the default tag (Universal 24). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - /// - public void WriteGeneralizedTime(Asn1Tag tag, DateTimeOffset value, bool omitFractionalSeconds = false) + public void WriteGeneralizedTime( + DateTimeOffset value, + bool omitFractionalSeconds = false, + Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.GeneralizedTime); // Clear the constructed flag, if present. - WriteGeneralizedTimeCore(tag.AsPrimitive(), value, omitFractionalSeconds); + WriteGeneralizedTimeCore( + tag?.AsPrimitive() ?? Asn1Tag.GeneralizedTime, + value, + omitFractionalSeconds); } // T-REC-X.680-201508 sec 46 // T-REC-X.690-201508 sec 11.7 - private void WriteGeneralizedTimeCore(Asn1Tag tag, DateTimeOffset value, bool omitFractionalSeconds) + private void WriteGeneralizedTimeCore( + Asn1Tag tag, + DateTimeOffset value, + bool omitFractionalSeconds) { // GeneralizedTime under BER allows many different options: // * (HHmmss), (HHmm), (HH) @@ -101,7 +92,7 @@ private void WriteGeneralizedTimeCore(Asn1Tag tag, DateTimeOffset value, bool om if (!Utf8Formatter.TryFormat(decimalTicks, fraction, out int bytesWritten, new StandardFormat('G'))) { Debug.Fail($"Utf8Formatter.TryFormat could not format {floatingTicks} / TicksPerSecond"); - throw new CryptographicException(); + throw new InvalidOperationException(); } Debug.Assert(bytesWritten > 2, $"{bytesWritten} should be > 2"); @@ -145,7 +136,7 @@ private void WriteGeneralizedTimeCore(Asn1Tag tag, DateTimeOffset value, bool om !Utf8Formatter.TryFormat(second, baseSpan.Slice(12, 2), out _, d2)) { Debug.Fail($"Utf8Formatter.TryFormat failed to build components of {normalized:O}"); - throw new CryptographicException(); + throw new InvalidOperationException(); } _offset += IntegerPortionLength; diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Integer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs similarity index 65% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Integer.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs index b8f436ba16d810..88444bd31c7446 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Integer.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs @@ -5,167 +5,109 @@ using System.Diagnostics; using System.Numerics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteInteger(long value) - { - WriteIntegerCore(Asn1Tag.Integer, value); - } - - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteInteger(ulong value) - { - WriteNonNegativeIntegerCore(Asn1Tag.Integer, value); - } - - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteInteger(BigInteger value) - { - WriteIntegerCore(Asn1Tag.Integer, value); - } - /// /// Write an Integer value with a specified tag. /// - /// The integer value to write, in signed big-endian byte order. - /// - /// the 9 most sigificant bits are all set --OR-- - /// the 9 most sigificant bits are all unset - /// - /// The writer has been Disposed. - public void WriteInteger(ReadOnlySpan value) - { - WriteIntegerCore(Asn1Tag.Integer, value); - } - - /// - /// Write an Integer value with a specified tag. - /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, long value) + public void WriteInteger(long value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerCore(tag.AsPrimitive(), value); + WriteIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, ulong value) + [CLSCompliant(false)] + public void WriteInteger(ulong value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteNonNegativeIntegerCore(tag.AsPrimitive(), value); + WriteNonNegativeIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, BigInteger value) + public void WriteInteger(BigInteger value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerCore(tag.AsPrimitive(), value); + WriteIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The integer value to write, in signed big-endian byte order. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// - /// the 9 most sigificant bits are all set --OR-- - /// the 9 most sigificant bits are all unset + /// + /// the 9 most significant bits are all set. + /// + /// -or- + /// + /// the 9 most significant bits are all unset. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, ReadOnlySpan value) + public void WriteInteger(ReadOnlySpan value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerCore(tag.AsPrimitive(), value); - } - - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The integer value to write, in unsigned big-endian byte order. - /// - /// the 9 most sigificant bits are all unset - /// - /// The writer has been Disposed. - public void WriteIntegerUnsigned(ReadOnlySpan value) - { - WriteIntegerUnsignedCore(Asn1Tag.Integer, value); + WriteIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The integer value to write, in unsigned big-endian byte order. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// - /// the 9 most sigificant bits are all unset + /// + /// the 9 most significant bits are all unset. /// - /// The writer has been Disposed. - public void WriteIntegerUnsigned(Asn1Tag tag, ReadOnlySpan value) + public void WriteIntegerUnsigned(ReadOnlySpan value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerUnsignedCore(tag.AsPrimitive(), value); + WriteIntegerUnsignedCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } // T-REC-X.690-201508 sec 8.3 @@ -278,13 +220,13 @@ private void WriteIntegerUnsignedCore(Asn1Tag tag, ReadOnlySpan value) { if (value.IsEmpty) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new ArgumentException(SR.Argument_IntegerCannotBeEmpty, nameof(value)); } // T-REC-X.690-201508 sec 8.3.2 if (value.Length > 1 && value[0] == 0 && value[1] < 0x80) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new ArgumentException(SR.Argument_IntegerRedundantByte, nameof(value)); } Debug.Assert(!tag.IsConstructed); @@ -307,11 +249,9 @@ private void WriteIntegerUnsignedCore(Asn1Tag tag, ReadOnlySpan value) private void WriteIntegerCore(Asn1Tag tag, ReadOnlySpan value) { - CheckDisposed(); - if (value.IsEmpty) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new ArgumentException(SR.Argument_IntegerCannotBeEmpty, nameof(value)); } // T-REC-X.690-201508 sec 8.3.2 @@ -324,7 +264,7 @@ private void WriteIntegerCore(Asn1Tag tag, ReadOnlySpan value) // If the first 9 bits are all 0 or are all 1, the value is invalid. if (masked == 0 || masked == RedundancyMask) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new ArgumentException(SR.Argument_IntegerRedundantByte, nameof(value)); } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs new file mode 100644 index 00000000000000..9008475748bcb7 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs @@ -0,0 +1,199 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write a [] enum value as a NamedBitList with + /// a specified tag. + /// + /// The boxed enumeration value to write + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not a boxed enum value. + /// + /// -or- + /// + /// the unboxed type of is not declared []. + /// + /// + /// is . + /// + public void WriteNamedBitList(Enum value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + WriteNamedBitList(tag, value.GetType(), value); + } + + /// + /// Write a [] enum value as a NamedBitList with + /// a specified tag. + /// + /// The enumeration value to write + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not an enum value. + /// + /// -or- + /// + /// is not declared []. + /// + public void WriteNamedBitList(TEnum value, Asn1Tag? tag = null) where TEnum : Enum + { + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + WriteNamedBitList(tag, typeof(TEnum), value); + } + + /// + /// Write a bit array value as a NamedBitList with a specified tag. + /// + /// The bits to write + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is . + /// + /// + /// The index of the bit array corresponds to the bit number in the encoded format, which is + /// different than the value produced by with a byte array. + /// For example, the bit array { false, true, true } encodes as 0b0110_0000 with 5 + /// unused bits. + /// + public void WriteNamedBitList(BitArray value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + WriteBitArray(value, tag); + } + + private void WriteNamedBitList(Asn1Tag? tag, Type tEnum, Enum value) + { + Type backingType = tEnum.GetEnumUnderlyingType(); + + if (!tEnum.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_NamedBitListRequiresFlagsEnum, + nameof(tEnum)); + } + + ulong integralValue; + + if (backingType == typeof(ulong)) + { + integralValue = Convert.ToUInt64(value); + } + else + { + // All other types fit in a (signed) long. + long numericValue = Convert.ToInt64(value); + integralValue = unchecked((ulong)numericValue); + } + + WriteNamedBitList(tag, integralValue); + } + + // T-REC-X.680-201508 sec 22 + // T-REC-X.690-201508 sec 8.6, 11.2.2 + private void WriteNamedBitList(Asn1Tag? tag, ulong integralValue) + { + Span temp = stackalloc byte[sizeof(ulong)]; + // Reset to all zeros, since we're just going to or-in bits we need. + temp.Clear(); + + int indexOfHighestSetBit = -1; + + for (int i = 0; integralValue != 0; integralValue >>= 1, i++) + { + if ((integralValue & 1) != 0) + { + temp[i / 8] |= (byte)(0x80 >> (i % 8)); + indexOfHighestSetBit = i; + } + } + + if (indexOfHighestSetBit < 0) + { + // No bits were set; this is an empty bit string. + // T-REC-X.690-201508 sec 11.2.2-note2 + WriteBitString(ReadOnlySpan.Empty, tag: tag); + } + else + { + // At least one bit was set. + // Determine the shortest length necessary to represent the bit string. + + // Since "bit 0" gets written down 0 => 1. + // Since "bit 8" is in the second byte 8 => 2. + // That makes the formula ((bit / 8) + 1) instead of ((bit + 7) / 8). + int byteLen = (indexOfHighestSetBit / 8) + 1; + int unusedBitCount = 7 - (indexOfHighestSetBit % 8); + + WriteBitString( + temp.Slice(0, byteLen), + unusedBitCount, + tag); + } + } + + private void WriteBitArray(BitArray value, Asn1Tag? tag) + { + if (value.Count == 0) + { + // No bits were set; this is an empty bit string. + // T-REC-X.690-201508 sec 11.2.2-note2 + WriteBitString(ReadOnlySpan.Empty, tag: tag); + return; + } + + int requiredBytes = checked((value.Count + 7) / 8); + int unusedBits = requiredBytes * 8 - value.Count; + byte[] rented = CryptoPool.Rent(requiredBytes); + + // Export the BitArray to a byte array. + // While bits 0-7 are in the first byte, they are numbered 76543210, + // but our wire form is 01234567, so we'll need to reverse the bits on each byte. + value.CopyTo(rented, 0); + + Span valueSpan = rented.AsSpan(0, requiredBytes); + AsnDecoder.ReverseBitsPerByte(valueSpan); + + WriteBitString(valueSpan, unusedBits, tag); + CryptoPool.Return(rented, requiredBytes); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Null.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs similarity index 58% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Null.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs index de2bc92e5237d9..dbe3670a53ecc1 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Null.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs @@ -4,35 +4,25 @@ using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { - /// - /// Write NULL with tag UNIVERSAL 5. - /// - /// The writer has been Disposed. - public void WriteNull() - { - WriteNullCore(Asn1Tag.Null); - } - /// /// Write NULL with a specified tag. /// - /// The tag to write. + /// The tag to write, or for the default tag (Universal 5). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteNull(Asn1Tag tag) + public void WriteNull(Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Null); - WriteNullCore(tag.AsPrimitive()); + WriteNullCore(tag?.AsPrimitive() ?? Asn1Tag.Null); } // T-REC-X.690-201508 sec 8.8 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.OctetString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs similarity index 58% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.OctetString.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs index 1dc9b0ac0364d2..e65053b3988c05 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.OctetString.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs @@ -4,39 +4,79 @@ using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Write an Octet String with tag UNIVERSAL 4. + /// Begin writing an Octet String value with a specified tag. /// - /// The value to write. - /// The writer has been Disposed. - /// - public void WriteOctetString(ReadOnlySpan octetString) + /// + /// A disposable value which will automatically call . + /// + /// + /// This method is just an accelerator for writing an Octet String value where the + /// contents are also ASN.1 data encoded under the same encoding system. + /// When is called the entire nested contents are + /// normalized as a single Octet String value, encoded correctly for the current encoding + /// rules. + /// This method does not necessarily create a Constructed encoding, and it is not invalid to + /// write values other than Octet String inside this Push/Pop. + /// + /// The tag to write, or for the default tag (Universal 4). + /// + public Scope PushOctetString(Asn1Tag? tag = null) { - WriteOctetString(Asn1Tag.PrimitiveOctetString, octetString); + CheckUniversalTag(tag, UniversalTagNumber.OctetString); + + return PushTag( + tag?.AsConstructed() ?? Asn1Tag.ConstructedOctetString, + UniversalTagNumber.OctetString); + } + + /// + /// Indicate that the open Octet String with the tag UNIVERSAL 4 is closed, + /// returning the writer to the parent context. + /// + /// The tag to write, or for the default tag (Universal 4). + /// + /// In and modes + /// the encoded contents will remain in a single primitive Octet String. + /// In mode the contents will be broken up into + /// multiple segments, when required. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// the writer is not currently positioned within an Octet String with the specified tag. + /// + public void PopOctetString(Asn1Tag? tag = default) + { + CheckUniversalTag(tag, UniversalTagNumber.OctetString); + PopTag(tag?.AsConstructed() ?? Asn1Tag.ConstructedOctetString, UniversalTagNumber.OctetString); } /// /// Write an Octet String value with a specified tag. /// - /// The tag to write. - /// The value to write. + /// The value to write. + /// The tag to write, or for the default tag (Universal 4). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteOctetString(Asn1Tag tag, ReadOnlySpan octetString) + public void WriteOctetString(ReadOnlySpan value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.OctetString); // Primitive or constructed, doesn't matter. - WriteOctetStringCore(tag, octetString); + WriteOctetStringCore(tag ?? Asn1Tag.PrimitiveOctetString, value); } // T-REC-X.690-201508 sec 8.7 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs similarity index 55% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Oid.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs index 6363c76b8d40a5..2ce7d540339e2f 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Oid.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs @@ -2,169 +2,74 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Diagnostics; using System.Numerics; +using System.Security.Cryptography; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// /// Write an Object Identifier with a specified tag. /// - /// The object identifier to write. - /// - /// is null - /// - /// - /// . is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Oid oid) - { - if (oid == null) - throw new ArgumentNullException(nameof(oid)); - - CheckDisposed(); - - if (oid.Value == null) - throw new CryptographicException(SR.Argument_InvalidOidValue); - - WriteObjectIdentifier(oid.Value); - } - - /// - /// Write an Object Identifier with a specified tag. - /// - /// The object identifier to write. - /// - /// is null - /// - /// - /// is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(string oidValue) - { - if (oidValue == null) - throw new ArgumentNullException(nameof(oidValue)); - - WriteObjectIdentifier(oidValue.AsSpan()); - } - - /// - /// Write an Object Identifier with tag UNIVERSAL 6. - /// - /// The object identifier to write. - /// - /// is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(ReadOnlySpan oidValue) - { - WriteObjectIdentifierCore(Asn1Tag.ObjectIdentifier, oidValue); - } - - /// - /// Write an Object Identifier with a specified tag. - /// - /// The tag to write. - /// The object identifier to write. - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// is null - /// - /// - /// . is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Asn1Tag tag, Oid oid) - { - if (oid == null) - throw new ArgumentNullException(nameof(oid)); - - CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier); - CheckDisposed(); - - if (oid.Value == null) - throw new CryptographicException(SR.Argument_InvalidOidValue); - - WriteObjectIdentifier(tag, oid.Value); - } - - /// - /// Write an Object Identifier with a specified tag. - /// - /// The tag to write. /// The object identifier to write. + /// The tag to write, or for the default tag (Universal 6). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. + /// + /// -or- + /// + /// is not a valid dotted decimal + /// object identifier. /// /// - /// is null - /// - /// - /// is not a valid dotted decimal - /// object identifier + /// is . /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Asn1Tag tag, string oidValue) + public void WriteObjectIdentifier(string oidValue, Asn1Tag? tag = null) { if (oidValue == null) throw new ArgumentNullException(nameof(oidValue)); - WriteObjectIdentifier(tag, oidValue.AsSpan()); + WriteObjectIdentifier(oidValue.AsSpan(), tag); } /// /// Write an Object Identifier with a specified tag. /// - /// The tag to write. /// The object identifier to write. + /// The tag to write, or for the default tag (Universal 6). /// /// . is /// , but /// . is not correct for - /// the method - /// - /// + /// the method. + /// + /// -or- + /// /// is not a valid dotted decimal - /// object identifier + /// object identifier. /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Asn1Tag tag, ReadOnlySpan oidValue) + public void WriteObjectIdentifier(ReadOnlySpan oidValue, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier); - WriteObjectIdentifierCore(tag.AsPrimitive(), oidValue); + WriteObjectIdentifierCore(tag?.AsPrimitive() ?? Asn1Tag.ObjectIdentifier, oidValue); } - // T-REC-X.690-201508 sec 8.19 private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan oidValue) { - CheckDisposed(); - // T-REC-X.690-201508 sec 8.19.4 // The first character is in { 0, 1, 2 }, the second will be a '.', and a third (digit) // will also exist. if (oidValue.Length < 3) - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)); if (oidValue[1] != '.') - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)); // The worst case is "1.1.1.1.1", which takes 4 bytes (5 components, with the first two condensed) // Longer numbers get smaller: "2.1.127" is only 2 bytes. (81d (0x51) and 127 (0x7F)) @@ -179,7 +84,7 @@ private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan oidValue) '0' => 0, '1' => 1, '2' => 2, - _ => throw new CryptographicException(SR.Argument_InvalidOidValue), + _ => throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)), }; // The first two components are special: @@ -231,7 +136,7 @@ private static BigInteger ParseSubIdentifier(ref ReadOnlySpan oidValue) } else if (endIndex == 0 || endIndex == oidValue.Length - 1) { - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)); } // The following code is equivalent to @@ -244,7 +149,7 @@ private static BigInteger ParseSubIdentifier(ref ReadOnlySpan oidValue) if (position > 0 && value == 0) { // T-REC X.680-201508 sec 12.26 - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)); } value *= 10; @@ -262,7 +167,7 @@ private static int AtoI(char c) return c - '0'; } - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, "oidValue"); } // ITU-T-X.690-201508 sec 8.19.5 diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs new file mode 100644 index 00000000000000..885ea205289abc --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Begin writing a Sequence with a specified tag. + /// + /// The tag to write, or for the default tag (Universal 16). + /// + /// A disposable value which will automatically call . + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public Scope PushSequence(Asn1Tag? tag = null) + { + CheckUniversalTag(tag, UniversalTagNumber.Sequence); + + // Assert the constructed flag, in case it wasn't. + return PushSequenceCore(tag?.AsConstructed() ?? Asn1Tag.Sequence); + } + + /// + /// Indicate that the open Sequence with the specified tag is closed, + /// returning the writer to the parent context. + /// + /// The tag to write, or for the default tag (Universal 16). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// the writer is not currently positioned within a Sequence with the specified tag. + /// + /// + public void PopSequence(Asn1Tag? tag = null) + { + // PopSequence shouldn't be used to pop a SetOf. + CheckUniversalTag(tag, UniversalTagNumber.Sequence); + + // Assert the constructed flag, in case it wasn't. + PopSequenceCore(tag?.AsConstructed() ?? Asn1Tag.Sequence); + } + + // T-REC-X.690-201508 sec 8.9, 8.10 + private Scope PushSequenceCore(Asn1Tag tag) + { + Debug.Assert(tag.IsConstructed); + return PushTag(tag, UniversalTagNumber.Sequence); + } + + // T-REC-X.690-201508 sec 8.9, 8.10 + private void PopSequenceCore(Asn1Tag tag) + { + Debug.Assert(tag.IsConstructed); + PopTag(tag, UniversalTagNumber.Sequence); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.SetOf.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs similarity index 51% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.SetOf.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs index d1add9cb5f75da..33f12962da95bf 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.SetOf.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs @@ -2,29 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +using System.Diagnostics; + +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { - /// - /// Begin writing a Set-Of with a tag UNIVERSAL 17. - /// - /// - /// In and modes - /// the writer will sort the Set-Of elements when the tag is closed. - /// - /// The writer has been Disposed. - /// - /// - public void PushSetOf() - { - PushSetOf(Asn1Tag.SetOf); - } - /// /// Begin writing a Set-Of with a specified tag. /// - /// The tag to write. + /// The tag to write, or for the default tag (Universal 17). + /// + /// A disposable value which will automatically call . + /// /// /// In and modes /// the writer will sort the Set-Of elements when the tag is closed. @@ -33,42 +23,22 @@ public void PushSetOf() /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - /// - public void PushSetOf(Asn1Tag tag) + /// + public Scope PushSetOf(Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.SetOf); // Assert the constructed flag, in case it wasn't. - PushSetOfCore(tag.AsConstructed()); - } - - /// - /// Indicate that the open Set-Of with the tag UNIVERSAL 17 is closed, - /// returning the writer to the parent context. - /// - /// - /// In and modes - /// the writer will sort the Set-Of elements when the tag is closed. - /// - /// - /// the writer is not currently positioned within a Sequence with tag UNIVERSAL 17 - /// - /// The writer has been Disposed. - /// - /// - public void PopSetOf() - { - PopSetOfCore(Asn1Tag.SetOf); + return PushSetOfCore(tag?.AsConstructed() ?? Asn1Tag.SetOf); } /// /// Indicate that the open Set-Of with the specified tag is closed, /// returning the writer to the parent context. /// - /// The tag to write. + /// The tag to write, or for the default tag (Universal 17). /// /// In and modes /// the writer will sort the Set-Of elements when the tag is closed. @@ -77,32 +47,34 @@ public void PopSetOf() /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// - /// the writer is not currently positioned within a Set-Of with the specified tag + /// the writer is not currently positioned within a Set-Of with the specified tag. /// - /// The writer has been Disposed. - /// - public void PopSetOf(Asn1Tag tag) + /// + public void PopSetOf(Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.SetOf); // Assert the constructed flag, in case it wasn't. - PopSetOfCore(tag.AsConstructed()); + PopSetOfCore(tag?.AsConstructed() ?? Asn1Tag.SetOf); } // T-REC-X.690-201508 sec 8.12 // The writer claims SetOf, and not Set, so as to avoid the field // ordering clause of T-REC-X.690-201508 sec 9.3 - private void PushSetOfCore(Asn1Tag tag) + private Scope PushSetOfCore(Asn1Tag tag) { - PushTag(tag, UniversalTagNumber.SetOf); + Debug.Assert(tag.IsConstructed); + return PushTag(tag, UniversalTagNumber.SetOf); } // T-REC-X.690-201508 sec 8.12 private void PopSetOfCore(Asn1Tag tag) { + Debug.Assert(tag.IsConstructed); + // T-REC-X.690-201508 sec 11.6 bool sortContents = RuleSet == AsnEncodingRules.CER || RuleSet == AsnEncodingRules.DER; diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs new file mode 100644 index 00000000000000..e791fb4ac99719 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write the provided string using the specified encoding type using the specified + /// tag corresponding to the encoding type. + /// + /// + /// One of the enumeration values representing the encoding to use. + /// + /// The string to write. + /// + /// The tag to write, or for the universal tag that is appropriate to + /// the requested encoding type. + /// + /// is + /// + /// is not a restricted character string encoding type. + /// + /// -or- + /// + /// is a restricted character string encoding type that is not + /// currently supported by this method. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public void WriteCharacterString(UniversalTagNumber encodingType, string value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + WriteCharacterString(encodingType, value.AsSpan(), tag); + } + + /// + /// Write the provided string using the specified encoding type using the specified + /// tag corresponding to the encoding type. + /// + /// + /// One of the enumeration values representing the encoding to use. + /// + /// The string to write. + /// + /// The tag to write, or for the universal tag that is appropriate to + /// the requested encoding type. + /// + /// + /// is not a restricted character string encoding type. + /// + /// -or- + /// + /// is a restricted character string encoding type that is not + /// currently supported by this method. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public void WriteCharacterString(UniversalTagNumber encodingType, ReadOnlySpan str, Asn1Tag? tag = null) + { + CheckUniversalTag(tag, encodingType); + + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + WriteCharacterStringCore(tag ?? new Asn1Tag(encodingType), encoding, str); + } + + // T-REC-X.690-201508 sec 8.23 + private void WriteCharacterStringCore(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str) + { + int size = encoding.GetByteCount(str); + + // T-REC-X.690-201508 sec 9.2 + if (RuleSet == AsnEncodingRules.CER) + { + // If it exceeds the primitive segment size, use the constructed encoding. + if (size > AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerCharacterString(tag, encoding, str, size); + return; + } + } + + // Clear the constructed tag, if present. + WriteTag(tag.AsPrimitive()); + WriteLength(size); + Span dest = _buffer.AsSpan(_offset, size); + + int written = encoding.GetBytes(str, dest); + + if (written != size) + { + Debug.Fail( + $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); + } + + _offset += size; + } + + private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str, int size) + { + Debug.Assert(size > AsnReader.MaxCERSegmentSize); + + byte[] tmp = CryptoPool.Rent(size); + int written = encoding.GetBytes(str, tmp); + + if (written != size) + { + Debug.Fail( + $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); + } + + WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size)); + CryptoPool.Return(tmp, size); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.UtcTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs similarity index 59% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.UtcTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs index 60c1d71cbd603f..429c41b4c64714 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.UtcTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs @@ -6,94 +6,56 @@ using System.Buffers.Text; using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Write the provided as a UTCTime with tag - /// UNIVERSAL 23, and accepting the two-digit year as valid in context. - /// - /// The value to write. - /// The writer has been Disposed. - /// - /// - public void WriteUtcTime(DateTimeOffset value) - { - WriteUtcTimeCore(Asn1Tag.UtcTime, value); - } - - /// - /// Write the provided as a UTCTime with a specified tag, + /// Write the provided value as a UTCTime with a specified tag, /// accepting the two-digit year as valid in context. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 23). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - /// + /// /// - public void WriteUtcTime(Asn1Tag tag, DateTimeOffset value) + public void WriteUtcTime(DateTimeOffset value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.UtcTime); // Clear the constructed flag, if present. - WriteUtcTimeCore(tag.AsPrimitive(), value); - } - - /// - /// Write the provided as a UTCTime with tag - /// UNIVERSAL 23, provided the year is in the allowed range. - /// - /// The value to write. - /// - /// The maximum valid year for , after conversion to UTC. - /// For the X.509 Time.utcTime range of 1950-2049, pass 2049. - /// - /// - /// . (after conversion to UTC) - /// is not in the range - /// ( - 100, ] - /// - /// The writer has been Disposed. - /// - /// - public void WriteUtcTime(DateTimeOffset value, int twoDigitYearMax) - { - // Defer to the longer override for twoDigitYearMax validity. - WriteUtcTime(Asn1Tag.UtcTime, value, twoDigitYearMax); + WriteUtcTimeCore(tag?.AsPrimitive() ?? Asn1Tag.UtcTime, value); } /// - /// Write the provided as a UTCTime with a specified tag, + /// Write the provided value as a UTCTime with a specified tag, /// provided the year is in the allowed range. /// - /// The tag to write. /// The value to write. /// /// The maximum valid year for , after conversion to UTC. /// For the X.509 Time.utcTime range of 1950-2049, pass 2049. /// + /// The tag to write, or for the default tag (Universal 23). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// /// . (after conversion to UTC) /// is not in the range - /// ( - 100, ] + /// ( - 100, ]. /// - /// The writer has been Disposed. - /// + /// /// - public void WriteUtcTime(Asn1Tag tag, DateTimeOffset value, int twoDigitYearMax) + public void WriteUtcTime(DateTimeOffset value, int twoDigitYearMax, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.UtcTime); @@ -104,7 +66,7 @@ public void WriteUtcTime(Asn1Tag tag, DateTimeOffset value, int twoDigitYearMax) throw new ArgumentOutOfRangeException(nameof(value)); } - WriteUtcTimeCore(tag.AsPrimitive(), value); + WriteUtcTimeCore(tag?.AsPrimitive() ?? Asn1Tag.UtcTime, value); } // T-REC-X.680-201508 sec 47 @@ -145,7 +107,7 @@ private void WriteUtcTimeCore(Asn1Tag tag, DateTimeOffset value) !Utf8Formatter.TryFormat(second, baseSpan.Slice(10, 2), out _, format)) { Debug.Fail($"Utf8Formatter.TryFormat failed to build components of {normalized:O}"); - throw new CryptographicException(); + throw new InvalidOperationException(); } _buffer[_offset + 12] = (byte)'Z'; diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs similarity index 55% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs index e9c7af34eb0308..dcd7ead4d363eb 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs @@ -6,26 +6,27 @@ // abusing the normal EnsureWriteCapacity + ArrayPool behaviors of rounding up. //#define CHECK_ACCURATE_ENSURE -using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.InteropServices; +using System.Security.Cryptography; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// A writer for BER-, CER-, and DER-encoded ASN.1 data. /// - internal sealed partial class AsnWriter : IDisposable + public sealed partial class AsnWriter { private byte[] _buffer = null!; private int _offset; - private Stack<(Asn1Tag, int, UniversalTagNumber)>? _nestingStack; + private Stack? _nestingStack; /// - /// The in use by this writer. + /// Gets the encoding rules in use by this writer. /// + /// + /// The encoding rules in use by this writer. + /// public AsnEncodingRules RuleSet { get; } /// @@ -47,34 +48,11 @@ public AsnWriter(AsnEncodingRules ruleSet) RuleSet = ruleSet; } - /// - /// Release the resources held by this writer. - /// - public void Dispose() - { - _nestingStack = null; - - if (_buffer != null) - { - Array.Clear(_buffer, 0, _offset); -#if !CHECK_ACCURATE_ENSURE - // clearSize: 0 because it was already cleared. - CryptoPool.Return(_buffer, clearSize: 0); -#endif - _buffer = null!; - } - - _offset = -1; - } - /// /// Reset the writer to have no data, without releasing resources. /// - /// The writer has been Disposed. public void Reset() { - CheckDisposed(); - if (_offset > 0) { Debug.Assert(_buffer != null); @@ -89,17 +67,18 @@ public void Reset() /// Gets the number of bytes that would be written by . /// /// - /// The number of bytes that would be written by , or -1 - /// if a or has not been completed. + /// The number of bytes that would be written by . /// - /// The writer has been Disposed. + /// + /// , , or + /// was called without the corresponding + /// Pop method. + /// public int GetEncodedLength() { - CheckDisposed(); - if ((_nestingStack?.Count ?? 0) != 0) { - return -1; + throw new InvalidOperationException(SR.AsnWriter_EncodeUnbalancedStack); } return _offset; @@ -113,20 +92,17 @@ public int GetEncodedLength() /// On success, receives the number of bytes written to . /// /// - /// true if the encode succeeded, - /// false if is too small. + /// if the encode succeeded, + /// if is too small. /// /// - /// A or has not been closed via - /// or . + /// A or has not been closed via + /// or . /// - /// The writer has been Disposed. public bool TryEncode(Span destination, out int bytesWritten) { - CheckDisposed(); - if ((_nestingStack?.Count ?? 0) != 0) - throw new InvalidOperationException(SR.Cryptography_AsnWriter_EncodeUnbalancedStack); + throw new InvalidOperationException(SR.AsnWriter_EncodeUnbalancedStack); // If the stack is closed out then everything is a definite encoding (BER, DER) or a // required indefinite encoding (CER). So we're correctly sized up, and ready to copy. @@ -150,19 +126,18 @@ public bool TryEncode(Span destination, out int bytesWritten) /// /// Return a new array containing the encoded value. /// - /// A precisely-sized array containing the encoded value. + /// + /// A precisely-sized array containing the encoded value. + /// /// - /// A or has not been closed via - /// or . + /// A or has not been closed via + /// or . /// - /// The writer has been Disposed. public byte[] Encode() { - CheckDisposed(); - if ((_nestingStack?.Count ?? 0) != 0) { - throw new InvalidOperationException(SR.Cryptography_AsnWriter_EncodeUnbalancedStack); + throw new InvalidOperationException(SR.AsnWriter_EncodeUnbalancedStack); } if (_offset == 0) @@ -175,13 +150,11 @@ public byte[] Encode() return _buffer.AsSpan(0, _offset).ToArray(); } - internal ReadOnlySpan EncodeAsSpan() + private ReadOnlySpan EncodeAsSpan() { - CheckDisposed(); - if ((_nestingStack?.Count ?? 0) != 0) { - throw new InvalidOperationException(SR.Cryptography_AsnWriter_EncodeUnbalancedStack); + throw new InvalidOperationException(SR.AsnWriter_EncodeUnbalancedStack); } if (_offset == 0) @@ -203,27 +176,39 @@ internal ReadOnlySpan EncodeAsSpan() /// otherwise. /// /// - /// A or has not been closed via - /// or . + /// A or has not been closed via + /// or . /// - /// The writer has been Disposed. - public bool ValueEquals(ReadOnlySpan other) + public bool EncodedValueEquals(ReadOnlySpan other) { return EncodeAsSpan().SequenceEqual(other); } - private void CheckDisposed() + /// + /// Determines if would produce an output identical to + /// . + /// + /// + /// if the pending encoded data is identical to , + /// otherwise. + /// + /// + /// is . + /// + /// + /// A or has not been closed via + /// or . + /// + public bool EncodedValueEquals(AsnWriter other) { - if (_offset < 0) - { - throw new ObjectDisposedException(nameof(AsnWriter)); - } + if (other == null) + throw new ArgumentNullException(nameof(other)); + + return EncodeAsSpan().SequenceEqual(other.EncodeAsSpan()); } private void EnsureWriteCapacity(int pendingCount) { - CheckDisposed(); - if (pendingCount < 0) { throw new OverflowException(); @@ -232,36 +217,35 @@ private void EnsureWriteCapacity(int pendingCount) if (_buffer == null || _buffer.Length - _offset < pendingCount) { #if CHECK_ACCURATE_ENSURE -// A debug paradigm to make sure that throughout the execution nothing ever writes -// past where the buffer was "allocated". This causes quite a number of reallocs -// and copies, so it's a #define opt-in. + // A debug paradigm to make sure that throughout the execution nothing ever writes + // past where the buffer was "allocated". This causes quite a number of reallocs + // and copies, so it's a #define opt-in. byte[] newBytes = new byte[_offset + pendingCount]; if (_buffer != null) { - Buffer.BlockCopy(_buffer, 0, newBytes, 0, _offset); + Span bufferSpan = _buffer.AsSpan(0, _offset); + bufferSpan.CopyTo(newBytes); + bufferSpan.Clear(); } + + _buffer = newBytes; #else const int BlockSize = 1024; - // While the ArrayPool may have similar logic, make sure we don't run into a lot of - // "grow a little" by asking in 1k steps. + // Make sure we don't run into a lot of "grow a little" by asking in 1k steps. int blocks = checked(_offset + pendingCount + (BlockSize - 1)) / BlockSize; byte[]? oldBytes = _buffer; - _buffer = CryptoPool.Rent(BlockSize * blocks); + Array.Resize(ref _buffer, BlockSize * blocks); if (oldBytes != null) { - Buffer.BlockCopy(oldBytes, 0, _buffer, 0, _offset); - CryptoPool.Return(oldBytes, _offset); + oldBytes.AsSpan(0, _offset).Clear(); } #endif #if DEBUG - // Ensure no "implicit 0" is happening - for (int i = _offset; i < _buffer.Length; i++) - { - _buffer[i] ^= 0xFF; - } + // Ensure no "implicit 0" is happening, in case we move to pooling. + _buffer.AsSpan(_offset).Fill(0xCA); #endif } } @@ -275,7 +259,7 @@ private void WriteTag(Asn1Tag tag) written != spaceRequired) { Debug.Fail($"TryWrite failed or written was wrong value ({written} vs {spaceRequired})"); - throw new CryptographicException(); + throw new InvalidOperationException(); } _offset += spaceRequired; @@ -350,51 +334,81 @@ private static int GetEncodedLengthSubsequentByteCount(int length) } /// - /// Write a single value which has already been encoded. + /// Copy the value of this writer into another. /// - /// The value to write. - /// - /// This method only checks that the tag and length are encoded according to the current ruleset, - /// and that the end of the value is the end of the input. The contents are not evaluated for - /// semantic meaning. - /// - /// - /// could not be read under the current encoding rules --OR-- - /// has data beyond the end of the first value + /// The writer to receive the value. + /// + /// is . + /// + /// + /// A or has not been closed via + /// or . + /// + /// -or- + /// + /// This writer is empty. + /// + /// -or- + /// + /// This writer represents more than one top-level value. + /// + /// -or- + /// + /// This writer's value is encoded in a manner that is not compatible with the + /// ruleset for the destination writer. /// - /// The writer has been Disposed. - public unsafe void WriteEncodedValue(ReadOnlySpan preEncodedValue) + public void CopyTo(AsnWriter destination) { - CheckDisposed(); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); - fixed (byte* ptr = &MemoryMarshal.GetReference(preEncodedValue)) + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, preEncodedValue.Length)) - { - WriteEncodedValue(manager.Memory); - } + destination.WriteEncodedValue(EncodeAsSpan()); + } + catch (ArgumentException e) + { + throw new InvalidOperationException(new InvalidOperationException().Message, e); } } - private void WriteEncodedValue(ReadOnlyMemory preEncodedValue) + /// + /// Write a single value which has already been encoded. + /// + /// The value to write. + /// + /// This method only checks that the tag and length are encoded according to the current ruleset, + /// and that the end of the value is the end of the input. The contents are not evaluated for + /// semantic meaning. + /// + /// + /// could not be read under the current encoding rules. + /// + /// -or- + /// + /// has data beyond the end of the first value. + /// + public void WriteEncodedValue(ReadOnlySpan value) { - AsnReader reader = new AsnReader(preEncodedValue, RuleSet); - // Is it legal under the current rules? - ReadOnlyMemory parsedBack = reader.ReadEncodedValue(); + bool read = AsnDecoder.TryReadEncodedValue( + value, + RuleSet, + out _, + out _, + out _, + out int consumed); - if (reader.HasData) + if (!read || consumed != value.Length) { throw new ArgumentException( - SR.Cryptography_WriteEncodedValue_OneValueAtATime, - nameof(preEncodedValue)); + SR.Argument_WriteEncodedValue_OneValueAtATime, + nameof(value)); } - Debug.Assert(parsedBack.Length == preEncodedValue.Length); - - EnsureWriteCapacity(preEncodedValue.Length); - preEncodedValue.Span.CopyTo(_buffer.AsSpan(_offset)); - _offset += preEncodedValue.Length; + EnsureWriteCapacity(value.Length); + value.CopyTo(_buffer.AsSpan(_offset)); + _offset += value.Length; } // T-REC-X.690-201508 sec 8.1.5 @@ -405,30 +419,27 @@ private void WriteEndOfContents() _buffer[_offset++] = 0; } - private void PushTag(Asn1Tag tag, UniversalTagNumber tagType) + private Scope PushTag(Asn1Tag tag, UniversalTagNumber tagType) { - CheckDisposed(); - if (_nestingStack == null) { - _nestingStack = new Stack<(Asn1Tag, int, UniversalTagNumber)>(); + _nestingStack = new Stack(); } Debug.Assert(tag.IsConstructed); WriteTag(tag); - _nestingStack.Push((tag, _offset, tagType)); + _nestingStack.Push(new StackFrame(tag, _offset, tagType)); // Indicate that the length is indefinite. // We'll come back and clean this up (as appropriate) in PopTag. WriteLength(-1); + return new Scope(this); } private void PopTag(Asn1Tag tag, UniversalTagNumber tagType, bool sortContents = false) { - CheckDisposed(); - if (_nestingStack == null || _nestingStack.Count == 0) { - throw new InvalidOperationException(SR.Cryptography_AsnWriter_PopWrongTag); + throw new InvalidOperationException(SR.AsnWriter_PopWrongTag); } (Asn1Tag stackTag, int lenOffset, UniversalTagNumber stackTagType) = _nestingStack.Peek(); @@ -436,13 +447,14 @@ private void PopTag(Asn1Tag tag, UniversalTagNumber tagType, bool sortContents = Debug.Assert(tag.IsConstructed); if (stackTag != tag || stackTagType != tagType) { - throw new InvalidOperationException(SR.Cryptography_AsnWriter_PopWrongTag); + throw new InvalidOperationException(SR.AsnWriter_PopWrongTag); } _nestingStack.Pop(); if (sortContents) { + Debug.Assert(tagType == UniversalTagNumber.SetOf); SortContents(_buffer, lenOffset + 1, _offset); } @@ -455,7 +467,7 @@ private void PopTag(Asn1Tag tag, UniversalTagNumber tagType, bool sortContents = // T-REC-X.690-201508 sec 9.1 (constructed CER => indefinite length) // T-REC-X.690-201508 sec 8.1.3.6 - if (RuleSet == AsnEncodingRules.CER) + if (RuleSet == AsnEncodingRules.CER && tagType != UniversalTagNumber.OctetString) { WriteEndOfContents(); return; @@ -464,6 +476,49 @@ private void PopTag(Asn1Tag tag, UniversalTagNumber tagType, bool sortContents = int containedLength = _offset - 1 - lenOffset; Debug.Assert(containedLength >= 0); + int start = lenOffset + 1; + + // T-REC-X.690-201508 sec 9.2 + // T-REC-X.690-201508 sec 10.2 + if (tagType == UniversalTagNumber.OctetString) + { + if (RuleSet != AsnEncodingRules.CER || containedLength <= AsnDecoder.MaxCERSegmentSize) + { + // Need to replace the tag with the primitive tag. + // Since the P/C bit doesn't affect the length, overwrite the tag. + int tagLen = tag.CalculateEncodedSize(); + tag.AsPrimitive().Encode(_buffer.AsSpan(lenOffset - tagLen, tagLen)); + // Continue with the regular flow. + } + else + { + int fullSegments = Math.DivRem( + containedLength, + AsnDecoder.MaxCERSegmentSize, + out int lastSegmentSize); + + int requiredPadding = + // Each full segment has a header of 048203E8 + 4 * fullSegments + + // The last one is 04 plus the encoded length. + 2 + GetEncodedLengthSubsequentByteCount(lastSegmentSize); + + // Shift the data forward so we can use right-source-overlapped + // copy in the existing method. + // Also, ensure the space for the end-of-contents marker. + EnsureWriteCapacity(requiredPadding + 2); + ReadOnlySpan src = _buffer.AsSpan(start, containedLength); + Span dest = _buffer.AsSpan(start + requiredPadding, containedLength); + src.CopyTo(dest); + + int expectedEnd = start + containedLength + requiredPadding + 2; + _offset = lenOffset - tag.CalculateEncodedSize(); + WriteConstructedCerOctetString(tag, dest); + Debug.Assert(_offset == expectedEnd); + return; + } + } + int shiftSize = GetEncodedLengthSubsequentByteCount(containedLength); // Best case, length fits in the compact byte @@ -477,7 +532,6 @@ private void PopTag(Asn1Tag tag, UniversalTagNumber tagType, bool sortContents = EnsureWriteCapacity(shiftSize); // Buffer.BlockCopy correctly does forward-overlapped, so use it. - int start = lenOffset + 1; Buffer.BlockCopy(_buffer, start, _buffer, start + shiftSize, containedLength); int tmp = _offset; @@ -555,13 +609,18 @@ internal static void Reverse(Span span) } } - private static void CheckUniversalTag(Asn1Tag tag, UniversalTagNumber universalTagNumber) + private static void CheckUniversalTag(Asn1Tag? tag, UniversalTagNumber universalTagNumber) { - if (tag.TagClass == TagClass.Universal && tag.TagValue != (int)universalTagNumber) + if (tag != null) { - throw new ArgumentException( - SR.Cryptography_Asn_UniversalValueIsFixed, - nameof(tag)); + Asn1Tag value = tag.Value; + + if (value.TagClass == TagClass.Universal && value.TagValue != (int)universalTagNumber) + { + throw new ArgumentException( + SR.Argument_UniversalValueIsFixed, + nameof(tag)); + } } } @@ -593,5 +652,92 @@ public int Compare((int, int) x, (int, int) y) return value; } } + + private readonly struct StackFrame : IEquatable + { + public Asn1Tag Tag { get; } + public int Offset { get; } + public UniversalTagNumber ItemType { get; } + + internal StackFrame(Asn1Tag tag, int offset, UniversalTagNumber itemType) + { + Tag = tag; + Offset = offset; + ItemType = itemType; + } + + public void Deconstruct(out Asn1Tag tag, out int offset, out UniversalTagNumber itemType) + { + tag = Tag; + offset = Offset; + itemType = ItemType; + } + + public bool Equals(StackFrame other) + { + return Tag.Equals(other.Tag) && Offset == other.Offset && ItemType == other.ItemType; + } + + public override bool Equals(object? obj) => obj is StackFrame other && Equals(other); + + public override int GetHashCode() + { + return (Tag, Offset, ItemType).GetHashCode(); + } + + public static bool operator ==(StackFrame left, StackFrame right) => left.Equals(right); + + public static bool operator !=(StackFrame left, StackFrame right) => !left.Equals(right); + } + + public readonly struct Scope : IDisposable + { + private readonly AsnWriter _writer; + private readonly StackFrame _frame; + private readonly int _depth; + + internal Scope(AsnWriter writer) + { + Debug.Assert(writer._nestingStack != null); + + _writer = writer; + _frame = _writer._nestingStack.Peek(); + _depth = _writer._nestingStack.Count; + } + + public void Dispose() + { + if (_writer == null || _writer._nestingStack.Count == 0) + { + return; + } + + if (_writer._nestingStack.Peek() == _frame) + { + switch (_frame.ItemType) + { + case UniversalTagNumber.SetOf: + _writer.PopSetOf(_frame.Tag); + break; + case UniversalTagNumber.Sequence: + _writer.PopSequence(_frame.Tag); + break; + case UniversalTagNumber.OctetString: + _writer.PopOctetString(_frame.Tag); + break; + default: + Debug.Fail($"No handler for {_frame.ItemType}"); + throw new InvalidOperationException(); + } + } + else if (_writer._nestingStack.Count > _depth && + _writer._nestingStack.Contains(_frame)) + { + // Another frame was pushed when we got disposed. + // Report the imbalance. + throw new InvalidOperationException(SR.AsnWriter_PopWrongTag); + } + } + } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/SetOfValueComparer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs similarity index 91% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/SetOfValueComparer.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs index ad9a9b8f649fa1..3f33f2b95eeb95 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/SetOfValueComparer.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { internal class SetOfValueComparer : IComparer> { @@ -42,12 +42,7 @@ internal static int Compare(ReadOnlySpan x, ReadOnlySpan y) // which will make diff != 0. diff = x.Length - y.Length; - if (diff != 0) - { - return diff; - } - - return 0; + return diff; } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/TagClass.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs similarity index 91% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/TagClass.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs index 87b7249a2ae318..81cb7c8c33c3ff 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/TagClass.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// The tag class for a particular ASN.1 tag. /// // Uses a masked overlay of the tag class encoding. // T-REC-X.690-201508 sec 8.1.2.2 - internal enum TagClass : byte + public enum TagClass { /// /// The Universal tag class diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/UniversalTagNumber.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs similarity index 98% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/UniversalTagNumber.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs index c5c2ad1f5e04cc..8709f1ce8c90d2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/UniversalTagNumber.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// Tag assignments for the UNIVERSAL class in ITU-T X.680. /// // ITU-T-REC.X.680-201508 sec 8.6 - internal enum UniversalTagNumber + public enum UniversalTagNumber { /// /// The reserved identifier for the End-of-Contents marker in an indefinite diff --git a/src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs b/src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs new file mode 100644 index 00000000000000..ef723f685e66a3 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests +{ + public sealed class Asn1TagTests + { + [Fact] + public static void Universal15UndefinedFromEnum() + { + AssertExtensions.Throws( + "universalTagNumber", + () => new Asn1Tag((UniversalTagNumber)15)); + } + + [Fact] + public static void Universal15OKFromVerbose() + { + Asn1Tag tag = new Asn1Tag(TagClass.Universal, 15); + Span encoded = stackalloc byte[1]; + + Assert.Equal(1, tag.CalculateEncodedSize()); + tag.Encode(encoded); + + Assert.Equal("0F", encoded.ByteArrayToHex()); + } + + [Theory] + [InlineData(-1)] + [InlineData(-2)] + [InlineData(int.MinValue)] + [InlineData(37)] + [InlineData(38)] + [InlineData(int.MaxValue)] + public static void UniversalValuesLimitedToEnum(int value) + { + AssertExtensions.Throws( + "universalTagNumber", + () => new Asn1Tag((UniversalTagNumber)value)); + } + + [Theory] + [InlineData(-1)] + [InlineData(-2)] + [InlineData(int.MinValue)] + [InlineData(4)] + [InlineData(5)] + [InlineData(int.MaxValue)] + public static void TagClassIsVerified(int value) + { + AssertExtensions.Throws( + "tagClass", + () => new Asn1Tag((TagClass)value, 1)); + } + + [Theory] + [InlineData(TagClass.Universal, -1)] + [InlineData(TagClass.ContextSpecific, -1)] + [InlineData(TagClass.Application, -1)] + [InlineData(TagClass.Private, -1)] + [InlineData(TagClass.Universal, -2)] + [InlineData(TagClass.ContextSpecific, -2)] + [InlineData(TagClass.Application, -2)] + [InlineData(TagClass.Private, -2)] + [InlineData(TagClass.Universal, int.MinValue)] + [InlineData(TagClass.ContextSpecific, int.MinValue)] + [InlineData(TagClass.Application, int.MinValue)] + [InlineData(TagClass.Private, int.MinValue)] + public static void NoNegativeTagNumbers(TagClass tagClass, int value) + { + AssertExtensions.Throws( + "tagValue", + () => new Asn1Tag(tagClass, value)); + } + + [Fact] + public static void EqualsIsExact() + { + Assert.False(Asn1Tag.PrimitiveOctetString.Equals(Asn1Tag.ConstructedOctetString)); + Assert.False(Asn1Tag.PrimitiveOctetString.Equals((object)Asn1Tag.ConstructedOctetString)); + Assert.True(Asn1Tag.PrimitiveOctetString.Equals(Asn1Tag.PrimitiveOctetString)); + Assert.True(Asn1Tag.PrimitiveOctetString.Equals((object)Asn1Tag.PrimitiveOctetString)); + } + + [Fact] + public static void HasSameClassAndValueIsSoft() + { + Assert.True(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(Asn1Tag.ConstructedOctetString)); + Assert.True(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(Asn1Tag.PrimitiveOctetString)); + Assert.False(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(Asn1Tag.PrimitiveBitString)); + Assert.False(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))); + } + + public static IEnumerable EncodeDecodeCases { get; } = + new[] + { + new object[] { TagClass.Universal, 4, false, "04" }, + new object[] { TagClass.Universal, 4, true, "24" }, + new object[] { TagClass.Application, 4, false, "44" }, + new object[] { TagClass.Application, 4, true, "64" }, + new object[] { TagClass.ContextSpecific, 4, false, "84" }, + new object[] { TagClass.ContextSpecific, 4, true, "A4" }, + new object[] { TagClass.Private, 4, false, "C4" }, + new object[] { TagClass.Private, 4, true, "E4" }, + new object[] { TagClass.Universal, 30, false, "1E" }, + new object[] { TagClass.Universal, 30, true, "3E" }, + new object[] { TagClass.Application, 30, false, "5E" }, + new object[] { TagClass.Application, 30, true, "7E" }, + new object[] { TagClass.ContextSpecific, 30, false, "9E" }, + new object[] { TagClass.ContextSpecific, 30, true, "BE" }, + new object[] { TagClass.Private, 30, false, "DE" }, + new object[] { TagClass.Private, 30, true, "FE" }, + new object[] { TagClass.Universal, 31, false, "1F1F" }, + new object[] { TagClass.Universal, 31, true, "3F1F" }, + new object[] { TagClass.Application, 31, false, "5F1F" }, + new object[] { TagClass.Application, 31, true, "7F1F" }, + new object[] { TagClass.ContextSpecific, 31, false, "9F1F" }, + new object[] { TagClass.ContextSpecific, 31, true, "BF1F" }, + new object[] { TagClass.Private, 31, false, "DF1F" }, + new object[] { TagClass.Private, 31, true, "FF1F" }, + new object[] { TagClass.Universal, 127, false, "1F7F" }, + new object[] { TagClass.Universal, 127, true, "3F7F" }, + new object[] { TagClass.Application, 127, false, "5F7F" }, + new object[] { TagClass.Application, 127, true, "7F7F" }, + new object[] { TagClass.ContextSpecific, 127, false, "9F7F" }, + new object[] { TagClass.ContextSpecific, 127, true, "BF7F" }, + new object[] { TagClass.Private, 127, false, "DF7F" }, + new object[] { TagClass.Private, 127, true, "FF7F" }, + new object[] { TagClass.Universal, 128, false, "1F8100" }, + new object[] { TagClass.Universal, 128, true, "3F8100" }, + new object[] { TagClass.Application, 128, false, "5F8100" }, + new object[] { TagClass.Application, 128, true, "7F8100" }, + new object[] { TagClass.ContextSpecific, 128, false, "9F8100" }, + new object[] { TagClass.ContextSpecific, 128, true, "BF8100" }, + new object[] { TagClass.Private, 128, false, "DF8100" }, + new object[] { TagClass.Private, 128, true, "FF8100" }, + new object[] { TagClass.Universal, 16383, false, "1FFF7F" }, + new object[] { TagClass.Universal, 16383, true, "3FFF7F" }, + new object[] { TagClass.Application, 16383, false, "5FFF7F" }, + new object[] { TagClass.Application, 16383, true, "7FFF7F" }, + new object[] { TagClass.ContextSpecific, 16383, false, "9FFF7F" }, + new object[] { TagClass.ContextSpecific, 16383, true, "BFFF7F" }, + new object[] { TagClass.Private, 16383, false, "DFFF7F" }, + new object[] { TagClass.Private, 16383, true, "FFFF7F" }, + new object[] { TagClass.Universal, 16384, false, "1F818000" }, + new object[] { TagClass.Universal, 16384, true, "3F818000" }, + new object[] { TagClass.Application, 16384, false, "5F818000" }, + new object[] { TagClass.Application, 16384, true, "7F818000" }, + new object[] { TagClass.ContextSpecific, 16384, false, "9F818000" }, + new object[] { TagClass.ContextSpecific, 16384, true, "BF818000" }, + new object[] { TagClass.Private, 16384, false, "DF818000" }, + new object[] { TagClass.Private, 16384, true, "FF818000" }, + new object[] { TagClass.Universal, int.MaxValue, false, "1F87FFFFFF7F" }, + new object[] { TagClass.Universal, int.MaxValue, true, "3F87FFFFFF7F" }, + new object[] { TagClass.Application, int.MaxValue, false, "5F87FFFFFF7F" }, + new object[] { TagClass.Application, int.MaxValue, true, "7F87FFFFFF7F" }, + new object[] { TagClass.ContextSpecific, int.MaxValue, false, "9F87FFFFFF7F" }, + new object[] { TagClass.ContextSpecific, int.MaxValue, true, "BF87FFFFFF7F" }, + new object[] { TagClass.Private, int.MaxValue, false, "DF87FFFFFF7F" }, + new object[] { TagClass.Private, int.MaxValue, true, "FF87FFFFFF7F" }, + }; + + [Theory] + [MemberData(nameof(EncodeDecodeCases))] + public static void VerifyEncode(TagClass tagClass, int tagValue, bool constructed, string expectedHex) + { + Asn1Tag tag = new Asn1Tag(tagClass, tagValue, constructed); + Span buf = stackalloc byte[10]; + + Assert.False(tag.TryEncode(Span.Empty, out int written)); + Assert.Equal(0, written); + + int expectedSize = expectedHex.Length / 2; + Assert.Equal(expectedSize, tag.CalculateEncodedSize()); + + Assert.False(tag.TryEncode(buf.Slice(0, expectedSize - 1), out written)); + Assert.Equal(0, written); + + AssertExtensions.Throws( + "destination", + () => + { + Span tmp = stackalloc byte[expectedSize - 1]; + return tag.Encode(tmp); + }); + + Assert.True(tag.TryEncode(buf, out written)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(0, written).ByteArrayToHex()); + + written = tag.Encode(buf.Slice(1)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(1, written).ByteArrayToHex()); + + Assert.True(tag.TryEncode(buf.Slice(0, expectedSize), out written)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(0, written).ByteArrayToHex()); + + written = tag.Encode(buf.Slice(1, expectedSize)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(1, written).ByteArrayToHex()); + } + + [Theory] + [MemberData(nameof(EncodeDecodeCases))] + public static void VerifyDecode(TagClass tagClass, int tagValue, bool constructed, string inputHex) + { + Asn1Tag expectedTag = new Asn1Tag(tagClass, tagValue, constructed); + byte[] input = inputHex.HexToByteArray(); + byte[] padded = input; + Array.Resize(ref padded, input.Length + 3); + + int consumed; + Asn1Tag tag; + + Assert.False(Asn1Tag.TryDecode(input.AsSpan(0, input.Length - 1), out tag, out consumed)); + Assert.Equal(0, consumed); + Assert.Equal(default(Asn1Tag), tag); + + Assert.Throws(() => Asn1Tag.Decode(input.AsSpan(0, input.Length - 1), out consumed)); + Assert.Equal(0, consumed); + + Assert.True(Asn1Tag.TryDecode(padded, out tag, out consumed)); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + + Assert.True(Asn1Tag.TryDecode(input, out tag, out consumed)); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + + tag = Asn1Tag.Decode(padded, out consumed); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + + tag = Asn1Tag.Decode(input, out consumed); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs similarity index 93% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs index 2291e69b11dccc..7640b13aec50fe 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs @@ -4,13 +4,11 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; +using X509KeyUsageCSharpStyle=System.Formats.Asn1.Tests.Reader.ReadNamedBitList.X509KeyUsageCSharpStyle; -using X509KeyUsageCSharpStyle=System.Security.Cryptography.Tests.Asn1.ReadNamedBitList.X509KeyUsageCSharpStyle; - -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { public static class ComprehensiveReadTests { @@ -27,7 +25,7 @@ public static void ReadMicrosoftComCert() AsnReader sigAlgReader = certReader.ReadSequence(); Assert.True( - certReader.TryReadPrimitiveBitStringValue( + certReader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory signature), "certReader.TryReadPrimitiveBitStringValue"); @@ -46,7 +44,7 @@ public static void ReadMicrosoftComCert() AssertRefSame(serialBytes, ref bytes[15], "Serial number starts at bytes[15]"); AsnReader tbsSigAlgReader = tbsCertReader.ReadSequence(); - Assert.Equal("1.2.840.113549.1.1.11", tbsSigAlgReader.ReadObjectIdentifierAsString()); + Assert.Equal("1.2.840.113549.1.1.11", tbsSigAlgReader.ReadObjectIdentifier()); Assert.True(tbsSigAlgReader.HasData, "tbsSigAlgReader.HasData before ReadNull"); tbsSigAlgReader.ReadNull(); Assert.False(tbsSigAlgReader.HasData, "tbsSigAlgReader.HasData after ReadNull"); @@ -82,12 +80,12 @@ public static void ReadMicrosoftComCert() AsnReader subjectPublicKeyInfo = tbsCertReader.ReadSequence(); AsnReader spkiAlgorithm = subjectPublicKeyInfo.ReadSequence(); - Assert.Equal("1.2.840.113549.1.1.1", spkiAlgorithm.ReadObjectIdentifierAsString()); + Assert.Equal("1.2.840.113549.1.1.1", spkiAlgorithm.ReadObjectIdentifier()); spkiAlgorithm.ReadNull(); Assert.False(spkiAlgorithm.HasData, "spkiAlgorithm.HasData"); Assert.True( - subjectPublicKeyInfo.TryReadPrimitiveBitStringValue( + subjectPublicKeyInfo.TryReadPrimitiveBitString( out unusedBitCount, out ReadOnlyMemory encodedPublicKey), "subjectPublicKeyInfo.TryReadBitStringBytes"); @@ -110,21 +108,21 @@ public static void ReadMicrosoftComCert() Assert.False(extensionsContainer.HasData, "extensionsContainer.HasData"); AsnReader sanExtension = extensions.ReadSequence(); - Assert.Equal("2.5.29.17", sanExtension.ReadObjectIdentifierAsString()); - Assert.True(sanExtension.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory sanExtensionBytes)); + Assert.Equal("2.5.29.17", sanExtension.ReadObjectIdentifier()); + Assert.True(sanExtension.TryReadPrimitiveOctetString(out ReadOnlyMemory sanExtensionBytes)); Assert.False(sanExtension.HasData, "sanExtension.HasData"); AsnReader sanExtensionPayload = new AsnReader(sanExtensionBytes, AsnEncodingRules.DER); AsnReader sanExtensionValue = sanExtensionPayload.ReadSequence(); Assert.False(sanExtensionPayload.HasData, "sanExtensionPayload.HasData"); Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); - Assert.Equal("www.microsoft.com", sanExtensionValue.ReadCharacterString(dnsName, UniversalTagNumber.IA5String)); - Assert.Equal("wwwqa.microsoft.com", sanExtensionValue.ReadCharacterString(dnsName, UniversalTagNumber.IA5String)); + Assert.Equal("www.microsoft.com", sanExtensionValue.ReadCharacterString(UniversalTagNumber.IA5String, dnsName)); + Assert.Equal("wwwqa.microsoft.com", sanExtensionValue.ReadCharacterString(UniversalTagNumber.IA5String, dnsName)); Assert.False(sanExtensionValue.HasData, "sanExtensionValue.HasData"); AsnReader basicConstraints = extensions.ReadSequence(); - Assert.Equal("2.5.29.19", basicConstraints.ReadObjectIdentifierAsString()); - Assert.True(basicConstraints.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory basicConstraintsBytes)); + Assert.Equal("2.5.29.19", basicConstraints.ReadObjectIdentifier()); + Assert.True(basicConstraints.TryReadPrimitiveOctetString(out ReadOnlyMemory basicConstraintsBytes)); AsnReader basicConstraintsPayload = new AsnReader(basicConstraintsBytes, AsnEncodingRules.DER); AsnReader basicConstraintsValue = basicConstraintsPayload.ReadSequence(); @@ -132,9 +130,9 @@ public static void ReadMicrosoftComCert() Assert.False(basicConstraintsPayload.HasData, "basicConstraintsPayload.HasData"); AsnReader keyUsageExtension = extensions.ReadSequence(); - Assert.Equal("2.5.29.15", keyUsageExtension.ReadObjectIdentifierAsString()); + Assert.Equal("2.5.29.15", keyUsageExtension.ReadObjectIdentifier()); Assert.True(keyUsageExtension.ReadBoolean(), "keyUsageExtension.ReadBoolean() (IsCritical)"); - Assert.True(keyUsageExtension.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory keyUsageBytes)); + Assert.True(keyUsageExtension.TryReadPrimitiveOctetString(out ReadOnlyMemory keyUsageBytes)); AsnReader keyUsagePayload = new AsnReader(keyUsageBytes, AsnEncodingRules.DER); @@ -151,7 +149,7 @@ public static void ReadMicrosoftComCert() AssertExtension(extensions, "1.3.6.1.5.5.7.1.1", false, 1081, bytes); Assert.False(extensions.HasData, "extensions.HasData"); - Assert.Equal("1.2.840.113549.1.1.11", sigAlgReader.ReadObjectIdentifierAsString()); + Assert.Equal("1.2.840.113549.1.1.11", sigAlgReader.ReadObjectIdentifier()); sigAlgReader.ReadNull(); Assert.False(sigAlgReader.HasData); } @@ -159,14 +157,14 @@ public static void ReadMicrosoftComCert() private static void AssertExtension(AsnReader extensions, string oid, bool critical, int index, byte[] bytes) { AsnReader extension = extensions.ReadSequence(); - Assert.Equal(oid, extension.ReadObjectIdentifierAsString()); + Assert.Equal(oid, extension.ReadObjectIdentifier()); if (critical) { Assert.True(extension.ReadBoolean(), $"{oid} is critical"); } - Assert.True(extension.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory extensionBytes)); + Assert.True(extension.TryReadPrimitiveOctetString(out ReadOnlyMemory extensionBytes)); AssertRefSame(extensionBytes, ref bytes[index], $"{oid} extension value is at byte {index}"); } @@ -181,7 +179,7 @@ private static void AssertRdn( { AsnReader rdn = reader.ReadSetOf(); AsnReader attributeTypeAndValue = rdn.ReadSequence(); - Assert.Equal(atvOid, attributeTypeAndValue.ReadObjectIdentifierAsString()); + Assert.Equal(atvOid, attributeTypeAndValue.ReadObjectIdentifier()); ReadOnlyMemory value = attributeTypeAndValue.ReadEncodedValue(); ReadOnlySpan valueSpan = value.Span; diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/OverlappedReads.cs b/src/libraries/System.Formats.Asn1/tests/Reader/OverlappedReads.cs new file mode 100644 index 00000000000000..ccc36932bd11a4 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/OverlappedReads.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public static class OverlappedReads + { + private delegate bool TryWriteMethod( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int bytesConsumed, + out int bytesWritten); + + [Fact] + public static void NoOverlappedBitStrings() + { + static bool Method( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int consumed, + out int written) + { + bool ret = AsnDecoder.TryReadBitString( + source, + destination, + ruleSet, + out int unusedBitCount, + out consumed, + out written); + + if (ret) + { + Assert.Equal(2, unusedBitCount); + } + + return ret; + } + + byte[] input = { 0x00, 0x00, 0x03, 0x02, 0x02, 0x3C, 0x00 }; + + NoOverlappedReads( + input, + encodedValueOffset: 2, + encodedValueLength: 4, + copyLength: 1, + Method); + } + + [Fact] + public static void NoOverlappedOctetStrings() + { + static bool Method( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int consumed, + out int written) + { + return AsnDecoder.TryReadOctetString( + source, + destination, + ruleSet, + out consumed, + out written); + } + + byte[] input = { 0x00, 0x00, 0x04, 0x01, 0x21, 0x00 }; + + NoOverlappedReads( + input, + encodedValueOffset: 2, + encodedValueLength: 3, + copyLength: 1, + Method); + } + + [Fact] + public static void NoOverlappedTextStrings() + { + static bool Method( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int consumed, + out int written) + { + return AsnDecoder.TryReadCharacterStringBytes( + source, + destination, + ruleSet, + new Asn1Tag(UniversalTagNumber.UTF8String), + out consumed, + out written); + } + + byte[] input = { 0x00, 0x00, 0x0C, 0x01, 0x30, 0x00 }; + + NoOverlappedReads( + input, + encodedValueOffset: 2, + encodedValueLength: 3, + copyLength: 1, + Method); + } + + private static void NoOverlappedReads( + byte[] input, + int encodedValueOffset, + int encodedValueLength, + int copyLength, + TryWriteMethod tryWriteMethod) + { + // The write starts beyond the portion of source that will be read, + // but that hasn't yet been determined. + AssertExtensions.Throws( + "destination", + () => tryWriteMethod( + input.AsSpan(encodedValueOffset), + input.AsSpan(encodedValueOffset + encodedValueLength), + AsnEncodingRules.BER, + out _, + out _)); + + // The CopyTo would actually end up with source == dest for this one + AssertExtensions.Throws( + "destination", + () => tryWriteMethod( + input.AsSpan(encodedValueOffset), + input.AsSpan(encodedValueOffset + encodedValueLength - copyLength, copyLength), + AsnEncodingRules.BER, + out _, + out _)); + + // destination[1] is source[0], but there isn't actually an overwrite because + // the value length isn't long enough. + AssertExtensions.Throws( + "destination", + () => tryWriteMethod( + input.AsSpan(encodedValueOffset), + input.AsSpan(encodedValueOffset - copyLength, copyLength + 1), + AsnEncodingRules.BER, + out _, + out _)); + + Assert.True( + tryWriteMethod( + input.AsSpan(encodedValueOffset, encodedValueLength), + input.AsSpan(encodedValueOffset + encodedValueLength, copyLength), + AsnEncodingRules.BER, + out int bytesConsumed, + out int bytesWritten)); + + Assert.Equal(encodedValueLength, bytesConsumed); + Assert.Equal(copyLength, bytesWritten); + Assert.Equal( + input[encodedValueOffset + encodedValueLength - copyLength], + input[encodedValueOffset + encodedValueLength]); + + Assert.True( + tryWriteMethod( + input.AsSpan(encodedValueOffset, encodedValueLength), + input.AsSpan(0, copyLength), + AsnEncodingRules.BER, + out bytesConsumed, + out bytesWritten)); + + Assert.Equal(encodedValueLength, bytesConsumed); + Assert.Equal(copyLength, bytesWritten); + Assert.Equal(input[encodedValueOffset + encodedValueLength - copyLength], input[0]); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs similarity index 58% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs index 15e5c69786740b..5042a4cc0419f4 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs @@ -2,49 +2,48 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ParseTag : Asn1ReaderTests + public sealed class ParseTag { [Theory] - [InlineData(PublicTagClass.Universal, false, 0, "00")] - [InlineData(PublicTagClass.Universal, false, 1, "01")] - [InlineData(PublicTagClass.Application, true, 1, "61")] - [InlineData(PublicTagClass.ContextSpecific, false, 1, "81")] - [InlineData(PublicTagClass.ContextSpecific, true, 1, "A1")] - [InlineData(PublicTagClass.Private, false, 1, "C1")] - [InlineData(PublicTagClass.Universal, false, 30, "1E")] - [InlineData(PublicTagClass.Application, false, 30, "5E")] - [InlineData(PublicTagClass.ContextSpecific, false, 30, "9E")] - [InlineData(PublicTagClass.Private, false, 30, "DE")] - [InlineData(PublicTagClass.Universal, false, 31, "1F1F")] - [InlineData(PublicTagClass.Application, false, 31, "5F1F")] - [InlineData(PublicTagClass.ContextSpecific, false, 31, "9F1F")] - [InlineData(PublicTagClass.Private, false, 31, "DF1F")] - [InlineData(PublicTagClass.Private, false, 127, "DF7F")] - [InlineData(PublicTagClass.Private, false, 128, "DF8100")] - [InlineData(PublicTagClass.Private, false, 253, "DF817D")] - [InlineData(PublicTagClass.Private, false, 255, "DF817F")] - [InlineData(PublicTagClass.Private, false, 256, "DF8200")] - [InlineData(PublicTagClass.Private, false, 1 << 9, "DF8400")] - [InlineData(PublicTagClass.Private, false, 1 << 10, "DF8800")] - [InlineData(PublicTagClass.Private, false, 0b0011_1101_1110_0111, "DFFB67")] - [InlineData(PublicTagClass.Private, false, 1 << 14, "DF818000")] - [InlineData(PublicTagClass.Private, false, 1 << 18, "DF908000")] - [InlineData(PublicTagClass.Private, false, 1 << 18 | 1 << 9, "DF908400")] - [InlineData(PublicTagClass.Private, false, 1 << 20, "DFC08000")] - [InlineData(PublicTagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] - [InlineData(PublicTagClass.Private, false, 1 << 21, "DF81808000")] - [InlineData(PublicTagClass.Private, false, 1 << 27, "DFC0808000")] - [InlineData(PublicTagClass.Private, false, 1 << 28, "DF8180808000")] - [InlineData(PublicTagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] - [InlineData(PublicTagClass.Universal, false, 119, "1F77")] + [InlineData(TagClass.Universal, false, 0, "00")] + [InlineData(TagClass.Universal, false, 1, "01")] + [InlineData(TagClass.Application, true, 1, "61")] + [InlineData(TagClass.ContextSpecific, false, 1, "81")] + [InlineData(TagClass.ContextSpecific, true, 1, "A1")] + [InlineData(TagClass.Private, false, 1, "C1")] + [InlineData(TagClass.Universal, false, 30, "1E")] + [InlineData(TagClass.Application, false, 30, "5E")] + [InlineData(TagClass.ContextSpecific, false, 30, "9E")] + [InlineData(TagClass.Private, false, 30, "DE")] + [InlineData(TagClass.Universal, false, 31, "1F1F")] + [InlineData(TagClass.Application, false, 31, "5F1F")] + [InlineData(TagClass.ContextSpecific, false, 31, "9F1F")] + [InlineData(TagClass.Private, false, 31, "DF1F")] + [InlineData(TagClass.Private, false, 127, "DF7F")] + [InlineData(TagClass.Private, false, 128, "DF8100")] + [InlineData(TagClass.Private, false, 253, "DF817D")] + [InlineData(TagClass.Private, false, 255, "DF817F")] + [InlineData(TagClass.Private, false, 256, "DF8200")] + [InlineData(TagClass.Private, false, 1 << 9, "DF8400")] + [InlineData(TagClass.Private, false, 1 << 10, "DF8800")] + [InlineData(TagClass.Private, false, 0b0011_1101_1110_0111, "DFFB67")] + [InlineData(TagClass.Private, false, 1 << 14, "DF818000")] + [InlineData(TagClass.Private, false, 1 << 18, "DF908000")] + [InlineData(TagClass.Private, false, 1 << 18 | 1 << 9, "DF908400")] + [InlineData(TagClass.Private, false, 1 << 20, "DFC08000")] + [InlineData(TagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] + [InlineData(TagClass.Private, false, 1 << 21, "DF81808000")] + [InlineData(TagClass.Private, false, 1 << 27, "DFC0808000")] + [InlineData(TagClass.Private, false, 1 << 28, "DF8180808000")] + [InlineData(TagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] + [InlineData(TagClass.Universal, false, 119, "1F77")] public static void ParseValidTag( - PublicTagClass tagClass, + TagClass tagClass, bool isConstructed, int tagValue, string inputHex) @@ -55,7 +54,7 @@ public static void ParseValidTag( Assert.True(parsed, "Asn1Tag.TryDecode"); Assert.Equal(inputBytes.Length, bytesRead); - Assert.Equal((TagClass)tagClass, tag.TagClass); + Assert.Equal(tagClass, tag.TagClass); Assert.Equal(tagValue, tag.TagValue); if (isConstructed) @@ -95,6 +94,20 @@ public static void ParseCorruptTag(string description, string inputHex) Assert.Equal(default(Asn1Tag), tag); Assert.Equal(0, bytesRead); + + Assert.False( + AsnDecoder.TryReadEncodedValue( + inputBytes, + AsnEncodingRules.BER, + out tag, + out int contentOffset, + out int contentLength, + out int bytesConsumed)); + + Assert.Equal(0, contentOffset); + Assert.Equal(0, contentLength); + Assert.Equal(0, bytesConsumed); + Assert.Equal(default(Asn1Tag), tag); } [Fact] @@ -126,14 +139,14 @@ public static void TestEquals() } [Theory] - [InlineData(PublicTagClass.Universal, false, 0, "00")] - [InlineData(PublicTagClass.ContextSpecific, true, 1, "A1")] - [InlineData(PublicTagClass.Application, false, 31, "5F1F")] - [InlineData(PublicTagClass.Private, false, 128, "DF8100")] - [InlineData(PublicTagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] - [InlineData(PublicTagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] + [InlineData(TagClass.Universal, false, 0, "00")] + [InlineData(TagClass.ContextSpecific, true, 1, "A1")] + [InlineData(TagClass.Application, false, 31, "5F1F")] + [InlineData(TagClass.Private, false, 128, "DF8100")] + [InlineData(TagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] + [InlineData(TagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] public static void ParseTagWithMoreData( - PublicTagClass tagClass, + TagClass tagClass, bool isConstructed, int tagValue, string inputHex) @@ -145,7 +158,7 @@ public static void ParseTagWithMoreData( Assert.True(parsed, "Asn1Tag.TryDecode"); Assert.Equal(inputHex.Length / 2, bytesRead); - Assert.Equal((TagClass)tagClass, tag.TagClass); + Assert.Equal(tagClass, tag.TagClass); Assert.Equal(tagValue, tag.TagValue); if (isConstructed) diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs similarity index 84% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs index 1ed43701da4b18..4ccb24c3506a88 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs @@ -4,13 +4,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class PeekTests : Asn1ReaderTests + public sealed class PeekTests { [Fact] public static void ReaderPeekTag_Valid() @@ -32,14 +31,7 @@ public static void ReaderPeekTag_Invalid() byte[] data = { 0x1F }; AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); - try - { - reader.PeekTag(); - Assert.True(false, "CryptographicException was thrown"); - } - catch (CryptographicException) - { - } + Assert.Throws(() => reader.PeekTag()); } [Fact] @@ -81,7 +73,7 @@ public static void PeekEncodedValue_Corrupt_Throws() byte[] data = (EncodedValue + "0500").HexToByteArray(); - Assert.Throws( + Assert.Throws( () => { AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); @@ -128,7 +120,7 @@ public static void PeekContentSpan_Corrupt_Throws() byte[] data = (EncodedValue + "0500").HexToByteArray(); - Assert.Throws( + Assert.Throws( () => { AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); @@ -209,6 +201,20 @@ public static void PeekEncodedValue_ExtremelyNested(bool fullArray) ReadOnlyMemory contents = reader.PeekEncodedValue(); Assert.Equal(expectedLength, contents.Length); Assert.True(Unsafe.AreSame(ref dataBytes[0], ref MemoryMarshal.GetReference(contents.Span))); + + Assert.True( + AsnDecoder.TryReadEncodedValue( + dataBytes, + AsnEncodingRules.BER, + out Asn1Tag tag, + out int contentOffset, + out int contentLength, + out int bytesConsumed)); + + Assert.Equal(2, contentOffset); + Assert.Equal(expectedLength - 4, contentLength); + Assert.Equal(expectedLength, bytesConsumed); + Assert.Equal(new Asn1Tag(TagClass.ContextSpecific, 0, true), tag); } [Fact] @@ -218,21 +224,22 @@ public static void PeekEncodedValue_InvalidLength() AsnReader reader = new AsnReader(badLength, AsnEncodingRules.BER); - Assert.Throws(() => reader.PeekEncodedValue()); - Assert.Throws(() => reader.ReadEncodedValue()); - - Assert.Throws( - () => - { - AsnValueReader valueReader = new AsnValueReader(badLength, AsnEncodingRules.BER); - valueReader.PeekEncodedValue(); - }); - Assert.Throws( - () => - { - AsnValueReader valueReader = new AsnValueReader(badLength, AsnEncodingRules.BER); - valueReader.ReadEncodedValue(); - }); + Assert.Throws(() => reader.PeekEncodedValue()); + Assert.Throws(() => reader.ReadEncodedValue()); + + Assert.False( + AsnDecoder.TryReadEncodedValue( + badLength, + AsnEncodingRules.BER, + out Asn1Tag tag, + out int contentOffset, + out int contentLength, + out int bytesConsumed)); + + Assert.Equal(0, contentOffset); + Assert.Equal(0, contentLength); + Assert.Equal(0, bytesConsumed); + Assert.Equal(default(Asn1Tag), tag); } [Fact] @@ -242,13 +249,7 @@ public static void PeekContentBytes_InvalidLength() AsnReader reader = new AsnReader(badLength, AsnEncodingRules.BER); - Assert.Throws(() => reader.PeekContentBytes()); - Assert.Throws( - () => - { - AsnValueReader valueReader = new AsnValueReader(badLength, AsnEncodingRules.BER); - valueReader.PeekContentBytes(); - }); + Assert.Throws(() => reader.PeekContentBytes()); } } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs similarity index 57% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs index 48afbb1c7e79a8..751802f61ad5b2 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs @@ -5,80 +5,79 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadBMPString : Asn1ReaderTests + public sealed class ReadBMPString { public static IEnumerable ValidEncodingData { get; } = new object[][] { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1E1A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1E1A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1E1A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E80" + "041A004A006F0068006E00200051002E00200053006D006900740068" + "0000", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E1C" + "041A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1E00", "", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1E00", "", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1E00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E80" + "0000", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E80" + "2480" + // "Dr." @@ -123,12 +122,12 @@ public sealed class ReadBMPString : Asn1ReaderTests [Theory] [MemberData(nameof(ValidEncodingData))] public static void GetBMPString_Success( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); string value = reader.ReadCharacterString(UniversalTagNumber.BMPString); Assert.Equal(expectedValue, value); @@ -137,14 +136,14 @@ public static void GetBMPString_Success( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyBMPString( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); char[] output = new char[expectedValue.Length]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int charsWritten; @@ -174,7 +173,7 @@ public static void TryCopyBMPString( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyBMPStringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedString) { @@ -182,7 +181,7 @@ public static void TryCopyBMPStringBytes( string expectedHex = Text.Encoding.BigEndianUnicode.GetBytes(expectedString).ByteArrayToHex(); byte[] output = new byte[expectedHex.Length / 2]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int bytesWritten; @@ -211,16 +210,16 @@ public static void TryCopyBMPStringBytes( } [Theory] - [InlineData(PublicEncodingRules.BER, "1E020020", true)] - [InlineData(PublicEncodingRules.BER, "3E80" + "04020020" + "0000", false)] - [InlineData(PublicEncodingRules.BER, "3E04" + "04020020", false)] + [InlineData(AsnEncodingRules.BER, "1E020020", true)] + [InlineData(AsnEncodingRules.BER, "3E80" + "04020020" + "0000", false)] + [InlineData(AsnEncodingRules.BER, "3E04" + "04020020", false)] public static void TryReadBMPStringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, bool expectSuccess) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool got = reader.TryReadBMPStringBytes(out ReadOnlyMemory contents); @@ -241,29 +240,29 @@ ref MemoryMarshal.GetReference(contents.Span), } [Theory] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "1E0600480069")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "1E0600480069")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "1E0600480069")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "3E0404020049")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.CER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.DER, "1E")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1E02")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "1E0600480069")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "1E0600480069")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "1E0600480069")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "3E0404020049")] public static void TryReadBMPStringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => { reader.TryReadBMPStringBytes(out ReadOnlyMemory contents); @@ -271,51 +270,51 @@ public static void TryReadBMPStringBytes_Throws( } [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] - [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3E02")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3E80")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3E80")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "1E034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "1E034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "1E034869")] - [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3E03040149")] - [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3E03040149")] - [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "3E800401490000")] - [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "3E800401490000")] - [InlineData("No nested content", PublicEncodingRules.CER, "3E800000")] - [InlineData("No EoC", PublicEncodingRules.BER, "3E80" + "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] - [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3E04" + "1E024869")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "3E04800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3E80800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3E80800400FACE0000")] - [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3E07" + ("2402" + "0404") + "04020049")] - [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3E03" + "040548656C6C6F")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "3E8020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "3E8020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3E80000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3E80000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3E80008100")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.CER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.DER, "1E")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1E02")] + [InlineData("Missing Contents - Constructed", AsnEncodingRules.BER, "3E02")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.BER, "3E80")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.CER, "3E80")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "1E034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "1E034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "1E034869")] + [InlineData("Definite Constructed Form", AsnEncodingRules.CER, "3E03040149")] + [InlineData("Definite Constructed Form", AsnEncodingRules.DER, "3E03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", AsnEncodingRules.CER, "3E800401490000")] + [InlineData("Indefinite Constructed Form", AsnEncodingRules.DER, "3E800401490000")] + [InlineData("No nested content", AsnEncodingRules.CER, "3E800000")] + [InlineData("No EoC", AsnEncodingRules.BER, "3E80" + "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", AsnEncodingRules.BER, "3E04" + "1E024869")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "3E04800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "3E80800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "3E80800400FACE0000")] + [InlineData("Nested Length Too Long", AsnEncodingRules.BER, "3E07" + ("2402" + "0404") + "04020049")] + [InlineData("Nested Simple Length Too Long", AsnEncodingRules.BER, "3E03" + "040548656C6C6F")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "3E8020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "3E8020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "3E80000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "3E80000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "3E80008100")] public static void TryCopyBMPStringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; @@ -325,9 +324,9 @@ public static void TryCopyBMPStringBytes_Throws( int bytesWritten = -1; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => { reader.TryCopyBMPStringBytes(outputData, out bytesWritten); @@ -337,15 +336,15 @@ public static void TryCopyBMPStringBytes_Throws( Assert.Equal(252, outputData[0]); } - private static void TryCopyBMPString_Throws_Helper(PublicEncodingRules ruleSet, byte[] inputData) + private static void TryCopyBMPString_Throws_Helper(AsnEncodingRules ruleSet, byte[] inputData) { char[] outputData = new char[inputData.Length + 1]; outputData[0] = 'a'; int bytesWritten = -1; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => { reader.TryCopyBMPString( @@ -358,34 +357,34 @@ private static void TryCopyBMPString_Throws_Helper(PublicEncodingRules ruleSet, } [Theory] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "1E0600480069")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "1E0600480069")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "1E0600480069")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "3E0404020049")] - [InlineData("Bad BMP value (odd length)", PublicEncodingRules.BER, "1E0120")] - [InlineData("Bad BMP value (high surrogate)", PublicEncodingRules.BER, "1E02D800")] - [InlineData("Bad BMP value (high private surrogate)", PublicEncodingRules.BER, "1E02DB81")] - [InlineData("Bad BMP value (low surrogate)", PublicEncodingRules.BER, "1E02DC00")] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "04024869")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.CER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.DER, "1E")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1E02")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "1E0600480069")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "1E0600480069")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "1E0600480069")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "3E0404020049")] + [InlineData("Bad BMP value (odd length)", AsnEncodingRules.BER, "1E0120")] + [InlineData("Bad BMP value (high surrogate)", AsnEncodingRules.BER, "1E02D800")] + [InlineData("Bad BMP value (high private surrogate)", AsnEncodingRules.BER, "1E02DB81")] + [InlineData("Bad BMP value (low surrogate)", AsnEncodingRules.BER, "1E02DC00")] + [InlineData("Wrong Tag", AsnEncodingRules.BER, "04024869")] public static void GetBMPString_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => { reader.ReadCharacterString(UniversalTagNumber.BMPString); @@ -393,55 +392,55 @@ public static void GetBMPString_Throws( } [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.CER, "1E")] - [InlineData("Missing Length", PublicEncodingRules.DER, "1E")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1E02")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1E02")] - [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3E02")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3E80")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3E80")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "1E034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "1E034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "1E034869")] - [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3E03040149")] - [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3E03040149")] - [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "3E800401490000")] - [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "3E800401490000")] - [InlineData("No nested content", PublicEncodingRules.CER, "3E800000")] - [InlineData("No EoC", PublicEncodingRules.BER, "3E80" + "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] - [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3E04" + "1E024869")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "3E04800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3E80800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3E80800400FACE0000")] - [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3E07" + ("2402" + "0404") + "04020049")] - [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3E03" + "040548656C6C6F")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "3E8020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "3E8020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3E80000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3E80000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3E80008100")] - [InlineData("Bad BMP value (odd length)", PublicEncodingRules.BER, "1E0120")] - [InlineData("Bad BMP value (high surrogate)", PublicEncodingRules.BER, "1E02D800")] - [InlineData("Bad BMP value (high private surrogate)", PublicEncodingRules.BER, "1E02DB81")] - [InlineData("Bad BMP value (low surrogate)", PublicEncodingRules.BER, "1E02DC00")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.CER, "1E")] + [InlineData("Missing Length", AsnEncodingRules.DER, "1E")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1E02")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1E02")] + [InlineData("Missing Contents - Constructed", AsnEncodingRules.BER, "3E02")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.BER, "3E80")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.CER, "3E80")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "1E034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "1E034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "1E034869")] + [InlineData("Definite Constructed Form", AsnEncodingRules.CER, "3E03040149")] + [InlineData("Definite Constructed Form", AsnEncodingRules.DER, "3E03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", AsnEncodingRules.CER, "3E800401490000")] + [InlineData("Indefinite Constructed Form", AsnEncodingRules.DER, "3E800401490000")] + [InlineData("No nested content", AsnEncodingRules.CER, "3E800000")] + [InlineData("No EoC", AsnEncodingRules.BER, "3E80" + "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", AsnEncodingRules.BER, "3E04" + "1E024869")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "3E04800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "3E80800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "3E80800400FACE0000")] + [InlineData("Nested Length Too Long", AsnEncodingRules.BER, "3E07" + ("2402" + "0404") + "04020049")] + [InlineData("Nested Simple Length Too Long", AsnEncodingRules.BER, "3E03" + "040548656C6C6F")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "3E8020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "3E8020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "3E80000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "3E80000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "3E80008100")] + [InlineData("Bad BMP value (odd length)", AsnEncodingRules.BER, "1E0120")] + [InlineData("Bad BMP value (high surrogate)", AsnEncodingRules.BER, "1E02D800")] + [InlineData("Bad BMP value (high private surrogate)", AsnEncodingRules.BER, "1E02DB81")] + [InlineData("Bad BMP value (low surrogate)", AsnEncodingRules.BER, "1E02DC00")] public static void TryCopyBMPString_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; @@ -475,7 +474,7 @@ public static void TryCopyBMPString_Throws_CER_NestedTooLong() input[5] = 0xE9; // EOC implicit since the byte[] initializes to zeros - TryCopyBMPString_Throws_Helper(PublicEncodingRules.CER, input); + TryCopyBMPString_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -513,7 +512,7 @@ public static void TryCopyBMPString_Throws_CER_NestedTooShortIntermediate() input[1011] = 0x02; // EOC implicit since the byte[] initializes to zeros - TryCopyBMPString_Throws_Helper(PublicEncodingRules.CER, input); + TryCopyBMPString_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -615,13 +614,13 @@ public static void TryCopyBMPStringBytes_Success_CER_MinConstructedLength() } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = { 0x1E, 4, 0, (byte)'h', 0, (byte)'i' }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -629,7 +628,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.TryReadBMPStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -640,13 +639,13 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = { 0x87, 2, 0x20, 0x10 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -654,16 +653,16 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.TryReadBMPStringBytes(out _)); + Assert.Throws(() => reader.TryReadBMPStringBytes(out _)); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.TryReadBMPStringBytes(new Asn1Tag(TagClass.Application, 0), out _)); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.TryReadBMPStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -678,33 +677,33 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER, "1E022010", PublicTagClass.Universal, 30)] - [InlineData(PublicEncodingRules.CER, "1E022010", PublicTagClass.Universal, 30)] - [InlineData(PublicEncodingRules.DER, "1E022010", PublicTagClass.Universal, 30)] - [InlineData(PublicEncodingRules.BER, "8002FE60", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C02FE60", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4602FE60", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "1E022010", TagClass.Universal, 30)] + [InlineData(AsnEncodingRules.CER, "1E022010", TagClass.Universal, 30)] + [InlineData(AsnEncodingRules.DER, "1E022010", TagClass.Universal, 30)] + [InlineData(AsnEncodingRules.BER, "8002FE60", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C02FE60", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4602FE60", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryReadBMPStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, true), + new Asn1Tag(tagClass, tagValue, true), out ReadOnlyMemory val1)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryReadBMPStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, false), + new Asn1Tag(tagClass, tagValue, false), out ReadOnlyMemory val2)); Assert.False(reader.HasData); @@ -720,7 +719,7 @@ public static bool TryReadBMPStringBytes( out ReadOnlyMemory contents) { return reader.TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber.BMPString, + new Asn1Tag(UniversalTagNumber.BMPString), out contents); } @@ -731,7 +730,6 @@ public static bool TryReadBMPStringBytes( { return reader.TryReadPrimitiveCharacterStringBytes( expectedTag, - UniversalTagNumber.BMPString, out contents); } @@ -740,9 +738,9 @@ public static bool TryCopyBMPStringBytes( Span destination, out int bytesWritten) { - return reader.TryCopyCharacterStringBytes( - UniversalTagNumber.BMPString, + return reader.TryReadCharacterStringBytes( destination, + new Asn1Tag(UniversalTagNumber.BMPString), out bytesWritten); } @@ -751,9 +749,9 @@ public static bool TryCopyBMPString( Span destination, out int charsWritten) { - return reader.TryCopyCharacterString( - UniversalTagNumber.BMPString, + return reader.TryReadCharacterString( destination, + UniversalTagNumber.BMPString, out charsWritten); } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs similarity index 50% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs index 6282bbd21f903e..43667481bf5e4b 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs @@ -4,31 +4,30 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadBitString : Asn1ReaderTests + public static class ReadBitString { [Theory] - [InlineData("Uncleared unused bit", PublicEncodingRules.BER, "030201FF")] - [InlineData("Constructed Payload", PublicEncodingRules.BER, "2302030100")] - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.BER, "238003010000")] + [InlineData("Uncleared unused bit", AsnEncodingRules.BER, "030201FF")] + [InlineData("Constructed Payload", AsnEncodingRules.BER, "2302030100")] + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.BER, "238003010000")] // This value is actually invalid CER, but it returns false since it's not primitive and // it isn't worth preempting the descent to find out it was invalid. - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.CER, "238003010000")] - public static void TryGetBitStringBytes_Fails( + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.CER, "238003010000")] + public static void TryReadPrimitiveBitStringValue_Fails( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveBitStringValue( + bool didRead = reader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory contents); @@ -38,21 +37,21 @@ public static void TryGetBitStringBytes_Fails( } [Theory] - [InlineData(PublicEncodingRules.BER, 0, 0, "030100")] - [InlineData(PublicEncodingRules.BER, 1, 1, "030201FE")] - [InlineData(PublicEncodingRules.CER, 2, 4, "030502FEEFF00C")] - [InlineData(PublicEncodingRules.DER, 7, 1, "03020780")] - [InlineData(PublicEncodingRules.DER, 0, 4, "030500FEEFF00D" + "0500")] - public static void TryGetBitStringBytes_Success( - PublicEncodingRules ruleSet, + [InlineData(AsnEncodingRules.BER, 0, 0, "030100")] + [InlineData(AsnEncodingRules.BER, 1, 1, "030201FE")] + [InlineData(AsnEncodingRules.CER, 2, 4, "030502FEEFF00C")] + [InlineData(AsnEncodingRules.DER, 7, 1, "03020780")] + [InlineData(AsnEncodingRules.DER, 0, 4, "030500FEEFF00D" + "0500")] + public static void TryReadPrimitiveBitStringValue_Success( + AsnEncodingRules ruleSet, int expectedUnusedBitCount, int expectedLength, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveBitStringValue( + bool didRead = reader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory contents); @@ -62,36 +61,48 @@ public static void TryGetBitStringBytes_Success( } [Theory] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] - [InlineData("Zero Length", PublicEncodingRules.BER, "0300")] - [InlineData("Zero Length", PublicEncodingRules.CER, "0300")] - [InlineData("Zero Length", PublicEncodingRules.DER, "0300")] - [InlineData("Bad Length", PublicEncodingRules.BER, "030200")] - [InlineData("Bad Length", PublicEncodingRules.CER, "030200")] - [InlineData("Bad Length", PublicEncodingRules.DER, "030200")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "2303030100")] - public static void TryGetBitStringBytes_Throws( + [InlineData("Wrong Tag", AsnEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.DER, "0500")] + [InlineData("Zero Length", AsnEncodingRules.BER, "0300")] + [InlineData("Zero Length", AsnEncodingRules.CER, "0300")] + [InlineData("Zero Length", AsnEncodingRules.DER, "0300")] + [InlineData("Bad Length", AsnEncodingRules.BER, "030200")] + [InlineData("Bad Length", AsnEncodingRules.CER, "030200")] + [InlineData("Bad Length", AsnEncodingRules.DER, "030200")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "2303030100")] + public static void TryReadPrimitiveBitStringValue_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => { - reader.TryReadPrimitiveBitStringValue( + reader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory contents); }); + + Assert.Throws( + () => + { + reader.TryReadBitString( + new byte[inputData.Length], + out int unusedBitCount, + out int written); + }); + + Assert.Throws( + () => reader.ReadBitString(out int unusedBitCount)); } [Fact] - public static void TryGetBitStringBytes_Throws_CER_TooLong() + public static void TryReadPrimitiveBitStringValue_Throws_CER_TooLong() { // CER says that the maximum encoding length for a BitString primitive is // 1000 (999 value bytes and 1 unused bit count byte). @@ -107,17 +118,29 @@ public static void TryGetBitStringBytes_Throws_CER_TooLong() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - Assert.Throws( + Assert.Throws( () => { - reader.TryReadPrimitiveBitStringValue( + reader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory contents); }); + + Assert.Throws( + () => + { + reader.TryReadBitString( + new byte[input.Length], + out int unusedBitCount, + out int written); + }); + + Assert.Throws( + () => reader.ReadBitString(out int unusedBitCount)); } [Fact] - public static void TryGetBitStringBytes_Success_CER_MaxLength() + public static void TryReadPrimitiveBitStringValue_Success_CER_MaxLength() { // CER says that the maximum encoding length for a BitString primitive is // 1000 (999 value bytes and 1 unused bit count byte). @@ -141,7 +164,7 @@ public static void TryGetBitStringBytes_Success_CER_MaxLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryReadPrimitiveBitStringValue( + bool success = reader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory contents); @@ -157,43 +180,43 @@ ref MemoryMarshal.GetReference(contents.Span), } [Theory] - [InlineData(PublicEncodingRules.BER, "03020780")] - [InlineData(PublicEncodingRules.BER, "030207FF")] - [InlineData(PublicEncodingRules.CER, "03020780")] - [InlineData(PublicEncodingRules.DER, "03020780")] + [InlineData(AsnEncodingRules.BER, "03020780")] + [InlineData(AsnEncodingRules.BER, "030207FF")] + [InlineData(AsnEncodingRules.CER, "03020780")] + [InlineData(AsnEncodingRules.DER, "03020780")] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2380" + "2380" + "0000" + "03020000" + "0000")] - public static void TryCopyBitStringBytes_Fails(PublicEncodingRules ruleSet, string inputHex) + public static void TryCopyBitStringBytes_Fails(AsnEncodingRules ruleSet, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryCopyBitStringBytes( + bool didRead = reader.TryReadBitString( Span.Empty, out int unusedBitCount, out int bytesWritten); - Assert.False(didRead, "reader.TryCopyBitStringBytes"); + Assert.False(didRead, "reader.TryReadBitString"); Assert.Equal(0, unusedBitCount); Assert.Equal(0, bytesWritten); } [Theory] - [InlineData(PublicEncodingRules.BER, "03020780", "80", 7)] - [InlineData(PublicEncodingRules.BER, "030207FF", "80", 7)] - [InlineData(PublicEncodingRules.CER, "03020780", "80", 7)] - [InlineData(PublicEncodingRules.DER, "03020680", "80", 6)] - [InlineData(PublicEncodingRules.BER, "23800000", "", 0)] - [InlineData(PublicEncodingRules.BER, "2300", "", 0)] - [InlineData(PublicEncodingRules.BER, "2300" + "0500", "", 0)] - [InlineData(PublicEncodingRules.BER, "0303010203" + "0500", "0202", 1)] + [InlineData(AsnEncodingRules.BER, "03020780", "80", 7)] + [InlineData(AsnEncodingRules.BER, "030207FF", "80", 7)] + [InlineData(AsnEncodingRules.CER, "03020780", "80", 7)] + [InlineData(AsnEncodingRules.DER, "03020680", "80", 6)] + [InlineData(AsnEncodingRules.BER, "23800000", "", 0)] + [InlineData(AsnEncodingRules.BER, "2300", "", 0)] + [InlineData(AsnEncodingRules.BER, "2300" + "0500", "", 0)] + [InlineData(AsnEncodingRules.BER, "0303010203" + "0500", "0202", 1)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2380" + "2380" + "0000" + @@ -202,7 +225,7 @@ public static void TryCopyBitStringBytes_Fails(PublicEncodingRules ruleSet, stri "00", 0)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "230C" + "2380" + "2380" + @@ -212,7 +235,7 @@ public static void TryCopyBitStringBytes_Fails(PublicEncodingRules ruleSet, stri "00", 0)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2380" + "2308" + "030200FA" + @@ -233,104 +256,113 @@ public static void TryCopyBitStringBytes_Fails(PublicEncodingRules ruleSet, stri "FACEF00D010203F8", 3)] public static void TryCopyBitStringBytes_Success( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedHex, int expectedUnusedBitCount) { byte[] inputData = inputHex.HexToByteArray(); byte[] output = new byte[expectedHex.Length / 2]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryCopyBitStringBytes( + bool didRead = reader.TryReadBitString( output, out int unusedBitCount, out int bytesWritten); - Assert.True(didRead, "reader.TryCopyBitStringBytes"); + Assert.True(didRead, "reader.TryReadBitString"); Assert.Equal(expectedUnusedBitCount, unusedBitCount); Assert.Equal(expectedHex, output.AsSpan(0, bytesWritten).ByteArrayToHex()); } - private static void TryCopyBitStringBytes_Throws_Helper( - PublicEncodingRules ruleSet, + private static void TryReadBitString_Throws_Helper( + AsnEncodingRules ruleSet, byte[] input) { - AsnReader reader = new AsnReader(input, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(input, ruleSet); - Assert.Throws( + Assert.Throws( () => { - reader.TryCopyBitStringBytes( + reader.TryReadBitString( Span.Empty, out int unusedBitCount, out int bytesWritten); }); } + private static void ReadBitString_Throws( + AsnEncodingRules ruleSet, + byte[] input) + { + AsnReader reader = new AsnReader(input, ruleSet); + Assert.Throws(() => reader.ReadBitString(out int unusedBitCount)); + } + [Theory] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] - [InlineData("Zero Length", PublicEncodingRules.BER, "0300")] - [InlineData("Zero Length", PublicEncodingRules.CER, "0300")] - [InlineData("Zero Length", PublicEncodingRules.DER, "0300")] - [InlineData("Bad Length", PublicEncodingRules.BER, "030200")] - [InlineData("Bad Length", PublicEncodingRules.CER, "030200")] - [InlineData("Bad Length", PublicEncodingRules.DER, "030200")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "2303030100")] - [InlineData("Bad unused bits", PublicEncodingRules.BER, "03020800")] - [InlineData("Bad unused bits", PublicEncodingRules.CER, "03020800")] - [InlineData("Bad unused bits", PublicEncodingRules.DER, "03020800")] - [InlineData("Bad unused bits-nodata", PublicEncodingRules.BER, "030101")] - [InlineData("Bad unused bits-nodata", PublicEncodingRules.CER, "030101")] - [InlineData("Bad unused bits-nodata", PublicEncodingRules.DER, "030101")] - [InlineData("Bad nested unused bits", PublicEncodingRules.BER, "230403020800")] - [InlineData("Bad nested unused bits-indef", PublicEncodingRules.BER, "2380030208000000")] - [InlineData("Bad nested unused bits-indef", PublicEncodingRules.CER, "2380030208000000")] - [InlineData("Bad nested unused bits-nodata", PublicEncodingRules.BER, "2303030101")] - [InlineData("Bad nested unused bits-nodata-indef", PublicEncodingRules.BER, "23800301010000")] - [InlineData("Bad nested unused bits-nodata-indef", PublicEncodingRules.CER, "23800301010000")] - [InlineData("Bad mask", PublicEncodingRules.CER, "030201FF")] - [InlineData("Bad mask", PublicEncodingRules.DER, "030201FF")] - [InlineData("Bad nested mask", PublicEncodingRules.CER, "2380030201FF0000")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "2304800300FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2380800300FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2380800300FACE0000")] - [InlineData("Nested boolean", PublicEncodingRules.BER, "2303010100")] - [InlineData("Nested boolean (indef)", PublicEncodingRules.BER, "23800101000000")] - [InlineData("Nested boolean (indef)", PublicEncodingRules.CER, "23800101000000")] - [InlineData("Nested constructed form", PublicEncodingRules.CER, "2380" + "2380" + "03010" + "000000000")] - [InlineData("No terminator", PublicEncodingRules.BER, "2380" + "03020000" + "")] - [InlineData("No terminator", PublicEncodingRules.CER, "2380" + "03020000" + "")] - [InlineData("No content", PublicEncodingRules.BER, "2380")] - [InlineData("No content", PublicEncodingRules.CER, "2380")] - [InlineData("No nested content", PublicEncodingRules.CER, "23800000")] - [InlineData("Nested value too long", PublicEncodingRules.BER, "2380030A00")] - [InlineData("Nested value too long - constructed", PublicEncodingRules.BER, "2380230A00")] - [InlineData("Nested value too long - simple", PublicEncodingRules.BER, "2303" + "03050000000000")] + [InlineData("Wrong Tag", AsnEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.DER, "0500")] + [InlineData("Zero Length", AsnEncodingRules.BER, "0300")] + [InlineData("Zero Length", AsnEncodingRules.CER, "0300")] + [InlineData("Zero Length", AsnEncodingRules.DER, "0300")] + [InlineData("Bad Length", AsnEncodingRules.BER, "030200")] + [InlineData("Bad Length", AsnEncodingRules.CER, "030200")] + [InlineData("Bad Length", AsnEncodingRules.DER, "030200")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "2303030100")] + [InlineData("Bad unused bits", AsnEncodingRules.BER, "03020800")] + [InlineData("Bad unused bits", AsnEncodingRules.CER, "03020800")] + [InlineData("Bad unused bits", AsnEncodingRules.DER, "03020800")] + [InlineData("Bad unused bits-nodata", AsnEncodingRules.BER, "030101")] + [InlineData("Bad unused bits-nodata", AsnEncodingRules.CER, "030101")] + [InlineData("Bad unused bits-nodata", AsnEncodingRules.DER, "030101")] + [InlineData("Bad nested unused bits", AsnEncodingRules.BER, "230403020800")] + [InlineData("Bad nested unused bits-indef", AsnEncodingRules.BER, "2380030208000000")] + [InlineData("Bad nested unused bits-indef", AsnEncodingRules.CER, "2380030208000000")] + [InlineData("Bad nested unused bits-nodata", AsnEncodingRules.BER, "2303030101")] + [InlineData("Bad nested unused bits-nodata-indef", AsnEncodingRules.BER, "23800301010000")] + [InlineData("Bad nested unused bits-nodata-indef", AsnEncodingRules.CER, "23800301010000")] + [InlineData("Bad mask", AsnEncodingRules.CER, "030201FF")] + [InlineData("Bad mask", AsnEncodingRules.DER, "030201FF")] + [InlineData("Bad nested mask", AsnEncodingRules.CER, "2380030201FF0000")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "2304800300FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "2380800300FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "2380800300FACE0000")] + [InlineData("Nested boolean", AsnEncodingRules.BER, "2303010100")] + [InlineData("Nested boolean (indef)", AsnEncodingRules.BER, "23800101000000")] + [InlineData("Nested boolean (indef)", AsnEncodingRules.CER, "23800101000000")] + [InlineData("Nested constructed form", AsnEncodingRules.CER, "2380" + "2380" + "03010" + "000000000")] + [InlineData("No terminator", AsnEncodingRules.BER, "2380" + "03020000" + "")] + [InlineData("No terminator", AsnEncodingRules.CER, "2380" + "03020000" + "")] + [InlineData("No content", AsnEncodingRules.BER, "2380")] + [InlineData("No content", AsnEncodingRules.CER, "2380")] + [InlineData("No nested content", AsnEncodingRules.CER, "23800000")] + [InlineData("Nested value too long", AsnEncodingRules.BER, "2380030A00")] + [InlineData("Nested value too long - constructed", AsnEncodingRules.BER, "2380230A00")] + [InlineData("Nested value too long - simple", AsnEncodingRules.BER, "2303" + "03050000000000")] [InlineData( "Unused bits in intermediate segment", - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2380" + "0303000102" + "0303020304" + "0303010506" + "0000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "238020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "238020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2380000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2380000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2380008100")] - [InlineData("Constructed Payload-TooShort", PublicEncodingRules.CER, "23800301000000")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "238020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "238020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "2380000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "2380000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "2380008100")] + [InlineData("Constructed Payload-TooShort", AsnEncodingRules.CER, "23800301000000")] public static void TryCopyBitStringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - TryCopyBitStringBytes_Throws_Helper(ruleSet, inputData); + TryReadBitString_Throws_Helper(ruleSet, inputData); + ReadBitString_Throws(ruleSet, inputData); } [Fact] @@ -348,7 +380,8 @@ public static void TryCopyBitStringBytes_Throws_CER_TooLong() input[2] = 0x03; input[3] = 0xE9; - TryCopyBitStringBytes_Throws_Helper(PublicEncodingRules.CER, input); + TryReadBitString_Throws_Helper(AsnEncodingRules.CER, input); + ReadBitString_Throws(AsnEncodingRules.CER, input); } [Fact] @@ -377,7 +410,8 @@ public static void TryCopyBitStringBytes_Throws_CER_NestedTooLong() input[5] = 0xE9; // EOC implicit since the byte[] initializes to zeros - TryCopyBitStringBytes_Throws_Helper(PublicEncodingRules.CER, input); + TryReadBitString_Throws_Helper(AsnEncodingRules.CER, input); + ReadBitString_Throws(AsnEncodingRules.CER, input); } [Fact] @@ -416,7 +450,7 @@ public static void TryCopyBitStringBytes_Throws_CER_NestedTooShortIntermediate() input[1011] = 0x02; // EOC implicit since the byte[] initializes to zeros - TryCopyBitStringBytes_Throws_Helper(PublicEncodingRules.CER, input); + TryReadBitString_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -446,18 +480,23 @@ public static void TryCopyBitStringBytes_Success_CER_MaxPrimitiveLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryCopyBitStringBytes( + bool success = reader.TryReadBitString( output, out int unusedBitCount, out int bytesWritten); - Assert.True(success, "reader.TryCopyBitStringBytes"); + Assert.True(success, "reader.TryReadBitString"); Assert.Equal(input[4], unusedBitCount); Assert.Equal(999, bytesWritten); Assert.Equal( input.AsSpan(5).ByteArrayToHex(), output.ByteArrayToHex()); + + reader = new AsnReader(input, AsnEncodingRules.CER); + byte[] output2 = reader.ReadBitString(out int ubc2); + Assert.Equal(unusedBitCount, ubc2); + Assert.Equal(output, output2); } [Fact] @@ -521,122 +560,191 @@ public static void TryCopyBitStringBytes_Success_CER_MinConstructedLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryCopyBitStringBytes( + bool success = reader.TryReadBitString( output, out int unusedBitCount, out int bytesWritten); - Assert.True(success, "reader.TryCopyBitStringBytes"); + Assert.True(success, "reader.TryReadBitString"); Assert.Equal(input[1006], unusedBitCount); Assert.Equal(1000, bytesWritten); Assert.Equal( expected.ByteArrayToHex(), output.ByteArrayToHex()); + + reader = new AsnReader(input, AsnEncodingRules.CER); + byte[] output2 = reader.ReadBitString(out int ubc2); + Assert.Equal(unusedBitCount, ubc2); + Assert.Equal(output, output2); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = { 3, 2, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.TryReadPrimitiveBitStringValue(Asn1Tag.Null, out _, out _)); + () => reader.TryReadPrimitiveBitString(out _, out _, Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.TryReadPrimitiveBitStringValue(new Asn1Tag(TagClass.ContextSpecific, 0), out _, out _)); + Assert.Throws( + () => reader.TryReadPrimitiveBitString(out _, out _, new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); - Assert.True(reader.TryReadPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlyMemory contents)); + Assert.True(reader.TryReadPrimitiveBitString(out int unusedBitCount, out ReadOnlyMemory contents)); Assert.Equal("7E", contents.ByteArrayToHex()); Assert.Equal(1, unusedBitCount); Assert.False(reader.HasData, "HasData after read"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = { 0x87, 2, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + byte[] output = new byte[inputData.Length]; + AsnReader reader = new AsnReader(inputData, ruleSet); + + Asn1Tag wrongTag1 = new Asn1Tag(TagClass.Application, 0); + Asn1Tag wrongTag2 = new Asn1Tag(TagClass.ContextSpecific, 1); + Asn1Tag correctTag = new Asn1Tag(TagClass.ContextSpecific, 7); AssertExtensions.Throws( "expectedTag", - () => reader.TryReadPrimitiveBitStringValue(Asn1Tag.Null, out _, out _)); + () => reader.TryReadPrimitiveBitString(out _, out _, Asn1Tag.Null)); + AssertExtensions.Throws( + "expectedTag", + () => reader.TryReadBitString(output, out _, out _, Asn1Tag.Null)); + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBitString(out _, Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.TryReadPrimitiveBitStringValue(out _, out _)); - + Assert.Throws(() => reader.TryReadPrimitiveBitString(out _, out _)); + Assert.Throws(() => reader.TryReadBitString(output, out _, out _)); + Assert.Throws(() => reader.ReadBitString(out _)); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( - () => reader.TryReadPrimitiveBitStringValue(new Asn1Tag(TagClass.Application, 0), out _, out _)); - + Assert.Throws(() => reader.TryReadPrimitiveBitString(out _, out _, wrongTag1)); + Assert.Throws(() => reader.TryReadBitString(output, out _, out _, wrongTag1)); + Assert.Throws(() => reader.ReadBitString(out _, wrongTag1)); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( - () => reader.TryReadPrimitiveBitStringValue(new Asn1Tag(TagClass.ContextSpecific, 1), out _, out _)); - + Assert.Throws(() => reader.TryReadPrimitiveBitString(out _, out _, wrongTag2)); + Assert.Throws(() => reader.TryReadBitString(output, out _, out _, wrongTag2)); + Assert.Throws(() => reader.ReadBitString(out _, wrongTag2)); Assert.True(reader.HasData, "HasData after wrong custom tag value"); Assert.True( - reader.TryReadPrimitiveBitStringValue( - new Asn1Tag(TagClass.ContextSpecific, 7), + reader.TryReadPrimitiveBitString( out int unusedBitCount, - out ReadOnlyMemory contents)); + out ReadOnlyMemory contents, + correctTag)); Assert.Equal("80", contents.ByteArrayToHex()); Assert.Equal(0, unusedBitCount); Assert.False(reader.HasData, "HasData after reading value"); + + reader = new AsnReader(inputData, ruleSet); + + Assert.True( + reader.TryReadBitString( + output.AsSpan(1), + out unusedBitCount, + out int written, + correctTag)); + + Assert.Equal("80", output.AsSpan(1, written).ByteArrayToHex()); + Assert.Equal(0, unusedBitCount); + Assert.False(reader.HasData, "HasData after reading value"); + + reader = new AsnReader(inputData, ruleSet); + byte[] output2 = reader.ReadBitString(out unusedBitCount, correctTag); + + Assert.Equal("80", output2.ByteArrayToHex()); + Assert.Equal(0, unusedBitCount); + Assert.False(reader.HasData, "HasData after reading value"); } [Theory] - [InlineData(PublicEncodingRules.BER, "030400010203", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.CER, "030400010203", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.DER, "030400010203", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.BER, "800200FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C0200FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A460200FF", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "030400010203", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.CER, "030400010203", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.DER, "030400010203", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.BER, "800200FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C0200FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A460200FF", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); + Asn1Tag correctConstructed = new Asn1Tag(tagClass, tagValue, true); + Asn1Tag correctPrimitive = new Asn1Tag(tagClass, tagValue, false); Assert.True( - reader.TryReadPrimitiveBitStringValue( - new Asn1Tag((TagClass)tagClass, tagValue, true), + reader.TryReadPrimitiveBitString( out int ubc1, - out ReadOnlyMemory val1)); + out ReadOnlyMemory val1, + correctConstructed)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.True( - reader.TryReadPrimitiveBitStringValue( - new Asn1Tag((TagClass)tagClass, tagValue, false), + reader.TryReadPrimitiveBitString( out int ubc2, - out ReadOnlyMemory val2)); + out ReadOnlyMemory val2, + correctPrimitive)); Assert.False(reader.HasData); - Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + string val1Hex = val1.ByteArrayToHex(); + Assert.Equal(val1Hex, val2.ByteArrayToHex()); Assert.Equal(ubc1, ubc2); + + reader = new AsnReader(inputData, ruleSet); + byte[] output1 = new byte[inputData.Length]; + + Assert.True(reader.TryReadBitString(output1.AsSpan(1), out ubc1, out int written, correctConstructed)); + Assert.Equal(ubc2, ubc1); + Assert.Equal(val1Hex, output1.AsSpan(1, written).ByteArrayToHex()); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + Assert.True(reader.TryReadBitString(output1.AsSpan(2), out ubc1, out written, correctPrimitive)); + Assert.Equal(ubc2, ubc1); + Assert.Equal(val1Hex, output1.AsSpan(2, written).ByteArrayToHex()); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + byte[] output2 = reader.ReadBitString(out ubc1, correctConstructed); + + Assert.Equal(ubc2, ubc1); + Assert.Equal(val1Hex, output2.ByteArrayToHex()); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + byte[] output3 = reader.ReadBitString(out ubc1, correctPrimitive); + + Assert.Equal(ubc2, ubc1); + Assert.Equal(val1Hex, output3.ByteArrayToHex()); + Assert.False(reader.HasData); } [Fact] @@ -666,10 +774,17 @@ public static void TryCopyBitStringBytes_ExtremelyNested() int unusedBitCount; Assert.True( - reader.TryCopyBitStringBytes(Span.Empty, out unusedBitCount, out bytesWritten)); + reader.TryReadBitString(Span.Empty, out unusedBitCount, out bytesWritten)); Assert.Equal(0, bytesWritten); Assert.Equal(0, unusedBitCount); + + reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + byte[] output = reader.ReadBitString(out unusedBitCount); + Assert.Equal(0, unusedBitCount); + + // It's Same (ReferenceEqual) on .NET Core, but just Equal on .NET Framework + Assert.Equal(Array.Empty(), output); } } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs new file mode 100644 index 00000000000000..75eab9e395c18b --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs @@ -0,0 +1,226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadBoolean + { + [Theory] + [InlineData(AsnEncodingRules.BER, false, 3, "010100")] + [InlineData(AsnEncodingRules.BER, true, 3, "010101")] + // Padded length + [InlineData(AsnEncodingRules.BER, true, 4, "01810101")] + [InlineData(AsnEncodingRules.BER, true, 3, "0101FF0500")] + [InlineData(AsnEncodingRules.CER, false, 3, "0101000500")] + [InlineData(AsnEncodingRules.CER, true, 3, "0101FF")] + [InlineData(AsnEncodingRules.DER, false, 3, "010100")] + [InlineData(AsnEncodingRules.DER, true, 3, "0101FF0500")] + // Context Specific 0 + [InlineData(AsnEncodingRules.DER, true, 3, "8001FF0500")] + // Application 31 + [InlineData(AsnEncodingRules.DER, true, 4, "5F1F01FF0500")] + // Private 253 + [InlineData(AsnEncodingRules.CER, false, 5, "DF817D01000500")] + public static void ReadBoolean_Success( + AsnEncodingRules ruleSet, + bool expectedValue, + int expectedBytesRead, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Asn1Tag tag = reader.PeekTag(); + bool value; + + if (tag.TagClass == TagClass.Universal) + { + value = reader.ReadBoolean(); + } + else + { + value = reader.ReadBoolean(tag); + } + + if (inputData.Length == expectedBytesRead) + { + Assert.False(reader.HasData, "reader.HasData"); + } + else + { + Assert.True(reader.HasData, "reader.HasData"); + } + + if (expectedValue) + { + Assert.True(value, "value"); + } + else + { + Assert.False(value, "value"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 1, 1, 0 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBoolean(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + bool value = reader.ReadBoolean(); + Assert.False(value, "value"); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x80, 1, 0xFF }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBoolean(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadBoolean()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + bool value = reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0)); + Assert.True(value, "value"); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0101FF", TagClass.Universal, 1)] + [InlineData(AsnEncodingRules.CER, "0101FF", TagClass.Universal, 1)] + [InlineData(AsnEncodingRules.DER, "0101FF", TagClass.Universal, 1)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + bool val1 = reader.ReadBoolean(new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, ruleSet); + bool val2 = reader.ReadBoolean(new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + + [Theory] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("TagOnly", AsnEncodingRules.BER, "01")] + [InlineData("TagOnly", AsnEncodingRules.CER, "01")] + [InlineData("TagOnly", AsnEncodingRules.DER, "01")] + [InlineData("MultiByte TagOnly", AsnEncodingRules.DER, "9F1F")] + [InlineData("MultiByte TagOnly", AsnEncodingRules.CER, "9F1F")] + [InlineData("MultiByte TagOnly", AsnEncodingRules.BER, "9F1F")] + [InlineData("TagAndLength", AsnEncodingRules.BER, "0101")] + [InlineData("Tag and MultiByteLength", AsnEncodingRules.BER, "01820001")] + [InlineData("TagAndLength", AsnEncodingRules.CER, "8001")] + [InlineData("TagAndLength", AsnEncodingRules.DER, "C001")] + [InlineData("MultiByteTagAndLength", AsnEncodingRules.DER, "9F2001")] + [InlineData("MultiByteTagAndLength", AsnEncodingRules.CER, "9F2001")] + [InlineData("MultiByteTagAndLength", AsnEncodingRules.BER, "9F2001")] + [InlineData("MultiByteTagAndMultiByteLength", AsnEncodingRules.BER, "9F28200001")] + [InlineData("TooShort", AsnEncodingRules.BER, "0100")] + [InlineData("TooShort", AsnEncodingRules.CER, "8000")] + [InlineData("TooShort", AsnEncodingRules.DER, "0100")] + [InlineData("TooLong", AsnEncodingRules.DER, "C0020000")] + [InlineData("TooLong", AsnEncodingRules.CER, "01020000")] + [InlineData("TooLong", AsnEncodingRules.BER, "C081020000")] + [InlineData("MissingContents", AsnEncodingRules.BER, "C001")] + [InlineData("MissingContents", AsnEncodingRules.CER, "0101")] + [InlineData("MissingContents", AsnEncodingRules.DER, "8001")] + [InlineData("NonCanonical", AsnEncodingRules.DER, "0101FE")] + [InlineData("NonCanonical", AsnEncodingRules.CER, "800101")] + [InlineData("Constructed", AsnEncodingRules.BER, "2103010101")] + [InlineData("Constructed", AsnEncodingRules.CER, "2103010101")] + [InlineData("Constructed", AsnEncodingRules.DER, "2103010101")] + [InlineData("WrongTag", AsnEncodingRules.DER, "0400")] + [InlineData("WrongTag", AsnEncodingRules.CER, "0400")] + [InlineData("WrongTag", AsnEncodingRules.BER, "0400")] + [InlineData("IndefiniteLength", AsnEncodingRules.BER, "01800101FF00")] + [InlineData("IndefiniteLength", AsnEncodingRules.CER, "01800101FF00")] + [InlineData("IndefiniteLength", AsnEncodingRules.DER, "01800101FF00")] + public static void ReadBoolean_Failure( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + Asn1Tag tag = default(Asn1Tag); + + if (inputData.Length > 0) + { + tag = reader.PeekTag(); + } + + if (tag.TagClass == TagClass.Universal) + { + Assert.Throws(() => reader.ReadBoolean()); + } + else + { + Assert.Throws(() => reader.ReadBoolean(tag)); + } + + if (inputData.Length == 0) + { + // If we started with nothing, where did the data come from? + Assert.False(reader.HasData, "reader.HasData"); + } + else + { + // Nothing should have moved + Assert.True(reader.HasData, "reader.HasData"); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs new file mode 100644 index 00000000000000..d19fbe618f05e9 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs @@ -0,0 +1,802 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadEnumerated + { + public enum ByteBacked : byte + { + Zero = 0, + NotFluffy = 11, + Fluff = 12, + } + + public enum SByteBacked : sbyte + { + Zero = 0, + Fluff = 83, + Pillow = -17, + } + + public enum ShortBacked : short + { + Zero = 0, + Fluff = 521, + Pillow = -1024, + } + + public enum UShortBacked : ushort + { + Zero = 0, + Fluff = 32768, + } + + public enum IntBacked : int + { + Zero = 0, + Fluff = 0x010001, + Pillow = -Fluff, + } + + public enum UIntBacked : uint + { + Zero = 0, + Fluff = 0x80000005, + } + + public enum LongBacked : long + { + Zero = 0, + Fluff = 0x0200000441, + Pillow = -0x100000000L, + } + + public enum ULongBacked : ulong + { + Zero = 0, + Fluff = 0xFACEF00DCAFEBEEF, + } + + private static void GetExpectedValue( + AsnEncodingRules ruleSet, + TEnum expectedValue, + string inputHex) + where TEnum : Enum + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + TEnum value = reader.ReadEnumeratedValue(); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, ByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, ByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, ByteBacked.Fluff, "0A010C")] + [InlineData(AsnEncodingRules.CER, ByteBacked.Fluff, "0A010C")] + [InlineData(AsnEncodingRules.DER, ByteBacked.Fluff, "0A010C")] + [InlineData(AsnEncodingRules.BER, (ByteBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (ByteBacked)128, "0A020080")] + [InlineData(AsnEncodingRules.DER, (ByteBacked)129, "0A020081")] + [InlineData(AsnEncodingRules.BER, (ByteBacked)254, "0A82000200FE")] + public static void GetExpectedValue_ByteBacked( + AsnEncodingRules ruleSet, + ByteBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, SByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, SByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, SByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, SByteBacked.Fluff, "0A0153")] + [InlineData(AsnEncodingRules.CER, SByteBacked.Fluff, "0A0153")] + [InlineData(AsnEncodingRules.DER, SByteBacked.Fluff, "0A0153")] + [InlineData(AsnEncodingRules.BER, SByteBacked.Pillow, "0A01EF")] + [InlineData(AsnEncodingRules.CER, (SByteBacked)sbyte.MinValue, "0A0180")] + [InlineData(AsnEncodingRules.DER, (SByteBacked)sbyte.MinValue + 1, "0A0181")] + [InlineData(AsnEncodingRules.BER, SByteBacked.Pillow, "0A820001EF")] + public static void GetExpectedValue_SByteBacked( + AsnEncodingRules ruleSet, + SByteBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, ShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, ShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, ShortBacked.Fluff, "0A020209")] + [InlineData(AsnEncodingRules.CER, ShortBacked.Fluff, "0A020209")] + [InlineData(AsnEncodingRules.DER, ShortBacked.Fluff, "0A020209")] + [InlineData(AsnEncodingRules.BER, ShortBacked.Pillow, "0A02FC00")] + [InlineData(AsnEncodingRules.CER, (ShortBacked)short.MinValue, "0A028000")] + [InlineData(AsnEncodingRules.DER, (ShortBacked)short.MinValue + 1, "0A028001")] + [InlineData(AsnEncodingRules.BER, ShortBacked.Pillow, "0A820002FC00")] + public static void GetExpectedValue_ShortBacked( + AsnEncodingRules ruleSet, + ShortBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, UShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, UShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, UShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, UShortBacked.Fluff, "0A03008000")] + [InlineData(AsnEncodingRules.CER, UShortBacked.Fluff, "0A03008000")] + [InlineData(AsnEncodingRules.DER, UShortBacked.Fluff, "0A03008000")] + [InlineData(AsnEncodingRules.BER, (UShortBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (UShortBacked)256, "0A020100")] + [InlineData(AsnEncodingRules.DER, (UShortBacked)0x7FED, "0A027FED")] + [InlineData(AsnEncodingRules.BER, (UShortBacked)ushort.MaxValue, "0A82000300FFFF")] + [InlineData(AsnEncodingRules.BER, (UShortBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_UShortBacked( + AsnEncodingRules ruleSet, + UShortBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, IntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, IntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, IntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, IntBacked.Fluff, "0A03010001")] + [InlineData(AsnEncodingRules.CER, IntBacked.Fluff, "0A03010001")] + [InlineData(AsnEncodingRules.DER, IntBacked.Fluff, "0A03010001")] + [InlineData(AsnEncodingRules.BER, IntBacked.Pillow, "0A03FEFFFF")] + [InlineData(AsnEncodingRules.CER, (IntBacked)int.MinValue, "0A0480000000")] + [InlineData(AsnEncodingRules.DER, (IntBacked)int.MinValue + 1, "0A0480000001")] + [InlineData(AsnEncodingRules.BER, IntBacked.Pillow, "0A820003FEFFFF")] + public static void GetExpectedValue_IntBacked( + AsnEncodingRules ruleSet, + IntBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, UIntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, UIntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, UIntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(AsnEncodingRules.CER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(AsnEncodingRules.DER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(AsnEncodingRules.BER, (UIntBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (UIntBacked)256, "0A020100")] + [InlineData(AsnEncodingRules.DER, (UIntBacked)0x7FED, "0A027FED")] + [InlineData(AsnEncodingRules.BER, (UIntBacked)uint.MaxValue, "0A82000500FFFFFFFF")] + [InlineData(AsnEncodingRules.BER, (UIntBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_UIntBacked( + AsnEncodingRules ruleSet, + UIntBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, LongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, LongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, LongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, LongBacked.Fluff, "0A050200000441")] + [InlineData(AsnEncodingRules.CER, LongBacked.Fluff, "0A050200000441")] + [InlineData(AsnEncodingRules.DER, LongBacked.Fluff, "0A050200000441")] + [InlineData(AsnEncodingRules.BER, LongBacked.Pillow, "0A05FF00000000")] + [InlineData(AsnEncodingRules.CER, (LongBacked)short.MinValue, "0A028000")] + [InlineData(AsnEncodingRules.DER, (LongBacked)short.MinValue + 1, "0A028001")] + [InlineData(AsnEncodingRules.BER, LongBacked.Pillow, "0A820005FF00000000")] + public static void GetExpectedValue_LongBacked( + AsnEncodingRules ruleSet, + LongBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ULongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, ULongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, ULongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.CER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.DER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (ULongBacked)256, "0A020100")] + [InlineData(AsnEncodingRules.DER, (ULongBacked)0x7FED, "0A027FED")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)uint.MaxValue, "0A82000500FFFFFFFF")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)ulong.MaxValue, "0A82000900FFFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_ULongBacked( + AsnEncodingRules ruleSet, + ULongBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A020102")] + [InlineData(AsnEncodingRules.CER, "0A020102")] + [InlineData(AsnEncodingRules.DER, "0A020102")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Byte(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A020102")] + [InlineData(AsnEncodingRules.CER, "0A020102")] + [InlineData(AsnEncodingRules.DER, "0A020102")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_SByte(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Short(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_UShort(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Int(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_UInt(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Long(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_ULong(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedValue_RequiresTypeArg(AsnEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + AssertExtensions.Throws( + "enumType", + () => reader.ReadEnumeratedValue(null!)); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedValue_NonEnumType(AsnEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue(typeof(Guid))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedValue_FlagsEnum(AsnEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + AssertExtensions.Throws( + "enumType", + () => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedBytes(AsnEncodingRules ruleSet) + { + const string Payload = "0102030405060708090A0B0C0D0E0F10"; + + // ENUMERATED (payload) followed by INTEGER (0) + byte[] data = ("0A10" + Payload + "020100").HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + ReadOnlyMemory contents = reader.ReadEnumeratedBytes(); + Assert.Equal(0x10, contents.Length); + Assert.Equal(Payload, contents.ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "010100")] + [InlineData(AsnEncodingRules.CER, "010100")] + [InlineData(AsnEncodingRules.DER, "010100")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + public static void ReadEnumeratedBytes_Throws(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedBytes()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x0A, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadEnumeratedValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + ShortBacked value = reader.ReadEnumeratedValue(); + Assert.Equal((ShortBacked)0x7E, value); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadEnumeratedValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + ShortBacked value = reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.Equal((ShortBacked)0x80, value); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0A01FF", TagClass.Universal, 10)] + [InlineData(AsnEncodingRules.CER, "0A01FF", TagClass.Universal, 10)] + [InlineData(AsnEncodingRules.DER, "0A01FF", TagClass.Universal, 10)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + Asn1Tag primitiveTag = new Asn1Tag(tagClass, tagValue, false); + Asn1Tag constructedTag = new Asn1Tag(tagClass, tagValue, true); + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + ShortBacked val1 = reader.ReadEnumeratedValue(constructedTag); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + ShortBacked val2 = reader.ReadEnumeratedValue(primitiveTag); + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + + reader = new AsnReader(inputData, ruleSet); + ShortBacked val3 = (ShortBacked)reader.ReadEnumeratedValue(typeof(ShortBacked), constructedTag); + Assert.False(reader.HasData); + + Assert.Equal(val1, val3); + + reader = new AsnReader(inputData, ruleSet); + ShortBacked val4 = (ShortBacked)reader.ReadEnumeratedValue(typeof(ShortBacked), primitiveTag); + Assert.False(reader.HasData); + + Assert.Equal(val1, val4); + + reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory bytes1 = reader.ReadEnumeratedBytes(constructedTag); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory bytes2 = reader.ReadEnumeratedBytes(primitiveTag); + Assert.False(reader.HasData); + + Assert.Equal(bytes1.ByteArrayToHex(), bytes2.ByteArrayToHex()); + Assert.Equal("FF", bytes1.ByteArrayToHex()); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs similarity index 65% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs index 294e55ea2f9750..86abaf6f1be9a1 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs @@ -3,73 +3,72 @@ // See the LICENSE file in the project root for more information. using System.Linq; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadGeneralizedTime : Asn1ReaderTests + public sealed class ReadGeneralizedTime { [Theory] // yyyyMMddHH (2017090821) - [InlineData(PublicEncodingRules.BER, "180A32303137303930383231", 2017, 9, 8, 21, 0, 0, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180A32303137303930383231", 2017, 9, 8, 21, 0, 0, 0, null, 0)] // yyyyMMddHHZ (2017090821Z) - [InlineData(PublicEncodingRules.BER, "180B323031373039303832315A", 2017, 9, 8, 21, 0, 0, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "180B323031373039303832315A", 2017, 9, 8, 21, 0, 0, 0, 0, 0)] // yyyyMMddHH-HH (2017090821-01) - [InlineData(PublicEncodingRules.BER, "180D323031373039303832312D3031", 2017, 9, 8, 21, 0, 0, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "180D323031373039303832312D3031", 2017, 9, 8, 21, 0, 0, 0, -1, 0)] // yyyyMMddHH+HHmm (2017090821+0118) - [InlineData(PublicEncodingRules.BER, "180F323031373039303832312B30313138", 2017, 9, 8, 21, 0, 0, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "180F323031373039303832312B30313138", 2017, 9, 8, 21, 0, 0, 0, 1, 18)] // yyyyMMddHH,hourFrac (2017090821.1) - [InlineData(PublicEncodingRules.BER, "180C323031373039303832312C31", 2017, 9, 8, 21, 6, 0, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180C323031373039303832312C31", 2017, 9, 8, 21, 6, 0, 0, null, 0)] // yyyyMMddHH.hourFracZ (2017090821.2010Z) - [InlineData(PublicEncodingRules.BER, "1810323031373039303832312E323031305A", 2017, 9, 8, 21, 12, 3, 600, 0, 0)] + [InlineData(AsnEncodingRules.BER, "1810323031373039303832312E323031305A", 2017, 9, 8, 21, 12, 3, 600, 0, 0)] // yyyyMMddHH,hourFrac-HH (2017090821,3099-01) - [InlineData(PublicEncodingRules.BER, "1812323031373039303832312C333039392D3031", 2017, 9, 8, 21, 18, 35, 640, -1, 0)] + [InlineData(AsnEncodingRules.BER, "1812323031373039303832312C333039392D3031", 2017, 9, 8, 21, 18, 35, 640, -1, 0)] // yyyyMMddHH.hourFrac+HHmm (2017090821.201+0118) - [InlineData(PublicEncodingRules.BER, "1813323031373039303832312E3230312B30313138", 2017, 9, 8, 21, 12, 3, 600, 1, 18)] + [InlineData(AsnEncodingRules.BER, "1813323031373039303832312E3230312B30313138", 2017, 9, 8, 21, 12, 3, 600, 1, 18)] // yyyyMMddHHmm (201709082358) - [InlineData(PublicEncodingRules.BER, "180C323031373039303832333538", 2017, 9, 8, 23, 58, 0, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180C323031373039303832333538", 2017, 9, 8, 23, 58, 0, 0, null, 0)] // yyyyMMddHHmmZ (201709082358Z) - [InlineData(PublicEncodingRules.BER, "180D3230313730393038323335385A", 2017, 9, 8, 23, 58, 0, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "180D3230313730393038323335385A", 2017, 9, 8, 23, 58, 0, 0, 0, 0)] // yyyyMMddHHmm-HH (201709082358-01) - [InlineData(PublicEncodingRules.BER, "180F3230313730393038323335382D3031", 2017, 9, 8, 23, 58, 0, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "180F3230313730393038323335382D3031", 2017, 9, 8, 23, 58, 0, 0, -1, 0)] // yyyyMMddHHmm+HHmm (201709082358+0118) - [InlineData(PublicEncodingRules.BER, "18113230313730393038323335382B30313138", 2017, 9, 8, 23, 58, 0, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "18113230313730393038323335382B30313138", 2017, 9, 8, 23, 58, 0, 0, 1, 18)] // yyyyMMddHHmm.minuteFrac (201709082358.01) - [InlineData(PublicEncodingRules.BER, "180F3230313730393038323335382E3031", 2017, 9, 8, 23, 58, 0, 600, null, 0)] + [InlineData(AsnEncodingRules.BER, "180F3230313730393038323335382E3031", 2017, 9, 8, 23, 58, 0, 600, null, 0)] // yyyyMMddHHmm,minuteFracZ (201709082358,11Z) - [InlineData(PublicEncodingRules.BER, "18103230313730393038323335382C31315A", 2017, 9, 8, 23, 58, 6, 600, 0, 0)] + [InlineData(AsnEncodingRules.BER, "18103230313730393038323335382C31315A", 2017, 9, 8, 23, 58, 6, 600, 0, 0)] // yyyyMMddHHmm.minuteFrac-HH (201709082358.05-01) - [InlineData(PublicEncodingRules.BER, "18123230313730393038323335382E30352D3031", 2017, 9, 8, 23, 58, 3, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "18123230313730393038323335382E30352D3031", 2017, 9, 8, 23, 58, 3, 0, -1, 0)] // yyyyMMddHHmm,minuteFrac+HHmm (201709082358,007+0118) - [InlineData(PublicEncodingRules.BER, "18153230313730393038323335382C3030372B30313138", 2017, 9, 8, 23, 58, 0, 420, 1, 18)] + [InlineData(AsnEncodingRules.BER, "18153230313730393038323335382C3030372B30313138", 2017, 9, 8, 23, 58, 0, 420, 1, 18)] // yyyyMMddHHmmss (20161106012345) - Ambiguous time due to DST "fall back" in US & Canada - [InlineData(PublicEncodingRules.BER, "180E3230313631313036303132333435", 2016, 11, 6, 1, 23, 45, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180E3230313631313036303132333435", 2016, 11, 6, 1, 23, 45, 0, null, 0)] // yyyyMMddHHmmssZ (20161106012345Z) - [InlineData(PublicEncodingRules.BER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] // yyyyMMddHHmmss-HH (20161106012345-01) - [InlineData(PublicEncodingRules.BER, "181132303136313130363031323334352D3031", 2016, 11, 6, 1, 23, 45, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "181132303136313130363031323334352D3031", 2016, 11, 6, 1, 23, 45, 0, -1, 0)] // yyyyMMddHHmmss+HHmm (20161106012345+0118) - [InlineData(PublicEncodingRules.BER, "181332303136313130363031323334352B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "181332303136313130363031323334352B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] // yyyyMMddHHmmss.secondFrac (20161106012345.6789) - Ambiguous time due to DST "fall back" in US & Canada - [InlineData(PublicEncodingRules.BER, "181332303136313130363031323334352E36373839", 2016, 11, 6, 1, 23, 45, 678, null, 0)] + [InlineData(AsnEncodingRules.BER, "181332303136313130363031323334352E36373839", 2016, 11, 6, 1, 23, 45, 678, null, 0)] // yyyyMMddHHmmss,secondFracZ (20161106012345,7654Z) - [InlineData(PublicEncodingRules.BER, "181432303136313130363031323334352C373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(AsnEncodingRules.BER, "181432303136313130363031323334352C373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] // yyyyMMddHHmmss.secondFrac-HH (20161106012345.001-01) - [InlineData(PublicEncodingRules.BER, "181532303136313130363031323334352E3030312D3031", 2016, 11, 6, 1, 23, 45, 1, -1, 0)] + [InlineData(AsnEncodingRules.BER, "181532303136313130363031323334352E3030312D3031", 2016, 11, 6, 1, 23, 45, 1, -1, 0)] // yyyyMMddHHmmss,secondFrac+HHmm (20161106012345,0009+0118) - [InlineData(PublicEncodingRules.BER, "181832303136313130363031323334352C303030392B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "181832303136313130363031323334352C303030392B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] // yyyyMMddHHmmssZ (20161106012345Z) - [InlineData(PublicEncodingRules.CER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] - [InlineData(PublicEncodingRules.DER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(AsnEncodingRules.CER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(AsnEncodingRules.DER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] // yyyyMMddHHmmss.secondFracZ (20161106012345,7654Z) - [InlineData(PublicEncodingRules.CER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] - [InlineData(PublicEncodingRules.DER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(AsnEncodingRules.CER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(AsnEncodingRules.DER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] public static void ParseTime_Valid( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, int year, int month, @@ -83,7 +82,7 @@ public static void ParseTime_Valid( { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); DateTimeOffset value = reader.ReadGeneralizedTime(); Assert.False(reader.HasData, "reader.HasData"); @@ -197,8 +196,8 @@ public static void ParseTime_BerOnly(string inputHex) AsnReader cerReader = new AsnReader(inputData, AsnEncodingRules.CER); AsnReader derReader = new AsnReader(inputData, AsnEncodingRules.DER); - Assert.Throws(() => cerReader.ReadGeneralizedTime()); - Assert.Throws(() => derReader.ReadGeneralizedTime()); + Assert.Throws(() => cerReader.ReadGeneralizedTime()); + Assert.Throws(() => derReader.ReadGeneralizedTime()); // Prove it was not just corrupt input AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); @@ -209,12 +208,12 @@ public static void ParseTime_BerOnly(string inputHex) } [Theory] - [InlineData(PublicEncodingRules.BER, "2017121900.06861111087Z")] - [InlineData(PublicEncodingRules.BER, "201712190004.11666665167Z")] - [InlineData(PublicEncodingRules.BER, "20171219000406.9999991Z")] - [InlineData(PublicEncodingRules.CER, "20171219000406.9999991Z")] - [InlineData(PublicEncodingRules.DER, "20171219000406.9999991Z")] - public static void MaximumEffectivePrecision(PublicEncodingRules ruleSet, string dateAscii) + [InlineData(AsnEncodingRules.BER, "2017121900.06861111087Z")] + [InlineData(AsnEncodingRules.BER, "201712190004.11666665167Z")] + [InlineData(AsnEncodingRules.BER, "20171219000406.9999991Z")] + [InlineData(AsnEncodingRules.CER, "20171219000406.9999991Z")] + [InlineData(AsnEncodingRules.DER, "20171219000406.9999991Z")] + public static void MaximumEffectivePrecision(AsnEncodingRules ruleSet, string dateAscii) { DateTimeOffset expectedTime = new DateTimeOffset(2017, 12, 19, 0, 4, 6, TimeSpan.Zero); expectedTime += new TimeSpan(TimeSpan.TicksPerSecond - 9); @@ -224,7 +223,7 @@ public static void MaximumEffectivePrecision(PublicEncodingRules ruleSet, string inputData[1] = (byte)dateAscii.Length; Text.Encoding.ASCII.GetBytes(dateAscii, 0, dateAscii.Length, inputData, 2); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.Equal(expectedTime, reader.ReadGeneralizedTime()); } @@ -258,9 +257,9 @@ public static void ExcessivelyPreciseFraction_OneTenthPlusEpsilon() } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void MultiSegmentExcessivelyPreciseFraction(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void MultiSegmentExcessivelyPreciseFraction(AsnEncodingRules ruleSet) { // This builds "20171207173522.0000...0001Z" where the Z required a second CER segment. // This is a bit of nonsense, really, because it is encoding 1e-985 seconds, which is @@ -280,7 +279,7 @@ public static void MultiSegmentExcessivelyPreciseFraction(PublicEncodingRules ru byte[] cdr = { 0x04, 0x01, (byte)'Z', 0x00, 0x00 }; byte[] inputData = header.Concat(contents0).Concat(cdr).ToArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); DateTimeOffset value = reader.ReadGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 0)); DateTimeOffset expected = new DateTimeOffset(2017, 12, 7, 17, 35, 22, TimeSpan.Zero); Assert.Equal(expected, value); @@ -300,8 +299,8 @@ public static void ExcessivelyPreciseFraction_OneTenthPlusEpsilonAndZero() AsnReader cerReader = new AsnReader(inputData, AsnEncodingRules.CER); AsnReader derReader = new AsnReader(inputData, AsnEncodingRules.DER); - Assert.Throws(() => cerReader.ReadGeneralizedTime()); - Assert.Throws(() => derReader.ReadGeneralizedTime()); + Assert.Throws(() => cerReader.ReadGeneralizedTime()); + Assert.Throws(() => derReader.ReadGeneralizedTime()); } [Fact] @@ -310,69 +309,7 @@ public static void ExcessivelyPreciseNonFraction() byte[] inputData = Text.Encoding.ASCII.GetBytes("\u0018\u002A2017092118.012345678901234567890123Q56789Z"); AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); - Assert.Throws(() => berReader.ReadGeneralizedTime()); - } - - [Theory] - // yyyyMMddHH,hourFrac (2017090821,1) - [InlineData("180C323031373039303832312C31")] - // yyyyMMddHH.hourFrac (2017090821.1) - [InlineData("180C323031373039303832312E31")] - // yyyyMMddHH,hourFracZ (2017090821,2010Z) - [InlineData("1810323031373039303832312C323031305A")] - // yyyyMMddHH.hourFracZ (2017090821.2010Z) - [InlineData("1810323031373039303832312E323031305A")] - // yyyyMMddHH,hourFrac-HH (2017090821,3099-01) - [InlineData("1812323031373039303832312C333039392D3031")] - // yyyyMMddHH.hourFrac-HH (2017090821.3099-01) - [InlineData("1812323031373039303832312E333039392D3031")] - // yyyyMMddHH,hourFrac+HHmm (2017090821,201+0118) - [InlineData("1813323031373039303832312C3230312B30313138")] - // yyyyMMddHH.hourFrac+HHmm (2017090821.201+0118) - [InlineData("1813323031373039303832312E3230312B30313138")] - // yyyyMMddHHmm,minuteFrac (201709082358,01) - [InlineData("180F3230313730393038323335382C3031")] - // yyyyMMddHHmm.minuteFrac (201709082358.01) - [InlineData("180F3230313730393038323335382E3031")] - // yyyyMMddHHmm,minuteFracZ (201709082358,11Z) - [InlineData("18103230313730393038323335382C31315A")] - // yyyyMMddHHmm.minuteFracZ (201709082358.11Z) - [InlineData("18103230313730393038323335382E31315A")] - // yyyyMMddHHmm,minuteFrac-HH (201709082358m05-01) - [InlineData("18123230313730393038323335382C30352D3031")] - // yyyyMMddHHmm.minuteFrac-HH (201709082358.05-01) - [InlineData("18123230313730393038323335382E30352D3031")] - // yyyyMMddHHmm,minuteFrac+HHmm (201709082358,007+0118) - [InlineData("18153230313730393038323335382C3030372B30313138")] - // yyyyMMddHHmm.minuteFrac+HHmm (201709082358.007+0118) - [InlineData("18153230313730393038323335382E3030372B30313138")] - // yyyyMMddHHmmss,secondFrac (20161106012345,6789) - [InlineData("181332303136313130363031323334352C36373839")] - // yyyyMMddHHmmss.secondFrac (20161106012345.6789) - [InlineData("181332303136313130363031323334352E36373839")] - // yyyyMMddHHmmss,secondFracZ (20161106012345,7654Z) - [InlineData("181432303136313130363031323334352C373635345A")] - // yyyyMMddHHmmss,secondFrac-HH (20161106012345,001-01) - [InlineData("181532303136313130363031323334352C3030312D3031")] - // yyyyMMddHHmmss.secondFrac-HH (20161106012345.001-01) - [InlineData("181532303136313130363031323334352E3030312D3031")] - // yyyyMMddHHmmss,secondFrac+HHmm (20161106012345,0009+0118) - [InlineData("181832303136313130363031323334352C303030392B30313138")] - // yyyyMMddHHmmss.secondFrac+HHmm (20161106012345.0009+0118) - [InlineData("181832303136313130363031323334352E303030392B30313138")] - // yyyyMMddHHmmss.secondFrac0Z (20161106012345.76540Z) - [InlineData("181532303136313130363031323334352E37363534305A")] - public static void VerifyDisallowFraction_BER(string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader berReader = new AsnReader(inputData, AsnEncodingRules.BER); - - Assert.Throws(() => berReader.ReadGeneralizedTime(disallowFractions: true)); - - // Legit if the fraction is allowed - berReader.ReadGeneralizedTime(); - Assert.False(berReader.HasData, "berReader.HasData"); + Assert.Throws(() => berReader.ReadGeneralizedTime()); } [Theory] @@ -446,17 +383,17 @@ public static void GetGeneralizedTime_Throws(string description, string inputHex byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - Assert.Throws(() => reader.ReadGeneralizedTime()); + Assert.Throws(() => reader.ReadGeneralizedTime()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = "180F32303136313130363031323334355A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -464,7 +401,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -477,13 +414,13 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = "850F32303136313130363031323334355A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -491,16 +428,16 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadUtcTime()); + Assert.Throws(() => reader.ReadUtcTime()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadGeneralizedTime(new Asn1Tag(TagClass.Application, 5))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.ReadGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 7))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -513,28 +450,28 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] - [InlineData(PublicEncodingRules.CER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] - [InlineData(PublicEncodingRules.DER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] - [InlineData(PublicEncodingRules.BER, "800F31393530303130323132333435365A", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C0F31393530303130323132333435365A", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A460F31393530303130323132333435365A", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "180F32303136313130363031323334355A", TagClass.Universal, 24)] + [InlineData(AsnEncodingRules.CER, "180F32303136313130363031323334355A", TagClass.Universal, 24)] + [InlineData(AsnEncodingRules.DER, "180F32303136313130363031323334355A", TagClass.Universal, 24)] + [InlineData(AsnEncodingRules.BER, "800F31393530303130323132333435365A", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C0F31393530303130323132333435365A", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A460F31393530303130323132333435365A", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - DateTimeOffset val1 = reader.ReadGeneralizedTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); + DateTimeOffset val1 = reader.ReadGeneralizedTime(new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); - DateTimeOffset val2 = reader.ReadGeneralizedTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); + DateTimeOffset val2 = reader.ReadGeneralizedTime(new Asn1Tag(tagClass, tagValue, false)); Assert.False(reader.HasData); diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs similarity index 57% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs index 9fe1323f8b5d11..ae7c6ededa53d7 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs @@ -5,80 +5,80 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; +using System.Text; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadIA5String : Asn1ReaderTests + public sealed class ReadIA5String { public static IEnumerable ValidEncodingData { get; } = new object[][] { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "160D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "160D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "160D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3680" + "040D4A6F686E20512E20536D697468" + "0000", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "360F" + "040D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1600", "", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1600", "", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1600", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3600", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3680" + "0000", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3680" + "2480" + // "Dr." @@ -119,12 +119,12 @@ public sealed class ReadIA5String : Asn1ReaderTests [Theory] [MemberData(nameof(ValidEncodingData))] public static void GetIA5String_Success( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); string value = reader.ReadCharacterString(UniversalTagNumber.IA5String); Assert.Equal(expectedValue, value); @@ -133,14 +133,14 @@ public static void GetIA5String_Success( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyIA5String( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); char[] output = new char[expectedValue.Length]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int charsWritten; @@ -168,7 +168,7 @@ public static void TryCopyIA5String( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyIA5StringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedString) { @@ -176,7 +176,7 @@ public static void TryCopyIA5StringBytes( string expectedHex = Text.Encoding.ASCII.GetBytes(expectedString).ByteArrayToHex(); byte[] output = new byte[expectedHex.Length / 2]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int bytesWritten; @@ -205,16 +205,16 @@ public static void TryCopyIA5StringBytes( } [Theory] - [InlineData(PublicEncodingRules.BER, "160120", true)] - [InlineData(PublicEncodingRules.BER, "3680" + "040120" + "0000", false)] - [InlineData(PublicEncodingRules.BER, "3603" + "040120", false)] + [InlineData(AsnEncodingRules.BER, "160120", true)] + [InlineData(AsnEncodingRules.BER, "3680" + "040120" + "0000", false)] + [InlineData(AsnEncodingRules.BER, "3603" + "040120", false)] public static void TryGetIA5StringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, bool expectSuccess) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool got = reader.TryGetIA5StringBytes(out ReadOnlyMemory contents); @@ -235,78 +235,78 @@ ref MemoryMarshal.GetReference(contents.Span), } [Theory] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "16")] - [InlineData("Missing Length", PublicEncodingRules.CER, "16")] - [InlineData("Missing Length", PublicEncodingRules.DER, "16")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1601")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1601")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1601")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "16034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "16034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "16034869")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "3603040149")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "16")] + [InlineData("Missing Length", AsnEncodingRules.CER, "16")] + [InlineData("Missing Length", AsnEncodingRules.DER, "16")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1601")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1601")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1601")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "16034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "16034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "16034869")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "3603040149")] public static void TryGetIA5StringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => reader.TryGetIA5StringBytes(out ReadOnlyMemory contents)); } [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "16")] - [InlineData("Missing Length", PublicEncodingRules.CER, "16")] - [InlineData("Missing Length", PublicEncodingRules.DER, "16")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1601")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1601")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1601")] - [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3601")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3680")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3680")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "16034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "16034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "16034869")] - [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3603040149")] - [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3603040149")] - [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "36800401490000")] - [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "36800401490000")] - [InlineData("No nested content", PublicEncodingRules.CER, "36800000")] - [InlineData("No EoC", PublicEncodingRules.BER, "3680" + "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] - [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3604" + "16024869")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "3604800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3680800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3680800400FACE0000")] - [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3607" + ("2402" + "0403") + "040149")] - [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3603" + "040548656C6C6F")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "368020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "368020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3680000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3680000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3680008100")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "16")] + [InlineData("Missing Length", AsnEncodingRules.CER, "16")] + [InlineData("Missing Length", AsnEncodingRules.DER, "16")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1601")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1601")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1601")] + [InlineData("Missing Contents - Constructed", AsnEncodingRules.BER, "3601")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.BER, "3680")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.CER, "3680")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "16034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "16034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "16034869")] + [InlineData("Definite Constructed Form", AsnEncodingRules.CER, "3603040149")] + [InlineData("Definite Constructed Form", AsnEncodingRules.DER, "3603040149")] + [InlineData("Indefinite Constructed Form - Short Payload", AsnEncodingRules.CER, "36800401490000")] + [InlineData("Indefinite Constructed Form", AsnEncodingRules.DER, "36800401490000")] + [InlineData("No nested content", AsnEncodingRules.CER, "36800000")] + [InlineData("No EoC", AsnEncodingRules.BER, "3680" + "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", AsnEncodingRules.BER, "3604" + "16024869")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "3604800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "3680800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "3680800400FACE0000")] + [InlineData("Nested Length Too Long", AsnEncodingRules.BER, "3607" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", AsnEncodingRules.BER, "3603" + "040548656C6C6F")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "368020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "368020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "3680000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "3680000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "3680008100")] public static void TryCopyIA5StringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; @@ -315,24 +315,24 @@ public static void TryCopyIA5StringBytes_Throws( outputData[0] = 252; int bytesWritten = -1; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => reader.TryCopyIA5StringBytes(outputData, out bytesWritten)); Assert.Equal(-1, bytesWritten); Assert.Equal(252, outputData[0]); } - private static void TryCopyIA5String_Throws_Helper(PublicEncodingRules ruleSet, byte[] inputData) + private static void TryCopyIA5String_Throws_Helper(AsnEncodingRules ruleSet, byte[] inputData) { char[] outputData = new char[inputData.Length + 1]; outputData[0] = 'a'; int bytesWritten = -1; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => reader.TryCopyIA5String(outputData, out bytesWritten)); Assert.Equal(-1, bytesWritten); @@ -340,70 +340,70 @@ private static void TryCopyIA5String_Throws_Helper(PublicEncodingRules ruleSet, } [Theory] - [InlineData("Bad IA5 value", PublicEncodingRules.BER, "1602E280")] - [InlineData("Bad IA5 value", PublicEncodingRules.CER, "1602E280")] - [InlineData("Bad IA5 value", PublicEncodingRules.DER, "1602E280")] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "04024869")] + [InlineData("Bad IA5 value", AsnEncodingRules.BER, "1602E280")] + [InlineData("Bad IA5 value", AsnEncodingRules.CER, "1602E280")] + [InlineData("Bad IA5 value", AsnEncodingRules.DER, "1602E280")] + [InlineData("Wrong Tag", AsnEncodingRules.BER, "04024869")] public static void GetIA5String_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => reader.ReadCharacterString(UniversalTagNumber.IA5String)); } [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "16")] - [InlineData("Missing Length", PublicEncodingRules.CER, "16")] - [InlineData("Missing Length", PublicEncodingRules.DER, "16")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "1601")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "1601")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "1601")] - [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "3601")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "3680")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "3680")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "16034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "16034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "16034869")] - [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "3603040149")] - [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "3603040149")] - [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "36800401490000")] - [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "36800401490000")] - [InlineData("No nested content", PublicEncodingRules.CER, "36800000")] - [InlineData("No EoC", PublicEncodingRules.BER, "3680" + "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] - [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "3604" + "16024869")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "3604800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "3680800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "3680800400FACE0000")] - [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "3607" + ("2402" + "0403") + "040149")] - [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "3603" + "040548656C6C6F")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "368020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "368020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "3680000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "3680000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "3680008100")] - [InlineData("Bad IA5 value", PublicEncodingRules.BER, "1602E280")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "16")] + [InlineData("Missing Length", AsnEncodingRules.CER, "16")] + [InlineData("Missing Length", AsnEncodingRules.DER, "16")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "1601")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "1601")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "1601")] + [InlineData("Missing Contents - Constructed", AsnEncodingRules.BER, "3601")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.BER, "3680")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.CER, "3680")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "16034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "16034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "16034869")] + [InlineData("Definite Constructed Form", AsnEncodingRules.CER, "3603040149")] + [InlineData("Definite Constructed Form", AsnEncodingRules.DER, "3603040149")] + [InlineData("Indefinite Constructed Form - Short Payload", AsnEncodingRules.CER, "36800401490000")] + [InlineData("Indefinite Constructed Form", AsnEncodingRules.DER, "36800401490000")] + [InlineData("No nested content", AsnEncodingRules.CER, "36800000")] + [InlineData("No EoC", AsnEncodingRules.BER, "3680" + "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", AsnEncodingRules.BER, "3604" + "16024869")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "3604800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "3680800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "3680800400FACE0000")] + [InlineData("Nested Length Too Long", AsnEncodingRules.BER, "3607" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", AsnEncodingRules.BER, "3603" + "040548656C6C6F")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "368020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "368020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "3680000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "3680000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "3680008100")] + [InlineData("Bad IA5 value", AsnEncodingRules.BER, "1602E280")] public static void TryCopyIA5String_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; @@ -437,7 +437,7 @@ public static void TryCopyIA5String_Throws_CER_NestedTooLong() input[5] = 0xE9; // EOC implicit since the byte[] initializes to zeros - TryCopyIA5String_Throws_Helper(PublicEncodingRules.CER, input); + TryCopyIA5String_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -475,7 +475,7 @@ public static void TryCopyIA5String_Throws_CER_NestedTooShortIntermediate() input[1011] = 0x02; // EOC implicit since the byte[] initializes to zeros - TryCopyIA5String_Throws_Helper(PublicEncodingRules.CER, input); + TryCopyIA5String_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -581,13 +581,13 @@ public static void TryCopyIA5StringBytes_Success_CER_MinConstructedLength() } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = { 0x16, 2, (byte)'e', (byte)'l' }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -595,7 +595,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.TryGetIA5StringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -606,13 +606,13 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = { 0x87, 2, (byte)'h', (byte)'i' }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -620,16 +620,16 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.TryGetIA5StringBytes(out _)); + Assert.Throws(() => reader.TryGetIA5StringBytes(out _)); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.TryGetIA5StringBytes(new Asn1Tag(TagClass.Application, 0), out _)); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.TryGetIA5StringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -644,38 +644,87 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER, "16026869", PublicTagClass.Universal, 22)] - [InlineData(PublicEncodingRules.CER, "16026869", PublicTagClass.Universal, 22)] - [InlineData(PublicEncodingRules.DER, "16026869", PublicTagClass.Universal, 22)] - [InlineData(PublicEncodingRules.BER, "80023132", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C023132", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A46023132", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "16026869", TagClass.Universal, 22)] + [InlineData(AsnEncodingRules.CER, "16026869", TagClass.Universal, 22)] + [InlineData(AsnEncodingRules.DER, "16026869", TagClass.Universal, 22)] + [InlineData(AsnEncodingRules.BER, "80023132", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C023132", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A46023132", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Asn1Tag correctCons = new Asn1Tag(tagClass, tagValue, true); + Asn1Tag correctPrim = new Asn1Tag(tagClass, tagValue, false); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryGetIA5StringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, true), + correctCons, out ReadOnlyMemory val1)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryGetIA5StringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, false), + correctPrim, out ReadOnlyMemory val2)); Assert.False(reader.HasData); Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + +#if NETCOREAPP + string expected = Encoding.ASCII.GetString(val1.Span); +#else + string expected = Encoding.ASCII.GetString(val1.ToArray()); +#endif + + reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadCharacterString(UniversalTagNumber.IA5String, correctPrim)); + + reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadCharacterString(UniversalTagNumber.IA5String, correctCons)); + + char[] output = new char[28]; + + reader = new AsnReader(inputData, ruleSet); + + Assert.True( + reader.TryReadCharacterString( + output.AsSpan(1), + UniversalTagNumber.IA5String, + out int charsWritten, + correctPrim)); + + Assert.Equal(expected, output.AsSpan(1, charsWritten).ToString()); + + reader = new AsnReader(inputData, ruleSet); + + Assert.True( + reader.TryReadCharacterString( + output.AsSpan(2), + UniversalTagNumber.IA5String, + out charsWritten, + correctCons)); + + Assert.Equal(expected, output.AsSpan(2, charsWritten).ToString()); + } + + [Fact] + public static void TryReadCharacterStringBytes_WrongKind() + { + byte[] inputData = "16026869".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryReadCharacterStringBytes(Span.Empty, Asn1Tag.Boolean, out _)); } } @@ -686,7 +735,7 @@ public static bool TryGetIA5StringBytes( out ReadOnlyMemory contents) { return reader.TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber.IA5String, + new Asn1Tag(UniversalTagNumber.IA5String), out contents); } @@ -697,7 +746,6 @@ public static bool TryGetIA5StringBytes( { return reader.TryReadPrimitiveCharacterStringBytes( expectedTag, - UniversalTagNumber.IA5String, out contents); } @@ -706,9 +754,9 @@ public static bool TryCopyIA5StringBytes( Span destination, out int bytesWritten) { - return reader.TryCopyCharacterStringBytes( - UniversalTagNumber.IA5String, + return reader.TryReadCharacterStringBytes( destination, + new Asn1Tag(UniversalTagNumber.IA5String), out bytesWritten); } @@ -717,9 +765,9 @@ public static bool TryCopyIA5String( Span destination, out int charsWritten) { - return reader.TryCopyCharacterString( - UniversalTagNumber.IA5String, + return reader.TryReadCharacterString( destination, + UniversalTagNumber.IA5String, out charsWritten); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs new file mode 100644 index 00000000000000..351455ec3ab01d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs @@ -0,0 +1,448 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadInteger + { + [Theory] + [InlineData("Constructed Encoding", AsnEncodingRules.BER, "2203020100")] + [InlineData("Constructed Encoding-Indefinite", AsnEncodingRules.BER, "228002010000")] + [InlineData("Constructed Encoding-Indefinite", AsnEncodingRules.CER, "228002010000")] + [InlineData("Constructed Encoding", AsnEncodingRules.DER, "2203020100")] + [InlineData("Wrong Universal Tag", AsnEncodingRules.BER, "030100")] + [InlineData("Bad Length", AsnEncodingRules.BER, "02030102")] + [InlineData("Incorrect Zero Encoding", AsnEncodingRules.BER, "0200")] + [InlineData("Incorrect Zero Encoding", AsnEncodingRules.CER, "0200")] + [InlineData("Incorrect Zero Encoding", AsnEncodingRules.DER, "0200")] + [InlineData("Redundant Leading 0x00", AsnEncodingRules.BER, "0202007F")] + [InlineData("Redundant Leading 0x00", AsnEncodingRules.CER, "0202007F")] + [InlineData("Redundant Leading 0x00", AsnEncodingRules.DER, "0202007F")] + [InlineData("Redundant Leading 0xFF", AsnEncodingRules.BER, "0202FF80")] + [InlineData("Redundant Leading 0xFF", AsnEncodingRules.CER, "0202FF80")] + [InlineData("Redundant Leading 0xFF", AsnEncodingRules.DER, "0202FF80")] + public static void InvalidData( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + Assert.Throws(() => reader.ReadIntegerBytes()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "020100", 0)] + [InlineData(AsnEncodingRules.DER, "020100", 0)] + [InlineData(AsnEncodingRules.DER, "0201FF", -1)] + [InlineData(AsnEncodingRules.CER, "0202FEFF", unchecked((int)0xFFFF_FEFF))] + [InlineData(AsnEncodingRules.BER, "028102FEEF", unchecked((int)0xFFFF_FEEF))] + [InlineData(AsnEncodingRules.BER, "02810480000000", int.MinValue)] + [InlineData(AsnEncodingRules.CER, "020480000000", int.MinValue)] + [InlineData(AsnEncodingRules.DER, "02047FFFFFFF", int.MaxValue)] + [InlineData(AsnEncodingRules.DER, "02026372", 0x6372)] + [InlineData(AsnEncodingRules.CER, "0203008ACE", 0x8ACE)] + [InlineData(AsnEncodingRules.BER, "0203FACE01", unchecked((int)0xFFFA_CE01))] + [InlineData(AsnEncodingRules.BER, "02820003FACE01", unchecked((int)0xFFFA_CE01))] + public static void ReadInt32_Success( + AsnEncodingRules ruleSet, + string inputHex, + int expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt32(out int value); + + Assert.True(didRead, "reader.TryReadInt32"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "02050102030405")] + [InlineData(AsnEncodingRules.CER, "02050102030405")] + [InlineData(AsnEncodingRules.DER, "02050102030405")] + public static void ReadInt32_TooMuchData( + AsnEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt32(out int value); + + Assert.False(didRead, "reader.TryReadInt32"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "02020080", 0x80)] + [InlineData(AsnEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(AsnEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(AsnEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(AsnEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(AsnEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(AsnEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.BER, "02830000050083828180", 0x83828180)] + public static void ReadUInt32_Success( + AsnEncodingRules ruleSet, + string inputHex, + uint expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt32(out uint value); + + Assert.True(didRead, "reader.TryReadUInt32"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020180")] + [InlineData(AsnEncodingRules.CER, "020180")] + [InlineData(AsnEncodingRules.DER, "020180")] + [InlineData(AsnEncodingRules.BER, "0201FF")] + [InlineData(AsnEncodingRules.CER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "02028000")] + [InlineData(AsnEncodingRules.DER, "0203800000")] + [InlineData(AsnEncodingRules.DER, "020480000000")] + [InlineData(AsnEncodingRules.DER, "02050100000000")] + public static void ReadUInt32_Failure(AsnEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt32(out uint value); + + Assert.False(didRead, "reader.TryReadUInt32"); + Assert.Equal((uint)0, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "02020080", 0x80)] + [InlineData(AsnEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(AsnEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(AsnEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(AsnEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(AsnEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(AsnEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.BER, "02830000050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.DER, "02050183828180", 0x0183828180)] + [InlineData(AsnEncodingRules.DER, "0206018483828180", 0x018483828180)] + [InlineData(AsnEncodingRules.DER, "020701858483828180", 0x01858483828180)] + [InlineData(AsnEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] + [InlineData(AsnEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] + [InlineData(AsnEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] + [InlineData(AsnEncodingRules.DER, "0201FF", -1)] + [InlineData(AsnEncodingRules.DER, "0201FE", -2)] + [InlineData(AsnEncodingRules.DER, "02028012", unchecked((long)0xFFFFFFFF_FFFF8012))] + [InlineData(AsnEncodingRules.DER, "0203818012", unchecked((long)0xFFFFFFFF_FF818012))] + [InlineData(AsnEncodingRules.DER, "020482818012", unchecked((long)0xFFFFFFFF_82818012))] + [InlineData(AsnEncodingRules.DER, "02058382818012", unchecked((long)0xFFFFFF83_82818012))] + [InlineData(AsnEncodingRules.DER, "0206848382818012", unchecked((long)0xFFFF8483_82818012))] + [InlineData(AsnEncodingRules.DER, "020785848382818012", unchecked((long)0xFF858483_82818012))] + [InlineData(AsnEncodingRules.DER, "02088685848382818012", unchecked((long)0x86858483_82818012))] + [InlineData(AsnEncodingRules.DER, "02088000000000000000", long.MinValue)] + [InlineData(AsnEncodingRules.BER, "028800000000000000088000000000000000", long.MinValue)] + public static void ReadInt64_Success( + AsnEncodingRules ruleSet, + string inputHex, + long expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt64(out long value); + + Assert.True(didRead, "reader.TryReadInt64"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0209010203040506070809")] + public static void ReadInt64_TooMuchData( + AsnEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt64(out long value); + + Assert.False(didRead, "reader.TryReadInt64"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "02020080", 0x80)] + [InlineData(AsnEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(AsnEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(AsnEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(AsnEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(AsnEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(AsnEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.BER, "02830000050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.DER, "02050183828180", 0x0183828180)] + [InlineData(AsnEncodingRules.DER, "0206018483828180", 0x018483828180)] + [InlineData(AsnEncodingRules.DER, "020701858483828180", 0x01858483828180)] + [InlineData(AsnEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] + [InlineData(AsnEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] + [InlineData(AsnEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] + [InlineData(AsnEncodingRules.DER, "0209008000000000000000", 0x80000000_00000000)] + [InlineData(AsnEncodingRules.DER, "020900FFFFFFFFFFFFFFFF", ulong.MaxValue)] + public static void ReadUInt64_Success( + AsnEncodingRules ruleSet, + string inputHex, + ulong expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt64(out ulong value); + + Assert.True(didRead, "reader.TryReadUInt64"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020180")] + [InlineData(AsnEncodingRules.CER, "020180")] + [InlineData(AsnEncodingRules.DER, "020180")] + [InlineData(AsnEncodingRules.BER, "0201FF")] + [InlineData(AsnEncodingRules.CER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "02028000")] + [InlineData(AsnEncodingRules.DER, "0203800000")] + [InlineData(AsnEncodingRules.DER, "020480000000")] + [InlineData(AsnEncodingRules.DER, "02058000000000")] + [InlineData(AsnEncodingRules.DER, "0206800000000000")] + [InlineData(AsnEncodingRules.DER, "020780000000000000")] + [InlineData(AsnEncodingRules.DER, "02088000000000000000")] + [InlineData(AsnEncodingRules.DER, "0209010000000000000000")] + public static void ReadUInt64_Failure(AsnEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt64(out ulong value); + + Assert.False(didRead, "reader.TryReadUInt64"); + Assert.Equal((uint)0, value); + } + + [Fact] + public static void ReadIntegerBytes() + { + const string Payload = "0102030405060708090A0B0C0D0E0F10"; + + // INTEGER (payload) followed by INTEGER (0) + byte[] data = ("0210" + Payload + "020100").HexToByteArray(); + AsnReader reader = new AsnReader(data, AsnEncodingRules.DER); + + ReadOnlyMemory contents = reader.ReadIntegerBytes(); + Assert.Equal(0x10, contents.Length); + Assert.Equal(Payload, contents.ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetBigInteger(AsnEncodingRules ruleSet) + { + byte[] inputData = ( + "0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B4D").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "2075455505718444046766086325128514549301113944667492053189486607" + + "5638152321436011512404808361119326026027238444019992518081753153" + + "5965931647037093368608713442955529617501657176146245891571745113" + + "4028700771890451167051818999837042261788828826028681595867897235" + + "7967091503500375497498573022675671178275171110498592645868107163" + + "8525996766798322809764200941677343791419428587801897366593842552" + + "7272226864578661449281241619675217353931828233756506947863330597" + + "8338073826285687331647183058971791153730741973483420110408271570" + + "1367336140572971505716740825623220507359429297584634909330541150" + + "79473593821332264673455059897928082590541"); + + AsnReader reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadInteger()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetNegativeBigInteger(AsnEncodingRules ruleSet) + { + // This uses a length that doesn't line up with the array pool size so + // the fill code gets tested on netstandard. + // It's the same data as above, minus the padding and lead byte (and the + // next byte changed from 0x68 to 0x88) + byte[] inputData = ( + "0281FF8861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B4D").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "-" + + "5898547409447487884446992857601985651316300515844052200158198046" + + "7814538020408452501006415149581619776188797413593169277984980446" + + "1302361382932378450492052337986628823880000831383555853860116718" + + "5361729331647715885538858385106930514758305144777880150203212976" + + "6715081632226412951106013360243549075631850526067219857352295397" + + "2308328327377769665309386917336850273904442596855844458638806936" + + "6169824439111394938336579524651037239551388910737675470211780509" + + "8035477769907389338548451561341965157059382875181284370047601682" + + "6924486017215979427815833587119797658480104671279234402026468966" + + "86109928634475300812601680679147599027"); + + AsnReader reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadInteger()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetDiminutiveBigInteger(AsnEncodingRules ruleSet) + { + // GetBigInteger with the last byte removed. + // Since it is no longer an ArrayPool alignment size the fill code gets tested on netstandard. + byte[] inputData = ( + "0282010000A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "2075455505718444046766086325128514549301113944667492053189486607" + + "5638152321436011512404808361119326026027238444019992518081753153" + + "5965931647037093368608713442955529617501657176146245891571745113" + + "4028700771890451167051818999837042261788828826028681595867897235" + + "7967091503500375497498573022675671178275171110498592645868107163" + + "8525996766798322809764200941677343791419428587801897366593842552" + + "7272226864578661449281241619675217353931828233756506947863330597" + + "8338073826285687331647183058971791153730741973483420110408271570" + + "1367336140572971505716740825623220507359429297584634909330541150" + + "79473593821332264673455059897928082590541") >> 8; + + AsnReader reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadInteger()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 2, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + ReadOnlyMemory value = reader.ReadIntegerBytes(); + Assert.Equal("7E", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadIntegerBytes()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + ReadOnlyMemory value = reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.Equal("0080", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0201FF", TagClass.Universal, 2)] + [InlineData(AsnEncodingRules.CER, "0201FF", TagClass.Universal, 2)] + [InlineData(AsnEncodingRules.DER, "0201FF", TagClass.Universal, 2)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory val1 = reader.ReadIntegerBytes(new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory val2 = reader.ReadIntegerBytes(new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs new file mode 100644 index 00000000000000..c0a8587912ac10 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadLength + { + private delegate Asn1Tag ReadTagAndLengthDelegate( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? parsedLength, + out int bytesRead); + + private static ReadTagAndLengthDelegate ReadTagAndLength = (ReadTagAndLengthDelegate) + typeof(AsnDecoder).GetMethod("ReadTagAndLength", BindingFlags.Static | BindingFlags.NonPublic) + .CreateDelegate(typeof(ReadTagAndLengthDelegate)); + + [Theory] + [InlineData(4, 0, "0400")] + [InlineData(1, 1, "0101")] + [InlineData(4, 127, "047F")] + [InlineData(4, 128, "048180")] + [InlineData(4, 255, "0481FF")] + [InlineData(2, 256, "02820100")] + [InlineData(4, int.MaxValue, "04847FFFFFFF")] + public static void MinimalPrimitiveLength(int tagValue, int length, string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + foreach (AsnEncodingRules rules in Enum.GetValues(typeof(AsnEncodingRules))) + { + Asn1Tag tag = ReadTagAndLength(inputBytes, rules, out int? parsedLength, out int bytesRead); + + Assert.Equal(inputBytes.Length, bytesRead); + Assert.False(tag.IsConstructed, "tag.IsConstructed"); + Assert.Equal(tagValue, tag.TagValue); + Assert.Equal(length, parsedLength.Value); + } + } + + [Theory] + [InlineData(-1)] + [InlineData(3)] + public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) + { + byte[] data = { 0x05, 0x00 }; + + Assert.Throws( + () => new AsnReader(data, (AsnEncodingRules)invalidRuleSetValue)); + } + + [Theory] + [InlineData("")] + [InlineData("05")] + [InlineData("0481")] + [InlineData("048201")] + [InlineData("04830102")] + [InlineData("0484010203")] + public static void ReadWithInsufficientData(string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + Assert.Throws( + () => ReadTagAndLength(inputData, AsnEncodingRules.DER, out _, out _)); + } + + [Theory] + [InlineData("DER indefinite constructed", AsnEncodingRules.DER, "3080" + "0500" + "0000")] + [InlineData("0xFF-BER", AsnEncodingRules.BER, "04FF")] + [InlineData("0xFF-CER", AsnEncodingRules.CER, "04FF")] + [InlineData("0xFF-DER", AsnEncodingRules.DER, "04FF")] + [InlineData("CER definite constructed", AsnEncodingRules.CER, "30820500")] + [InlineData("BER indefinite primitive", AsnEncodingRules.BER, "0480" + "0000")] + [InlineData("CER indefinite primitive", AsnEncodingRules.CER, "0480" + "0000")] + [InlineData("DER indefinite primitive", AsnEncodingRules.DER, "0480" + "0000")] + [InlineData("DER non-minimal 0", AsnEncodingRules.DER, "048100")] + [InlineData("DER non-minimal 7F", AsnEncodingRules.DER, "04817F")] + [InlineData("DER non-minimal 80", AsnEncodingRules.DER, "04820080")] + [InlineData("CER non-minimal 0", AsnEncodingRules.CER, "048100")] + [InlineData("CER non-minimal 7F", AsnEncodingRules.CER, "04817F")] + [InlineData("CER non-minimal 80", AsnEncodingRules.CER, "04820080")] + [InlineData("BER too large", AsnEncodingRules.BER, "048480000000")] + [InlineData("CER too large", AsnEncodingRules.CER, "048480000000")] + [InlineData("DER too large", AsnEncodingRules.DER, "048480000000")] + [InlineData("BER padded too large", AsnEncodingRules.BER, "0486000080000000")] + [InlineData("BER uint.MaxValue", AsnEncodingRules.BER, "0484FFFFFFFF")] + [InlineData("CER uint.MaxValue", AsnEncodingRules.CER, "0484FFFFFFFF")] + [InlineData("DER uint.MaxValue", AsnEncodingRules.DER, "0484FFFFFFFF")] + [InlineData("BER padded uint.MaxValue", AsnEncodingRules.BER, "048800000000FFFFFFFF")] + [InlineData("BER 5 byte spread", AsnEncodingRules.BER, "04850100000000")] + [InlineData("CER 5 byte spread", AsnEncodingRules.CER, "04850100000000")] + [InlineData("DER 5 byte spread", AsnEncodingRules.DER, "04850100000000")] + [InlineData("BER padded 5 byte spread", AsnEncodingRules.BER, "0486000100000000")] + public static void InvalidLengths( + string description, + AsnEncodingRules rules, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, rules); + + Assert.Throws( + () => ReadTagAndLength(inputData, rules, out _, out _)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void IndefiniteLength(AsnEncodingRules ruleSet) + { + // SEQUENCE (indefinite) + // NULL + // End-of-Contents + byte[] data = { 0x30, 0x80, 0x05, 0x00, 0x00, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + Asn1Tag tag = ReadTagAndLength( + data, + ruleSet, + out int? length, + out int bytesRead); + + Assert.Equal(2, bytesRead); + Assert.False(length.HasValue, "length.HasValue"); + Assert.Equal((int)UniversalTagNumber.Sequence, tag.TagValue); + Assert.True(tag.IsConstructed, "tag.IsConstructed"); + } + + [Theory] + [InlineData(0, "0483000000")] + [InlineData(1, "048A00000000000000000001")] + [InlineData(128, "049000000000000000000000000000000080")] + public static void BerNonMinimalLength(int expectedLength, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Asn1Tag tag = ReadTagAndLength( + inputData, + AsnEncodingRules.BER, + out int? length, + out int bytesRead); + + Assert.Equal(inputData.Length, bytesRead); + Assert.Equal(expectedLength, length.Value); + // ReadTagAndLength doesn't move the _data span forward. + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 4, 0, 5, "0483000000" + "0500")] + [InlineData(AsnEncodingRules.DER, 1, 1, 2, "0101" + "FF")] + [InlineData(AsnEncodingRules.CER, 0x10, null, 2, "3080" + "0500" + "0000")] + public static void ReadWithDataRemaining( + AsnEncodingRules ruleSet, + int tagValue, + int? expectedLength, + int expectedBytesRead, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + Asn1Tag tag = ReadTagAndLength( + inputData, + ruleSet, + out int? length, + out int bytesRead); + + Assert.Equal(expectedBytesRead, bytesRead); + Assert.Equal(tagValue, tag.TagValue); + Assert.Equal(expectedLength, length); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs new file mode 100644 index 00000000000000..4aa9e539f507b4 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs @@ -0,0 +1,518 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadNamedBitList + { + [Flags] + public enum X509KeyUsageCSharpStyle + { + None = 0, + DigitalSignature = 1, + NonRepudiation = 1 << 1, + KeyEncipherment = 1 << 2, + DataEncipherment = 1 << 3, + KeyAgreement = 1 << 4, + KeyCertSign = 1 << 5, + CrlSign = 1 << 6, + EncipherOnly = 1 << 7, + DecipherOnly = 1 << 8, + } + + [Flags] + public enum ULongFlags : ulong + { + None = 0, + Min = 1, + Mid = 1L << 32, + AlmostMax = 1L << 62, + Max = 1UL << 63, + } + + [Flags] + public enum LongFlags : long + { + None = 0, + Mid = 1L << 32, + Max = 1L << 62, + Min = long.MinValue, + } + + [Theory] + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.None), + "030100")] + [InlineData( + AsnEncodingRules.CER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), + "0303070480")] + [InlineData( + AsnEncodingRules.DER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.KeyAgreement), + "03020308")] + [InlineData( + AsnEncodingRules.BER, + typeof(LongFlags), + (long)(LongFlags.Mid | LongFlags.Max), + "0309010000000080000002")] + [InlineData( + AsnEncodingRules.CER, + typeof(LongFlags), + (long)(LongFlags.Mid | LongFlags.Min), + "0309000000000080000001")] + [InlineData( + AsnEncodingRules.DER, + typeof(LongFlags), + (long)(LongFlags.Min | LongFlags.Max), + "0309000000000000000003")] + // BER: Unused bits are unmapped, regardless of value. + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), + "030307048F")] + // BER: Trailing zeros are permitted. + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), + "03050014800000")] + // BER: Trailing 0-bits don't have to be declared "unused" + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), + "0303001480")] + public static void VerifyReadNamedBitListEncodings( + AsnEncodingRules ruleSet, + Type enumType, + long enumValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputBytes, ruleSet); + Enum readValue = reader.ReadNamedBitListValue(enumType); + + Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); + } + + [Theory] + [InlineData( + AsnEncodingRules.BER, + typeof(ULongFlags), + (ulong)(ULongFlags.Mid | ULongFlags.Max), + "0309000000000080000001")] + [InlineData( + AsnEncodingRules.CER, + typeof(ULongFlags), + (ulong)(ULongFlags.Min | ULongFlags.Mid), + "0306078000000080")] + [InlineData( + AsnEncodingRules.DER, + typeof(ULongFlags), + (ulong)(ULongFlags.Min | ULongFlags.Max), + "0309008000000000000001")] + public static void VerifyReadNamedBitListEncodings_ULong( + AsnEncodingRules ruleSet, + Type enumType, + ulong enumValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputBytes, ruleSet); + Enum readValue = reader.ReadNamedBitListValue(enumType); + + Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyGenericReadNamedBitList(AsnEncodingRules ruleSet) + { + string inputHex = "0306078000000080" + "0309010000000080000002"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + ULongFlags uLongFlags = reader.ReadNamedBitListValue(); + LongFlags longFlags = reader.ReadNamedBitListValue(); + + Assert.False(reader.HasData); + Assert.Equal(ULongFlags.Mid | ULongFlags.Min, uLongFlags); + Assert.Equal(LongFlags.Mid | LongFlags.Max, longFlags); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_RequiresTypeArg(AsnEncodingRules ruleSet) + { + string inputHex = "030100"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + AssertExtensions.Throws( + "flagsEnumType", + () => reader.ReadNamedBitListValue(null!)); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_RequiresFlags(AsnEncodingRules ruleSet) + { + string inputHex = "030100"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + AssertExtensions.Throws( + "flagsEnumType", + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_DataOutOfRange(AsnEncodingRules ruleSet) + { + string inputHex = "0309000000000100000001"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBytes(AsnEncodingRules ruleSet) + { + string inputHex = "03050014800000"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBits(AsnEncodingRules ruleSet) + { + string inputHex = "0303061480"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 3, 2, 1, 2 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNamedBitListValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + X509KeyUsageCSharpStyle.CrlSign, + reader.ReadNamedBitListValue()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 2, 4 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNamedBitListValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + X509KeyUsageCSharpStyle.KeyCertSign, + reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0303070080", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.CER, "0303070080", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.DER, "0303070080", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.BER, "8003070080", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C03070080", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4603070080", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Equal( + X509KeyUsageCSharpStyle.DecipherOnly, + reader.ReadNamedBitListValue( + new Asn1Tag(tagClass, tagValue, true))); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + Assert.Equal( + X509KeyUsageCSharpStyle.DecipherOnly, + reader.ReadNamedBitListValue( + new Asn1Tag(tagClass, tagValue, false))); + + Assert.False(reader.HasData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray(AsnEncodingRules ruleSet) + { + byte[] inputData = "440406400100".HexToByteArray(); + bool[] expected = new bool[18]; + expected[1] = expected[15] = true; + + AsnReader reader = new AsnReader(inputData, ruleSet); + + BitArray bits = reader.ReadNamedBitList(new Asn1Tag(TagClass.Application, 4)); + Assert.Equal(expected.Length, bits.Length); + Assert.False(reader.HasData); + + bool[] actual = new bool[expected.Length]; + bits.CopyTo(actual, 0); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_Empty(AsnEncodingRules ruleSet) + { + byte[] inputData = "030100".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + BitArray bits = reader.ReadNamedBitList(); + Assert.Equal(0, bits.Length); + Assert.False(reader.HasData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_EveryPattern(AsnEncodingRules ruleSet) + { + const string InputHex = + // Tag + "DF836B" + + // Length + "820101" + + // Unused bit count + "00" + + // Reversed bits for byte patterns 0x00-0x1F + "008040C020A060E0109050D030B070F0088848C828A868E8189858D838B878F8" + + // Reversed bits for byte patterns 0x20-0x3F + "048444C424A464E4149454D434B474F40C8C4CCC2CAC6CEC1C9C5CDC3CBC7CFC" + + // Reversed bits for byte patterns 0x40-0x5F + "028242C222A262E2129252D232B272F20A8A4ACA2AAA6AEA1A9A5ADA3ABA7AFA" + + // Reversed bits for byte patterns 0x60-0x7F + "068646C626A666E6169656D636B676F60E8E4ECE2EAE6EEE1E9E5EDE3EBE7EFE" + + // Reversed bits for byte patterns 0x80-0x9F + "018141C121A161E1119151D131B171F1098949C929A969E9199959D939B979F9" + + // Reversed bits for byte patterns 0xA0-0xBF + "058545C525A565E5159555D535B575F50D8D4DCD2DAD6DED1D9D5DDD3DBD7DFD" + + // Reversed bits for byte patterns 0xC0-0xDF + "038343C323A363E3139353D333B373F30B8B4BCB2BAB6BEB1B9B5BDB3BBB7BFB" + + // Reversed bits for byte patterns 0xE0-0xFF + "078747C727A767E7179757D737B777F70F8F4FCF2FAF6FEF1F9F5FDF3FBF7FFF"; + + byte[] inputData = InputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + byte[] allTheBytes = new byte[256]; + + for (int i = 0; i < allTheBytes.Length; i++) + { + allTheBytes[i] = (byte)i; + } + + BitArray bits = reader.ReadNamedBitList(new Asn1Tag(TagClass.Private, 491)); + Assert.Equal(allTheBytes.Length * 8, bits.Length); + Assert.False(reader.HasData); + + byte[] actual = new byte[allTheBytes.Length]; + bits.CopyTo(actual, 0); + + Assert.Equal(actual, actual); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_7992Bits(AsnEncodingRules ruleSet) + { + string inputHex = "848203E80008" + new string('0', 1994) + "02"; + byte[] inputData = inputHex.HexToByteArray(); + + BitArray expected = new BitArray(7992); + expected.Set(4, true); + expected.Set(7990, true); + + AsnReader reader = new AsnReader(inputData, ruleSet); + BitArray actual = reader.ReadNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 4)); + Assert.False(reader.HasData); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_7993Bits(AsnEncodingRules ruleSet) + { + string inputHex; + + if (ruleSet == AsnEncodingRules.CER) + { + inputHex = "A580038203E8" + new string('0', 2000) + "03020780" + "0000"; + } + else + { + inputHex = "858203E907" + new string('0', 1998) + "80"; + } + + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + BitArray actual = reader.ReadNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.False(reader.HasData); + + BitArray expected = new BitArray(7993); + expected.Set(7992, true); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyReadNamedBitList_KeyUsage_OneByte(AsnEncodingRules ruleSet) + { + // KeyUsage ::= BIT STRING { + // digitalSignature (0), + // nonRepudiation (1), + // keyEncipherment (2), + // dataEncipherment (3), + // keyAgreement (4), + // keyCertSign (5), + // cRLSign (6), + // encipherOnly (7), + // decipherOnly (8) } + + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, + critical: false); + + BitArray expected = new BitArray(7); + expected.Set(6, true); + expected.Set(5, true); + + AsnReader reader = new AsnReader(kuExt.RawData, ruleSet); + BitArray actual = reader.ReadNamedBitList(); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyReadNamedBitList_KeyUsage_TwoByte(AsnEncodingRules ruleSet) + { + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyAgreement | X509KeyUsageFlags.DecipherOnly, + critical: false); + + BitArray expected = new BitArray(9); + expected.Set(4, true); + expected.Set(8, true); + + AsnReader reader = new AsnReader(kuExt.RawData, ruleSet); + BitArray actual = reader.ReadNamedBitList(); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs new file mode 100644 index 00000000000000..f71e37946f156f --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadNull + { + [Theory] + [InlineData(AsnEncodingRules.BER, "0500")] + [InlineData(AsnEncodingRules.CER, "0500")] + [InlineData(AsnEncodingRules.DER, "0500")] + [InlineData(AsnEncodingRules.BER, "0583000000")] + public static void ReadNull_Success(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + reader.ReadNull(); + Assert.False(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData("Long length", AsnEncodingRules.CER, "0583000000")] + [InlineData("Long length", AsnEncodingRules.DER, "0583000000")] + [InlineData("Constructed definite length", AsnEncodingRules.BER, "2500")] + [InlineData("Constructed definite length", AsnEncodingRules.DER, "2500")] + [InlineData("Constructed indefinite length", AsnEncodingRules.BER, "25800000")] + [InlineData("Constructed indefinite length", AsnEncodingRules.CER, "25800000")] + [InlineData("No length", AsnEncodingRules.BER, "05")] + [InlineData("No length", AsnEncodingRules.CER, "05")] + [InlineData("No length", AsnEncodingRules.DER, "05")] + [InlineData("No data", AsnEncodingRules.BER, "")] + [InlineData("No data", AsnEncodingRules.CER, "")] + [InlineData("No data", AsnEncodingRules.DER, "")] + [InlineData("NonEmpty", AsnEncodingRules.BER, "050100")] + [InlineData("NonEmpty", AsnEncodingRules.CER, "050100")] + [InlineData("NonEmpty", AsnEncodingRules.DER, "050100")] + [InlineData("Incomplete length", AsnEncodingRules.BER, "0581")] + public static void ReadNull_Throws(string description, AsnEncodingRules ruleSet, string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadNull()); + } + + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 5, 0 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + reader.ReadNull(); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 0 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadNull()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0500", TagClass.Universal, 5)] + [InlineData(AsnEncodingRules.CER, "0500", TagClass.Universal, 5)] + [InlineData(AsnEncodingRules.DER, "0500", TagClass.Universal, 5)] + [InlineData(AsnEncodingRules.BER, "8000", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C00", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4600", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + reader.ReadNull(new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + reader.ReadNull(new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs similarity index 51% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs index 8ff87d12c39050..50a3276e621305 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs @@ -2,74 +2,73 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using System.Text; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadObjectIdentifier : Asn1ReaderTests + public sealed class ReadObjectIdentifier { [Theory] - [InlineData("Wrong tag", PublicEncodingRules.BER, "010100")] - [InlineData("Wrong tag", PublicEncodingRules.CER, "010100")] - [InlineData("Wrong tag", PublicEncodingRules.DER, "010100")] - [InlineData("Overreaching length", PublicEncodingRules.BER, "0608883703")] - [InlineData("Overreaching length", PublicEncodingRules.CER, "0608883703")] - [InlineData("Overreaching length", PublicEncodingRules.DER, "0608883703")] - [InlineData("Zero length", PublicEncodingRules.BER, "0600")] - [InlineData("Zero length", PublicEncodingRules.CER, "0600")] - [InlineData("Zero length", PublicEncodingRules.DER, "0600")] - [InlineData("Constructed Definite Form", PublicEncodingRules.BER, "2605" + "0603883703")] - [InlineData("Constructed Indefinite Form", PublicEncodingRules.BER, "2680" + "0603883703" + "0000")] - [InlineData("Constructed Indefinite Form", PublicEncodingRules.CER, "2680" + "0603883703" + "0000")] - [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.BER, "060188")] - [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.CER, "060188")] - [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.DER, "060188")] - [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.BER, "0603883781")] - [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.CER, "0603883781")] - [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.DER, "0603883781")] - [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.BER, "060488378001")] - [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.CER, "060488378001")] - [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.DER, "060488378001")] + [InlineData("Wrong tag", AsnEncodingRules.BER, "010100")] + [InlineData("Wrong tag", AsnEncodingRules.CER, "010100")] + [InlineData("Wrong tag", AsnEncodingRules.DER, "010100")] + [InlineData("Overreaching length", AsnEncodingRules.BER, "0608883703")] + [InlineData("Overreaching length", AsnEncodingRules.CER, "0608883703")] + [InlineData("Overreaching length", AsnEncodingRules.DER, "0608883703")] + [InlineData("Zero length", AsnEncodingRules.BER, "0600")] + [InlineData("Zero length", AsnEncodingRules.CER, "0600")] + [InlineData("Zero length", AsnEncodingRules.DER, "0600")] + [InlineData("Constructed Definite Form", AsnEncodingRules.BER, "2605" + "0603883703")] + [InlineData("Constructed Indefinite Form", AsnEncodingRules.BER, "2680" + "0603883703" + "0000")] + [InlineData("Constructed Indefinite Form", AsnEncodingRules.CER, "2680" + "0603883703" + "0000")] + [InlineData("Unresolved carry-bit (first sub-identifier)", AsnEncodingRules.BER, "060188")] + [InlineData("Unresolved carry-bit (first sub-identifier)", AsnEncodingRules.CER, "060188")] + [InlineData("Unresolved carry-bit (first sub-identifier)", AsnEncodingRules.DER, "060188")] + [InlineData("Unresolved carry-bit (later sub-identifier)", AsnEncodingRules.BER, "0603883781")] + [InlineData("Unresolved carry-bit (later sub-identifier)", AsnEncodingRules.CER, "0603883781")] + [InlineData("Unresolved carry-bit (later sub-identifier)", AsnEncodingRules.DER, "0603883781")] + [InlineData("Sub-Identifier with leading 0x80", AsnEncodingRules.BER, "060488378001")] + [InlineData("Sub-Identifier with leading 0x80", AsnEncodingRules.CER, "060488378001")] + [InlineData("Sub-Identifier with leading 0x80", AsnEncodingRules.DER, "060488378001")] public static void ReadObjectIdentifier_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadObjectIdentifier()); + Assert.Throws(() => reader.ReadObjectIdentifier()); } [Theory] - [InlineData(PublicEncodingRules.BER, "0603883703", "2.999.3")] - [InlineData(PublicEncodingRules.CER, "06028837", "2.999")] - [InlineData(PublicEncodingRules.DER, "06068837C27B0302", "2.999.8571.3.2")] - [InlineData(PublicEncodingRules.BER, "0603550406", "2.5.4.6")] - [InlineData(PublicEncodingRules.CER, "06092A864886F70D010105", "1.2.840.113549.1.1.5")] - [InlineData(PublicEncodingRules.DER, "060100", "0.0")] - [InlineData(PublicEncodingRules.BER, "06080992268993F22C63", "0.9.2342.19200300.99")] + [InlineData(AsnEncodingRules.BER, "0603883703", "2.999.3")] + [InlineData(AsnEncodingRules.CER, "06028837", "2.999")] + [InlineData(AsnEncodingRules.DER, "06068837C27B0302", "2.999.8571.3.2")] + [InlineData(AsnEncodingRules.BER, "0603550406", "2.5.4.6")] + [InlineData(AsnEncodingRules.CER, "06092A864886F70D010105", "1.2.840.113549.1.1.5")] + [InlineData(AsnEncodingRules.DER, "060100", "0.0")] + [InlineData(AsnEncodingRules.BER, "06080992268993F22C63", "0.9.2342.19200300.99")] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, "0616824F83F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 // this is // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } "2.255.329800735698586629295641978511506172918.3")] - public static void ReadObjectIdentifierAsString_Success( - PublicEncodingRules ruleSet, + public static void ReadObjectIdentifier_Success( + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - string oidValue = reader.ReadObjectIdentifierAsString(); + string oidValue = reader.ReadObjectIdentifier(); Assert.Equal(expectedValue, oidValue); } @@ -103,33 +102,18 @@ public static void VerifyMultiByteParsing(string inputHex, string expectedValue) byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, AsnEncodingRules.DER); - string oidValue = reader.ReadObjectIdentifierAsString(); + string oidValue = reader.ReadObjectIdentifier(); Assert.Equal(expectedValue, oidValue); } [Theory] - [InlineData(PublicEncodingRules.BER, "06082A864886F70D0307", "3des")] - [InlineData(PublicEncodingRules.BER, "0609608648016503040201", "sha256")] - public static void ReadObjectIdentifier_FriendlyName( - PublicEncodingRules ruleSet, - string inputHex, - string expectedFriendlyName) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Oid oid = reader.ReadObjectIdentifier(); - Assert.Equal(expectedFriendlyName, oid.FriendlyName); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = "06028837".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -137,23 +121,23 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws( + () => reader.ReadObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); - Assert.Equal("2.999", reader.ReadObjectIdentifierAsString()); + Assert.Equal("2.999", reader.ReadObjectIdentifier()); Assert.False(reader.HasData, "HasData after read"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = "87028837".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -161,73 +145,61 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadObjectIdentifierAsString()); + Assert.Throws(() => reader.ReadObjectIdentifier()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( - () => reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.Application, 0))); + Assert.Throws( + () => reader.ReadObjectIdentifier(new Asn1Tag(TagClass.Application, 0))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( - () => reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 1))); + Assert.Throws( + () => reader.ReadObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); Assert.Equal( "2.999", - reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 7))); + reader.ReadObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 7))); Assert.False(reader.HasData, "HasData after reading value"); } [Theory] - [InlineData(PublicEncodingRules.BER, "06028837", PublicTagClass.Universal, 6)] - [InlineData(PublicEncodingRules.CER, "06028837", PublicTagClass.Universal, 6)] - [InlineData(PublicEncodingRules.DER, "06028837", PublicTagClass.Universal, 6)] - [InlineData(PublicEncodingRules.BER, "80028837", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C028837", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A46028837", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "06028837", TagClass.Universal, 6)] + [InlineData(AsnEncodingRules.CER, "06028837", TagClass.Universal, 6)] + [InlineData(AsnEncodingRules.DER, "06028837", TagClass.Universal, 6)] + [InlineData(AsnEncodingRules.BER, "80028837", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C028837", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A46028837", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - Asn1Tag constructedTag = new Asn1Tag((TagClass)tagClass, tagValue, true); - Asn1Tag primitiveTag = new Asn1Tag((TagClass)tagClass, tagValue, false); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - string val1 = reader.ReadObjectIdentifierAsString(constructedTag); - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Oid oid1 = reader.ReadObjectIdentifier(constructedTag); - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + Asn1Tag constructedTag = new Asn1Tag(tagClass, tagValue, true); + Asn1Tag primitiveTag = new Asn1Tag(tagClass, tagValue, false); + AsnReader reader = new AsnReader(inputData, ruleSet); - string val2 = reader.ReadObjectIdentifierAsString(primitiveTag); + string val1 = reader.ReadObjectIdentifier(constructedTag); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); - Oid oid2 = reader.ReadObjectIdentifier(primitiveTag); + string val2 = reader.ReadObjectIdentifier(primitiveTag); Assert.False(reader.HasData); Assert.Equal(val1, val2); - Assert.Equal(oid1.Value, oid2.Value); - Assert.Equal(oid1.Value, val1); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadVeryLongOid(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadVeryLongOid(AsnEncodingRules ruleSet) { byte[] inputData = new byte[100000]; // 06 83 02 00 00 (OBJECT IDENTIFIER, 65536 bytes). @@ -250,18 +222,18 @@ public static void ReadVeryLongOid(PublicEncodingRules ruleSet) builder.Append(0); } - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - string oidString = reader.ReadObjectIdentifierAsString(); + AsnReader reader = new AsnReader(inputData, ruleSet); + string oidString = reader.ReadObjectIdentifier(); Assert.Equal(ExpectedLength, oidString.Length); Assert.Equal(builder.ToString(), oidString); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadVeryLongOidArc(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadVeryLongOidArc(AsnEncodingRules ruleSet) { byte[] inputData = new byte[255]; // 06 81 93 (OBJECT IDENTIFIER, 147 bytes). @@ -287,9 +259,9 @@ public static void ReadVeryLongOidArc(PublicEncodingRules ruleSet) "352068092908376276711465745599868114846199290762088390824060" + "56034224"; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - string oidString = reader.ReadObjectIdentifierAsString(); + string oidString = reader.ReadObjectIdentifier(); Assert.Equal(ExpectedOid, oidString); } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs similarity index 50% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs index 8919d94a363ab6..57bc752006ce2c 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs @@ -4,79 +4,78 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadOctetString : Asn1ReaderTests + public sealed class ReadOctetString { [Theory] - [InlineData("Constructed Payload", PublicEncodingRules.BER, "2402040100")] - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.BER, "248004010000")] + [InlineData("Constructed Payload", AsnEncodingRules.BER, "2402040100")] + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.BER, "248004010000")] // This value is actually invalid CER, but it returns false since it's not primitive and // it isn't worth preempting the descent to find out it was invalid. - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.CER, "248004010000")] - public static void TryGetOctetStringBytes_Fails( + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.CER, "248004010000")] + public static void TryReadPrimitiveOctetStringBytes_Fails( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + bool didRead = reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents); Assert.False(didRead, "reader.TryReadOctetStringBytes"); Assert.Equal(0, contents.Length); } [Theory] - [InlineData(PublicEncodingRules.BER, 0, "0400")] - [InlineData(PublicEncodingRules.BER, 1, "040100")] - [InlineData(PublicEncodingRules.BER, 2, "040201FE")] - [InlineData(PublicEncodingRules.CER, 5, "040502FEEFF00C")] - [InlineData(PublicEncodingRules.DER, 2, "04020780")] - [InlineData(PublicEncodingRules.DER, 5, "040500FEEFF00D" + "0500")] - public static void TryGetOctetStringBytes_Success( - PublicEncodingRules ruleSet, + [InlineData(AsnEncodingRules.BER, 0, "0400")] + [InlineData(AsnEncodingRules.BER, 1, "040100")] + [InlineData(AsnEncodingRules.BER, 2, "040201FE")] + [InlineData(AsnEncodingRules.CER, 5, "040502FEEFF00C")] + [InlineData(AsnEncodingRules.DER, 2, "04020780")] + [InlineData(AsnEncodingRules.DER, 5, "040500FEEFF00D" + "0500")] + public static void TryReadPrimitiveOctetStringBytes_Success( + AsnEncodingRules ruleSet, int expectedLength, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + bool didRead = reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents); Assert.True(didRead, "reader.TryReadOctetStringBytes"); Assert.Equal(expectedLength, contents.Length); } [Theory] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] - [InlineData("Bad Length", PublicEncodingRules.BER, "040200")] - [InlineData("Bad Length", PublicEncodingRules.CER, "040200")] - [InlineData("Bad Length", PublicEncodingRules.DER, "040200")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "2403040100")] - public static void TryGetOctetStringBytes_Throws( + [InlineData("Wrong Tag", AsnEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.DER, "0500")] + [InlineData("Bad Length", AsnEncodingRules.BER, "040200")] + [InlineData("Bad Length", AsnEncodingRules.CER, "040200")] + [InlineData("Bad Length", AsnEncodingRules.DER, "040200")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "2403040100")] + public static void TryReadPrimitiveOctetStringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( - () => reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents)); + Assert.Throws( + () => reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents)); } [Fact] - public static void TryGetOctetStringBytes_Throws_CER_TooLong() + public static void TryReadPrimitiveOctetStringBytes_Throws_CER_TooLong() { // CER says that the maximum encoding length for an OctetString primitive // is 1000. @@ -92,12 +91,17 @@ public static void TryGetOctetStringBytes_Throws_CER_TooLong() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - Assert.Throws( - () => reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents)); + Assert.Throws( + () => reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents)); + + Assert.Throws( + () => reader.TryReadOctetString(new byte[input.Length], out _)); + + Assert.Throws(() => reader.ReadOctetString()); } [Fact] - public static void TryGetOctetStringBytes_Success_CER_MaxLength() + public static void TryReadPrimitiveOctetStringBytes_Success_CER_MaxLength() { // CER says that the maximum encoding length for an OctetString primitive // is 1000. @@ -119,7 +123,7 @@ public static void TryGetOctetStringBytes_Success_CER_MaxLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + bool success = reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents); Assert.True(success, "reader.TryReadOctetStringBytes"); Assert.Equal(1000, contents.Length); @@ -132,40 +136,40 @@ ref MemoryMarshal.GetReference(contents.Span), } [Theory] - [InlineData(PublicEncodingRules.BER, "04020780")] - [InlineData(PublicEncodingRules.BER, "040207FF")] - [InlineData(PublicEncodingRules.CER, "04020780")] - [InlineData(PublicEncodingRules.DER, "04020780")] + [InlineData(AsnEncodingRules.BER, "04020780")] + [InlineData(AsnEncodingRules.BER, "040207FF")] + [InlineData(AsnEncodingRules.CER, "04020780")] + [InlineData(AsnEncodingRules.DER, "04020780")] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2480" + "2480" + "0000" + "04020000" + "0000")] - public static void TryCopyOctetStringBytes_Fails(PublicEncodingRules ruleSet, string inputHex) + public static void TryReadOctetStringBytes_Fails(AsnEncodingRules ruleSet, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryCopyOctetStringBytes( + bool didRead = reader.TryReadOctetString( Span.Empty, out int bytesWritten); - Assert.False(didRead, "reader.TryCopyOctetStringBytes"); + Assert.False(didRead, "reader.TryReadOctetString"); Assert.Equal(0, bytesWritten); } [Theory] - [InlineData(PublicEncodingRules.BER, "04020780", "0780")] - [InlineData(PublicEncodingRules.BER, "040207FF", "07FF")] - [InlineData(PublicEncodingRules.CER, "04020780", "0780")] - [InlineData(PublicEncodingRules.DER, "04020680", "0680")] - [InlineData(PublicEncodingRules.BER, "24800000", "")] - [InlineData(PublicEncodingRules.BER, "2400", "")] - [InlineData(PublicEncodingRules.BER, "2400" + "0500", "")] + [InlineData(AsnEncodingRules.BER, "04020780", "0780")] + [InlineData(AsnEncodingRules.BER, "040207FF", "07FF")] + [InlineData(AsnEncodingRules.CER, "04020780", "0780")] + [InlineData(AsnEncodingRules.DER, "04020680", "0680")] + [InlineData(AsnEncodingRules.BER, "24800000", "")] + [InlineData(AsnEncodingRules.BER, "2400", "")] + [InlineData(AsnEncodingRules.BER, "2400" + "0500", "")] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2480" + "2480" + "0000" + @@ -173,7 +177,7 @@ public static void TryCopyOctetStringBytes_Fails(PublicEncodingRules ruleSet, st "0000", "0005")] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2480" + "2406" + "0401FA" + @@ -192,74 +196,93 @@ public static void TryCopyOctetStringBytes_Fails(PublicEncodingRules ruleSet, st "0000" + "0000", "FACEF00D000100020303FF")] - public static void TryCopyOctetStringBytes_Success( - PublicEncodingRules ruleSet, + public static void TryReadOctetStringBytes_Success( + AsnEncodingRules ruleSet, string inputHex, string expectedHex) { byte[] inputData = inputHex.HexToByteArray(); byte[] output = new byte[expectedHex.Length / 2]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryCopyOctetStringBytes( + bool didRead = reader.TryReadOctetString( output, out int bytesWritten); - Assert.True(didRead, "reader.TryCopyOctetStringBytes"); + Assert.True(didRead, "reader.TryReadOctetString"); Assert.Equal(expectedHex, output.AsSpan(0, bytesWritten).ByteArrayToHex()); + + reader = new AsnReader(inputData, ruleSet); + byte[] output2 = reader.ReadOctetString(); + Assert.Equal(output, output2); } - private static void TryCopyOctetStringBytes_Throws_Helper( - PublicEncodingRules ruleSet, + private static void TryReadOctetStringBytes_Throws_Helper( + AsnEncodingRules ruleSet, byte[] input) { - Assert.Throws( + AsnReader reader = new AsnReader(input, ruleSet); + + Assert.Throws( () => { - AsnReader reader = new AsnReader(input, (AsnEncodingRules)ruleSet); - reader.TryCopyOctetStringBytes( + reader.TryReadOctetString( Span.Empty, out int bytesWritten); }); } + private static void ReadOctetStringBytes_Throws_Helper( + AsnEncodingRules ruleSet, + byte[] input) + { + AsnReader reader = new AsnReader(input, ruleSet); + + Assert.Throws( + () => + { + reader.ReadOctetString(); + }); + } + [Theory] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] - [InlineData("Bad Length", PublicEncodingRules.BER, "040200")] - [InlineData("Bad Length", PublicEncodingRules.CER, "040200")] - [InlineData("Bad Length", PublicEncodingRules.DER, "040200")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "2403040100")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "2404800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2480800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2480800400FACE0000")] - [InlineData("Nested boolean", PublicEncodingRules.BER, "2403010100")] - [InlineData("Nested boolean (indef)", PublicEncodingRules.BER, "24800101000000")] - [InlineData("Nested boolean (indef)", PublicEncodingRules.CER, "24800101000000")] - [InlineData("Nested constructed form", PublicEncodingRules.CER, "2480" + "2480" + "04010" + "000000000")] - [InlineData("No terminator", PublicEncodingRules.BER, "2480" + "04020000" + "")] - [InlineData("No terminator", PublicEncodingRules.CER, "2480" + "04020000" + "")] - [InlineData("No content", PublicEncodingRules.BER, "2480")] - [InlineData("No content", PublicEncodingRules.CER, "2480")] - [InlineData("No nested content", PublicEncodingRules.CER, "24800000")] - [InlineData("Nested value too long", PublicEncodingRules.BER, "2480040A00")] - [InlineData("Nested value too long - constructed", PublicEncodingRules.BER, "2480240A00")] - [InlineData("Nested value too long - simple", PublicEncodingRules.BER, "2403" + "04050000000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "248020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "248020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2480000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2480000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2480008100")] - [InlineData("Constructed Payload-TooShort", PublicEncodingRules.CER, "24800401000000")] - public static void TryCopyOctetStringBytes_Throws( + [InlineData("Wrong Tag", AsnEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.DER, "0500")] + [InlineData("Bad Length", AsnEncodingRules.BER, "040200")] + [InlineData("Bad Length", AsnEncodingRules.CER, "040200")] + [InlineData("Bad Length", AsnEncodingRules.DER, "040200")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "2403040100")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "2404800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "2480800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "2480800400FACE0000")] + [InlineData("Nested boolean", AsnEncodingRules.BER, "2403010100")] + [InlineData("Nested boolean (indef)", AsnEncodingRules.BER, "24800101000000")] + [InlineData("Nested boolean (indef)", AsnEncodingRules.CER, "24800101000000")] + [InlineData("Nested constructed form", AsnEncodingRules.CER, "2480" + "2480" + "04010" + "000000000")] + [InlineData("No terminator", AsnEncodingRules.BER, "2480" + "04020000" + "")] + [InlineData("No terminator", AsnEncodingRules.CER, "2480" + "04020000" + "")] + [InlineData("No content", AsnEncodingRules.BER, "2480")] + [InlineData("No content", AsnEncodingRules.CER, "2480")] + [InlineData("No nested content", AsnEncodingRules.CER, "24800000")] + [InlineData("Nested value too long", AsnEncodingRules.BER, "2480040A00")] + [InlineData("Nested value too long - constructed", AsnEncodingRules.BER, "2480240A00")] + [InlineData("Nested value too long - simple", AsnEncodingRules.BER, "2403" + "04050000000000")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "248020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "248020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "2480000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "2480000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "2480008100")] + [InlineData("Constructed Payload-TooShort", AsnEncodingRules.CER, "24800401000000")] + public static void TryReadOctetStringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - TryCopyOctetStringBytes_Throws_Helper(ruleSet, inputData); + TryReadOctetStringBytes_Throws_Helper(ruleSet, inputData); + ReadOctetStringBytes_Throws_Helper(ruleSet, inputData); } [Fact] @@ -288,7 +311,8 @@ public static void TryCopyOctetStringBytes_Throws_CER_NestedTooLong() input[5] = 0xE9; // EOC implicit since the byte[] initializes to zeros - TryCopyOctetStringBytes_Throws_Helper(PublicEncodingRules.CER, input); + TryReadOctetStringBytes_Throws_Helper(AsnEncodingRules.CER, input); + ReadOctetStringBytes_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -326,7 +350,8 @@ public static void TryCopyOctetStringBytes_Throws_CER_NestedTooShortIntermediate input[1011] = 0x02; // EOC implicit since the byte[] initializes to zeros - TryCopyOctetStringBytes_Throws_Helper(PublicEncodingRules.CER, input); + TryReadOctetStringBytes_Throws_Helper(AsnEncodingRules.CER, input); + ReadOctetStringBytes_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -354,16 +379,20 @@ public static void TryCopyOctetStringBytes_Success_CER_MaxPrimitiveLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryCopyOctetStringBytes( + bool success = reader.TryReadOctetString( output, out int bytesWritten); - Assert.True(success, "reader.TryCopyOctetStringBytes"); + Assert.True(success, "reader.TryReadOctetString"); Assert.Equal(1000, bytesWritten); Assert.Equal( input.AsSpan(4).ByteArrayToHex(), output.ByteArrayToHex()); + + reader = new AsnReader(input, AsnEncodingRules.CER); + byte[] output2 = reader.ReadOctetString(); + Assert.Equal(output, output2); } [Fact] @@ -421,110 +450,134 @@ public static void TryCopyOctetStringBytes_Success_CER_MinConstructedLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryCopyOctetStringBytes( + bool success = reader.TryReadOctetString( output, out int bytesWritten); - Assert.True(success, "reader.TryCopyOctetStringBytes"); + Assert.True(success, "reader.TryReadOctetString"); Assert.Equal(1001, bytesWritten); Assert.Equal( expected.ByteArrayToHex(), output.ByteArrayToHex()); + + reader = new AsnReader(input, AsnEncodingRules.CER); + byte[] output2 = reader.ReadOctetString(); + Assert.Equal(output, output2); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = { 4, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.TryReadPrimitiveOctetStringBytes(Asn1Tag.Null, out _)); + () => reader.TryReadPrimitiveOctetString(out _, Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); + Assert.Throws( + () => reader.TryReadPrimitiveOctetString(out _, new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); - Assert.True(reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory value)); + Assert.True(reader.TryReadPrimitiveOctetString(out ReadOnlyMemory value)); Assert.Equal("7E", value.ByteArrayToHex()); Assert.False(reader.HasData, "HasData after read"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = { 0x87, 2, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + byte[] output = new byte[inputData.Length]; + AsnReader reader = new AsnReader(inputData, ruleSet); + Asn1Tag wrongTag1 = new Asn1Tag(TagClass.Application, 0); + Asn1Tag wrongTag2 = new Asn1Tag(TagClass.ContextSpecific, 1); + Asn1Tag correctTag = new Asn1Tag(TagClass.ContextSpecific, 7); + + AssertExtensions.Throws( + "expectedTag", + () => reader.TryReadPrimitiveOctetString(out _, Asn1Tag.Null)); + AssertExtensions.Throws( + "expectedTag", + () => reader.TryReadOctetString(output, out _, Asn1Tag.Null)); AssertExtensions.Throws( "expectedTag", - () => reader.TryReadPrimitiveOctetStringBytes(Asn1Tag.Null, out _)); + () => reader.ReadOctetString(Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.TryReadPrimitiveOctetStringBytes(out _)); - + Assert.Throws(() => reader.TryReadPrimitiveOctetString(out _)); + Assert.Throws(() => reader.TryReadOctetString(output, out _)); + Assert.Throws(() => reader.ReadOctetString()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( - () => reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.Application, 0), out _)); - + Assert.Throws(() => reader.TryReadPrimitiveOctetString(out _, wrongTag1)); + Assert.Throws(() => reader.TryReadOctetString(output, out _, wrongTag1)); + Assert.Throws(() => reader.ReadOctetString(wrongTag1)); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( - () => reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); - + Assert.Throws(() => reader.TryReadPrimitiveOctetString(out _, wrongTag2)); + Assert.Throws(() => reader.TryReadOctetString(output, out _, wrongTag2)); + Assert.Throws(() => reader.ReadOctetString(wrongTag2)); Assert.True(reader.HasData, "HasData after wrong custom tag value"); - Assert.True( - reader.TryReadPrimitiveOctetStringBytes( - new Asn1Tag(TagClass.ContextSpecific, 7), - out ReadOnlyMemory value)); - + Assert.True(reader.TryReadPrimitiveOctetString(out ReadOnlyMemory value, correctTag)); Assert.Equal("0080", value.ByteArrayToHex()); Assert.False(reader.HasData, "HasData after reading value"); + + reader = new AsnReader(inputData, ruleSet); + + Assert.True(reader.TryReadOctetString(output.AsSpan(1), out int written, correctTag)); + Assert.Equal("0080", output.AsSpan(1, written).ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + + reader = new AsnReader(inputData, ruleSet); + + byte[] output2 = reader.ReadOctetString(correctTag); + Assert.Equal("0080", output2.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); } [Theory] - [InlineData(PublicEncodingRules.BER, "0401FF", PublicTagClass.Universal, 4)] - [InlineData(PublicEncodingRules.CER, "0401FF", PublicTagClass.Universal, 4)] - [InlineData(PublicEncodingRules.DER, "0401FF", PublicTagClass.Universal, 4)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "0401FF", TagClass.Universal, 4)] + [InlineData(AsnEncodingRules.CER, "0401FF", TagClass.Universal, 4)] + [InlineData(AsnEncodingRules.DER, "0401FF", TagClass.Universal, 4)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.True( - reader.TryReadPrimitiveOctetStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, true), - out ReadOnlyMemory val1)); + reader.TryReadPrimitiveOctetString( + out ReadOnlyMemory val1, + new Asn1Tag(tagClass, tagValue, true))); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.True( - reader.TryReadPrimitiveOctetStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, false), - out ReadOnlyMemory val2)); + reader.TryReadPrimitiveOctetString( + out ReadOnlyMemory val2, + new Asn1Tag(tagClass, tagValue, false))); Assert.False(reader.HasData); @@ -556,8 +609,14 @@ public static void TryCopyOctetStringBytes_ExtremelyNested() int bytesWritten; - Assert.True(reader.TryCopyOctetStringBytes(Span.Empty, out bytesWritten)); + Assert.True(reader.TryReadOctetString(Span.Empty, out bytesWritten)); Assert.Equal(0, bytesWritten); + + reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + byte[] output2 = reader.ReadOctetString(); + + // It's Same (ReferenceEqual) on .NET Core, but just Equal on .NET Framework + Assert.Equal(Array.Empty(), output2); } } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs new file mode 100644 index 00000000000000..ed43a254b522ed --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs @@ -0,0 +1,410 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadSequence + { + [Theory] + [InlineData(AsnEncodingRules.BER, "3000", false, -1)] + [InlineData(AsnEncodingRules.BER, "30800000", false, -1)] + [InlineData(AsnEncodingRules.BER, "3083000000", false, -1)] + [InlineData(AsnEncodingRules.CER, "30800000", false, -1)] + [InlineData(AsnEncodingRules.DER, "3000", false, -1)] + [InlineData(AsnEncodingRules.BER, "3000" + "0500", true, -1)] + [InlineData(AsnEncodingRules.BER, "3002" + "0500", false, 5)] + [InlineData(AsnEncodingRules.CER, "3080" + "0500" + "0000", false, 5)] + [InlineData(AsnEncodingRules.CER, "3080" + "010100" + "0000" + "0500", true, 1)] + [InlineData(AsnEncodingRules.DER, "3005" + "0500" + "0101FF", false, 5)] + public static void ReadSequence_Success( + AsnEncodingRules ruleSet, + string inputHex, + bool expectDataRemaining, + int expectedSequenceTagNumber) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader sequence = reader.ReadSequence(); + + if (expectDataRemaining) + { + Assert.True(reader.HasData, "reader.HasData"); + } + else + { + Assert.False(reader.HasData, "reader.HasData"); + } + + if (expectedSequenceTagNumber < 0) + { + Assert.False(sequence.HasData, "sequence.HasData"); + } + else + { + Assert.True(sequence.HasData, "sequence.HasData"); + + Asn1Tag firstTag = sequence.PeekTag(); + Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); + } + } + + [Theory] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "30")] + [InlineData("Missing Length", AsnEncodingRules.CER, "30")] + [InlineData("Missing Length", AsnEncodingRules.DER, "30")] + [InlineData("Primitive Encoding", AsnEncodingRules.BER, "1000")] + [InlineData("Primitive Encoding", AsnEncodingRules.CER, "1000")] + [InlineData("Primitive Encoding", AsnEncodingRules.DER, "1000")] + [InlineData("Definite Length Encoding", AsnEncodingRules.CER, "3000")] + [InlineData("Indefinite Length Encoding", AsnEncodingRules.DER, "3080" + "0000")] + [InlineData("Missing Content", AsnEncodingRules.BER, "3001")] + [InlineData("Missing Content", AsnEncodingRules.DER, "3001")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.BER, "3005" + "010100")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.DER, "3005" + "010100")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.BER, "3080")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.CER, "3080")] + [InlineData("Missing EoC", AsnEncodingRules.BER, "3080" + "010100")] + [InlineData("Missing EoC", AsnEncodingRules.CER, "3080" + "010100")] + [InlineData("Missing Outer EoC", AsnEncodingRules.BER, "3080" + "010100" + ("3080" + "0000"))] + [InlineData("Missing Outer EoC", AsnEncodingRules.CER, "3080" + "010100" + ("3080" + "0000"))] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.BER, "3100")] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.DER, "3100")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.BER, "3180" + "0000")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.CER, "3180" + "0000")] + public static void ReadSequence_Throws( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadSequence()); + } + + private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) + { + AsnReader mainReader = new AsnReader(inputData, ruleSet); + + AsnReader spkiReader = mainReader.ReadSequence(); + Assert.False(mainReader.HasData, "mainReader.HasData after reading SPKI"); + + AsnReader algorithmReader = spkiReader.ReadSequence(); + Assert.True(spkiReader.HasData, "spkiReader.HasData after reading algorithm"); + + ReadOnlyMemory publicKeyValue; + int unusedBitCount; + + if (!spkiReader.TryReadPrimitiveBitString(out unusedBitCount, out publicKeyValue)) + { + // The correct answer is 65 bytes. + for (int i = 10; ; i *= 2) + { + byte[] buf = new byte[i]; + + if (spkiReader.TryReadBitString(buf, out unusedBitCount, out int bytesWritten)) + { + publicKeyValue = new ReadOnlyMemory(buf, 0, bytesWritten); + break; + } + } + } + + Assert.False(spkiReader.HasData, "spkiReader.HasData after reading subjectPublicKey"); + Assert.True(algorithmReader.HasData, "algorithmReader.HasData before reading"); + + string algorithmOid = algorithmReader.ReadObjectIdentifier(); + Assert.True(algorithmReader.HasData, "algorithmReader.HasData after reading first OID"); + + Assert.Equal("1.2.840.10045.2.1", algorithmOid); + + string curveOid = algorithmReader.ReadObjectIdentifier(); + Assert.False(algorithmReader.HasData, "algorithmReader.HasData after reading second OID"); + + Assert.Equal("1.2.840.10045.3.1.7", curveOid); + + const string PublicKeyValue = + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + Assert.Equal(PublicKeyValue, publicKeyValue.ByteArrayToHex()); + Assert.Equal(0, unusedBitCount); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEcPublicKey_DefiniteLength(AsnEncodingRules ruleSet) + { + const string InputHex = + "3059" + + "3013" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0342" + + "00" + + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + byte[] inputData = InputHex.HexToByteArray(); + ReadEcPublicKey(ruleSet, inputData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void ReadEcPublicKey_IndefiniteLength(AsnEncodingRules ruleSet) + { + const string InputHex = + "3080" + + "3080" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0000" + + "0342" + + "00" + + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13" + + "0000"; + + byte[] inputData = InputHex.HexToByteArray(); + ReadEcPublicKey(ruleSet, inputData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "30020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSequence(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "308005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSequence(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A5020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSequence()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A58005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSequence()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "30030101FF", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.BER, "30800101000000", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.CER, "30800101000000", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.DER, "30030101FF", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.BER, "A0030101FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.BER, "A1800101000000", TagClass.ContextSpecific, 1)] + [InlineData(AsnEncodingRules.CER, "6C800101000000", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "FF8A46030101FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AsnReader val1 = reader.ReadSequence(new Asn1Tag(tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + AsnReader val2 = reader.ReadSequence(new Asn1Tag(tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadSequenceOf_PreservesOptions(AsnEncodingRules ruleSet) + { + // [5] (UtcTime) 500102123456Z + // UtcTime 120102235959Z + // + // They're sorted backwards, though. + const string PayloadHex = + "850D3530303130323132333435365A" + + "170D3132303130323233353935395A"; + + byte[] inputData; + + // Build the rule-specific form of SEQUENCE { [PRIVATE 9] SEQUENCE { SET-OF { dates }, NULL } } + // The outer Set-Of is also invalid, because the NULL should be first. + if (ruleSet == AsnEncodingRules.DER) + { + inputData = ("3024" + "E922" + "A21E" + PayloadHex + "0500").HexToByteArray(); + } + else + { + string inputHex = "3080" + "E980" + "A280" + PayloadHex + "0000" + "0500" + "0000" + "0000"; + inputData = inputHex.HexToByteArray(); + } + + AsnReaderOptions options = new AsnReaderOptions + { + SkipSetSortOrderVerification = true, + UtcTimeTwoDigitYearMax = 2011, + }; + + AsnReader initial = new AsnReader(inputData, ruleSet, options); + AsnReader outer = initial.ReadSequence(); + Assert.False(initial.HasData); + AsnReader inner = outer.ReadSequence(new Asn1Tag(TagClass.Private, 9)); + Assert.False(outer.HasData); + + Asn1Tag setTag = new Asn1Tag(TagClass.ContextSpecific, 2); + + if (ruleSet != AsnEncodingRules.BER) + { + Assert.Throws(() => inner.ReadSetOf(false, setTag)); + } + + // This confirms that we've passed SkipSetOrderVerification this far. + AsnReader setOf = inner.ReadSetOf(setTag); + Assert.True(inner.HasData); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + setOf.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); + + // This confirms that we've passed UtcTimeTwoDigitYearMax, + // the default would call this 2012. + Assert.Equal( + new DateTimeOffset(1912, 1, 2, 23, 59, 59, TimeSpan.Zero), + setOf.ReadUtcTime()); + + Assert.False(setOf.HasData); + + inner.ReadNull(); + Assert.False(inner.HasData); + + setOf.ThrowIfNotEmpty(); + inner.ThrowIfNotEmpty(); + outer.ThrowIfNotEmpty(); + initial.ThrowIfNotEmpty(); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs new file mode 100644 index 00000000000000..2dfb2af5354f78 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs @@ -0,0 +1,395 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadSetOf + { + [Theory] + [InlineData(AsnEncodingRules.BER, "3100", false, -1)] + [InlineData(AsnEncodingRules.BER, "31800000", false, -1)] + [InlineData(AsnEncodingRules.BER, "3183000000", false, -1)] + [InlineData(AsnEncodingRules.CER, "31800000", false, -1)] + [InlineData(AsnEncodingRules.DER, "3100", false, -1)] + [InlineData(AsnEncodingRules.BER, "3100" + "0500", true, -1)] + [InlineData(AsnEncodingRules.BER, "3102" + "0500", false, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "0500" + "0000", false, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0000" + "0500", true, 1)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", false, 1)] + [InlineData(AsnEncodingRules.DER, "3105" + "0101FF" + "0500", false, 1)] + public static void ReadSetOf_Success( + AsnEncodingRules ruleSet, + string inputHex, + bool expectDataRemaining, + int expectedSequenceTagNumber) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader sequence = reader.ReadSetOf(); + + if (expectDataRemaining) + { + Assert.True(reader.HasData, "reader.HasData"); + } + else + { + Assert.False(reader.HasData, "reader.HasData"); + } + + if (expectedSequenceTagNumber < 0) + { + Assert.False(sequence.HasData, "sequence.HasData"); + } + else + { + Assert.True(sequence.HasData, "sequence.HasData"); + + Asn1Tag firstTag = sequence.PeekTag(); + Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); + } + } + + [Theory] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "31")] + [InlineData("Missing Length", AsnEncodingRules.CER, "31")] + [InlineData("Missing Length", AsnEncodingRules.DER, "31")] + [InlineData("Primitive Encoding", AsnEncodingRules.BER, "1100")] + [InlineData("Primitive Encoding", AsnEncodingRules.CER, "1100")] + [InlineData("Primitive Encoding", AsnEncodingRules.DER, "1100")] + [InlineData("Definite Length Encoding", AsnEncodingRules.CER, "3100")] + [InlineData("Indefinite Length Encoding", AsnEncodingRules.DER, "3180" + "0000")] + [InlineData("Missing Content", AsnEncodingRules.BER, "3101")] + [InlineData("Missing Content", AsnEncodingRules.DER, "3101")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.BER, "3105" + "010100")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.DER, "3105" + "010100")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.BER, "3180")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.CER, "3180")] + [InlineData("Missing EoC", AsnEncodingRules.BER, "3180" + "010100")] + [InlineData("Missing EoC", AsnEncodingRules.CER, "3180" + "010100")] + [InlineData("Missing Outer EoC", AsnEncodingRules.BER, "3180" + "010100" + ("3180" + "0000"))] + [InlineData("Missing Outer EoC", AsnEncodingRules.CER, "3180" + "010100" + ("3180" + "0000"))] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.BER, "3000")] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.DER, "3000")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.BER, "3080" + "0000")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.CER, "3080" + "0000")] + public static void ReadSetOf_Throws( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadSetOf()); + } + + [Theory] + // BER can read out of order (indefinite) + [InlineData(AsnEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] + // BER can read out of order (definite) + [InlineData(AsnEncodingRules.BER, "3106" + "0101FF" + "010100", true, 1)] + // CER will not read out of order + [InlineData(AsnEncodingRules.CER, "3180" + "0500" + "010100" + "0000", false, 1)] + [InlineData(AsnEncodingRules.CER, "3180" + "0101FF" + "010100" + "0000", false, 1)] + // CER is happy in order: + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0500" + "0000", true, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", true, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "010100" + "0500" + "0000", true, 5)] + // DER will not read out of order + [InlineData(AsnEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] + [InlineData(AsnEncodingRules.DER, "3105" + "0500" + "010100", false, 1)] + // DER is happy in order: + [InlineData(AsnEncodingRules.DER, "3105" + "010100" + "0500", true, 5)] + [InlineData(AsnEncodingRules.DER, "3108" + "010100" + "0101FF" + "0500", true, 5)] + [InlineData(AsnEncodingRules.DER, "3108" + "010100" + "010100" + "0500", true, 5)] + public static void ReadSetOf_DataSorting( + AsnEncodingRules ruleSet, + string inputHex, + bool expectSuccess, + int lastTagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader setOf; + + AsnReader laxReader = new AsnReader( + inputData, + ruleSet, + new AsnReaderOptions { SkipSetSortOrderVerification = true }); + + if (expectSuccess) + { + setOf = reader.ReadSetOf(); + } + else + { + AsnReader alsoReader = new AsnReader(inputData, ruleSet); + Assert.Throws(() => alsoReader.ReadSetOf()); + Assert.Throws(() => laxReader.ReadSetOf(false)); + + setOf = reader.ReadSetOf(skipSortOrderValidation: true); + } + + int lastTag = -1; + + while (setOf.HasData) + { + Asn1Tag tag = setOf.PeekTag(); + lastTag = tag.TagValue; + + // Ignore the return, just drain it. + setOf.ReadEncodedValue(); + } + + Assert.Equal(lastTagValue, lastTag); + + setOf = laxReader.ReadSetOf(); + lastTag = -1; + + while (setOf.HasData) + { + Asn1Tag tag = setOf.PeekTag(); + lastTag = tag.TagValue; + + // Ignore the return, just drain it. + setOf.ReadEncodedValue(); + } + + Assert.Equal(lastTagValue, lastTag); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "31020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSetOf(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "318005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSetOf(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A5020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSetOf()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A58005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSetOf()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "31030101FF", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.BER, "31800101000000", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.CER, "31800101000000", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.DER, "31030101FF", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.BER, "A0030101FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.BER, "A1800101000000", TagClass.ContextSpecific, 1)] + [InlineData(AsnEncodingRules.CER, "6C800101000000", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "FF8A46030101FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AsnReader val1 = reader.ReadSetOf(expectedTag: new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + AsnReader val2 = reader.ReadSetOf(expectedTag: new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadSetOf_PreservesOptions(AsnEncodingRules ruleSet) + { + // [5] (UtcTime) 500102123456Z + // UtcTime 120102235959Z + // + // They're sorted backwards, though. + const string PayloadHex = + "850D3530303130323132333435365A" + + "170D3132303130323233353935395A"; + + byte[] inputData; + + // Build the rule-specific form of SET-OF { SET-OF { dates }, NULL } + // The outer Set-Of is also invalid, because the NULL should be first. + if (ruleSet == AsnEncodingRules.DER) + { + inputData = ("3122" + "A21E" + PayloadHex + "0500").HexToByteArray(); + } + else + { + inputData = ("3180" + "A280" + PayloadHex + "0000" + "0500" + "0000").HexToByteArray(); + } + + AsnReaderOptions options = new AsnReaderOptions + { + SkipSetSortOrderVerification = true, + UtcTimeTwoDigitYearMax = 2011, + }; + + AsnReader initial = new AsnReader(inputData, ruleSet, options); + + if (ruleSet != AsnEncodingRules.BER) + { + Assert.Throws(() => initial.ReadSetOf(false)); + } + + AsnReader outer = initial.ReadSetOf(); + Assert.False(initial.HasData); + + Asn1Tag innerTag = new Asn1Tag(TagClass.ContextSpecific, 2); + + if (ruleSet != AsnEncodingRules.BER) + { + Assert.Throws(() => outer.ReadSetOf(false, innerTag)); + } + + // This confirms that we've passed SkipSetOrderVerification this far. + AsnReader inner = outer.ReadSetOf(innerTag); + Assert.True(outer.HasData); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + inner.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); + + // This confirms that we've passed UtcTimeTwoDigitYearMax, + // the default would call this 2012. + Assert.Equal( + new DateTimeOffset(1912, 1, 2, 23, 59, 59, TimeSpan.Zero), + inner.ReadUtcTime()); + + Assert.False(inner.HasData); + + outer.ReadNull(); + Assert.False(outer.HasData); + + inner.ThrowIfNotEmpty(); + outer.ThrowIfNotEmpty(); + initial.ThrowIfNotEmpty(); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs similarity index 79% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs index 4c6cff568ff2e6..056785a99654c0 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs @@ -3,15 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadT61String : Asn1ReaderTests + public sealed class ReadT61String { public static IEnumerable ValidEncodingData { get; } = new object[][] @@ -19,47 +16,47 @@ public sealed class ReadT61String : Asn1ReaderTests // https://github.com/dotnet/runtime/issues/25195 new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "140E47726170654369747920696E632E", "GrapeCity inc.", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1411546F6F6C7320446576656C6F706D656E74", "Tools Development", }, // Mono test case taken from old bug report new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "14244865646562792773204DF862656C68616E64656C202F2F204356523A3133343731393637", "Hedeby's M\u00f8belhandel // CVR:13471967", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "14264865646562792773204DF862656C68616E64656C202D2053616C6773616664656C696E67656E", "Hedeby's M\u00f8belhandel - Salgsafdelingen", }, // Valid UTF-8 string is interpreted as UTF-8 new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1402C2A2", "\u00a2", }, // Valid UTF-8 string is interpreted as UTF-8 (multi-segment) new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "34800401C20401A20000", "\u00a2", }, // Invalid UTF-8 string with valid UTF-8 sequence is interpreted as ISO 8859-1 new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1403C2A2F8", "\u00c2\u00a2\u00f8", }, @@ -68,12 +65,12 @@ public sealed class ReadT61String : Asn1ReaderTests [Theory] [MemberData(nameof(ValidEncodingData))] public static void GetT61String_Success( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); string value = reader.ReadCharacterString(UniversalTagNumber.T61String); Assert.Equal(expectedValue, value); @@ -82,14 +79,14 @@ public static void GetT61String_Success( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyT61String( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); char[] output = new char[expectedValue.Length]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int charsWritten; @@ -97,9 +94,9 @@ public static void TryCopyT61String( { output[0] = 'a'; - copied = reader.TryCopyCharacterString( - UniversalTagNumber.T61String, + copied = reader.TryReadCharacterString( output.AsSpan(0, expectedValue.Length - 1), + UniversalTagNumber.T61String, out charsWritten); Assert.False(copied, "reader.TryCopyT61String - too short"); @@ -107,9 +104,9 @@ public static void TryCopyT61String( Assert.Equal('a', output[0]); } - copied = reader.TryCopyCharacterString( - UniversalTagNumber.T61String, + copied = reader.TryReadCharacterString( output, + UniversalTagNumber.T61String, out charsWritten); Assert.True(copied, "reader.TryCopyT61String"); diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs similarity index 55% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs index 4b9464e3e59490..c09de11b916252 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs @@ -5,80 +5,79 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadUTF8String : Asn1ReaderTests + public sealed class ReadUTF8String { public static IEnumerable ValidEncodingData { get; } = new object[][] { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "0C0D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "0C0D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "0C0D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C80" + "040D4A6F686E20512E20536D697468" + "0000", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C0F" + "040D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "0C00", "", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "0C00", "", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "0C00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C80" + "0000", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C80" + "2480" + // "Dr." @@ -123,12 +122,12 @@ public sealed class ReadUTF8String : Asn1ReaderTests [Theory] [MemberData(nameof(ValidEncodingData))] public static void GetUTF8String_Success( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); string value = reader.ReadCharacterString(UniversalTagNumber.UTF8String); Assert.Equal(expectedValue, value); @@ -137,14 +136,14 @@ public static void GetUTF8String_Success( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyUTF8String( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); char[] output = new char[expectedValue.Length]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int charsWritten; @@ -152,9 +151,9 @@ public static void TryCopyUTF8String( { output[0] = 'a'; - copied = reader.TryCopyCharacterString( - UniversalTagNumber.UTF8String, + copied = reader.TryReadCharacterString( output.AsSpan(0, expectedValue.Length - 1), + UniversalTagNumber.UTF8String, out charsWritten); Assert.False(copied, "reader.TryCopyUTF8String - too short"); @@ -162,9 +161,9 @@ public static void TryCopyUTF8String( Assert.Equal('a', output[0]); } - copied = reader.TryCopyCharacterString( - UniversalTagNumber.UTF8String, + copied = reader.TryReadCharacterString( output, + UniversalTagNumber.UTF8String, out charsWritten); Assert.True(copied, "reader.TryCopyUTF8String"); @@ -176,7 +175,7 @@ public static void TryCopyUTF8String( [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyUTF8StringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedString) { @@ -184,7 +183,7 @@ public static void TryCopyUTF8StringBytes( string expectedHex = Text.Encoding.UTF8.GetBytes(expectedString).ByteArrayToHex(); byte[] output = new byte[expectedHex.Length / 2]; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool copied; int bytesWritten; @@ -192,9 +191,9 @@ public static void TryCopyUTF8StringBytes( { output[0] = 32; - copied = reader.TryCopyCharacterStringBytes( - UniversalTagNumber.UTF8String, + copied = reader.TryReadCharacterStringBytes( output.AsSpan(0, output.Length - 1), + new Asn1Tag(UniversalTagNumber.UTF8String), out bytesWritten); Assert.False(copied, "reader.TryCopyUTF8StringBytes - too short"); @@ -202,9 +201,9 @@ public static void TryCopyUTF8StringBytes( Assert.Equal(32, output[0]); } - copied = reader.TryCopyCharacterStringBytes( - UniversalTagNumber.UTF8String, + copied = reader.TryReadCharacterStringBytes( output, + new Asn1Tag(UniversalTagNumber.UTF8String), out bytesWritten); Assert.True(copied, "reader.TryCopyUTF8StringBytes"); @@ -217,19 +216,19 @@ public static void TryCopyUTF8StringBytes( } [Theory] - [InlineData(PublicEncodingRules.BER, "0C0120", true)] - [InlineData(PublicEncodingRules.BER, "2C80" + "040120" + "0000", false)] - [InlineData(PublicEncodingRules.BER, "2C03" + "040120", false)] + [InlineData(AsnEncodingRules.BER, "0C0120", true)] + [InlineData(AsnEncodingRules.BER, "2C80" + "040120" + "0000", false)] + [InlineData(AsnEncodingRules.BER, "2C03" + "040120", false)] public static void TryGetUTF8StringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, bool expectSuccess) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); bool got = reader.TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber.UTF8String, + new Asn1Tag(UniversalTagNumber.UTF8String), out ReadOnlyMemory contents); if (expectSuccess) @@ -249,78 +248,78 @@ ref MemoryMarshal.GetReference(contents.Span), } [Theory] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "0C")] - [InlineData("Missing Length", PublicEncodingRules.CER, "0C")] - [InlineData("Missing Length", PublicEncodingRules.DER, "0C")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "0C01")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "0C01")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "0C01")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "0C034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "0C034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "0C034869")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "2C03040149")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "0C")] + [InlineData("Missing Length", AsnEncodingRules.CER, "0C")] + [InlineData("Missing Length", AsnEncodingRules.DER, "0C")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "0C01")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "0C01")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "0C01")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "0C034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "0C034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "0C034869")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "2C03040149")] public static void TryGetUTF8StringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( - () => reader.TryReadPrimitiveCharacterStringBytes(UniversalTagNumber.UTF8String, out _)); + Assert.Throws( + () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(UniversalTagNumber.UTF8String), out _)); } [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "0C")] - [InlineData("Missing Length", PublicEncodingRules.CER, "0C")] - [InlineData("Missing Length", PublicEncodingRules.DER, "0C")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "0C01")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "0C01")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "0C01")] - [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "2C01")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "2C80")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "2C80")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "0C034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "0C034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "0C034869")] - [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "2C03040149")] - [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "2C03040149")] - [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "2C800401490000")] - [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "2C800401490000")] - [InlineData("No nested content", PublicEncodingRules.CER, "2C800000")] - [InlineData("No EoC", PublicEncodingRules.BER, "2C80" + "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] - [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "2C04" + "0C024869")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "2C04800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2C80800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2C80800400FACE0000")] - [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "2C07" + ("2402" + "0403") + "040149")] - [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "2C03" + "040548656C6C6F")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "2C8020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "2C8020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2C80000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2C80000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2C80008100")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "0C")] + [InlineData("Missing Length", AsnEncodingRules.CER, "0C")] + [InlineData("Missing Length", AsnEncodingRules.DER, "0C")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "0C01")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "0C01")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "0C01")] + [InlineData("Missing Contents - Constructed", AsnEncodingRules.BER, "2C01")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.BER, "2C80")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.CER, "2C80")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "0C034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "0C034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "0C034869")] + [InlineData("Definite Constructed Form", AsnEncodingRules.CER, "2C03040149")] + [InlineData("Definite Constructed Form", AsnEncodingRules.DER, "2C03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", AsnEncodingRules.CER, "2C800401490000")] + [InlineData("Indefinite Constructed Form", AsnEncodingRules.DER, "2C800401490000")] + [InlineData("No nested content", AsnEncodingRules.CER, "2C800000")] + [InlineData("No EoC", AsnEncodingRules.BER, "2C80" + "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", AsnEncodingRules.BER, "2C04" + "0C024869")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "2C04800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "2C80800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "2C80800400FACE0000")] + [InlineData("Nested Length Too Long", AsnEncodingRules.BER, "2C07" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", AsnEncodingRules.BER, "2C03" + "040548656C6C6F")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "2C8020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "2C8020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "2C80000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "2C80000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "2C80008100")] public static void TryCopyUTF8StringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; @@ -329,95 +328,98 @@ public static void TryCopyUTF8StringBytes_Throws( outputData[0] = 252; int bytesWritten = -1; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( - () => reader.TryCopyCharacterStringBytes(UniversalTagNumber.UTF8String, outputData, out bytesWritten)); + Assert.Throws( + () => reader.TryReadCharacterStringBytes( + outputData, + new Asn1Tag(UniversalTagNumber.UTF8String), + out bytesWritten)); Assert.Equal(-1, bytesWritten); Assert.Equal(252, outputData[0]); } - private static void TryCopyUTF8String_Throws_Helper(PublicEncodingRules ruleSet, byte[] inputData) + private static void TryCopyUTF8String_Throws_Helper(AsnEncodingRules ruleSet, byte[] inputData) { char[] outputData = new char[inputData.Length + 1]; outputData[0] = 'a'; int bytesWritten = -1; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( - () => reader.TryCopyCharacterString(UniversalTagNumber.UTF8String, outputData, out bytesWritten)); + Assert.Throws( + () => reader.TryReadCharacterString(outputData, UniversalTagNumber.UTF8String, out bytesWritten)); Assert.Equal(-1, bytesWritten); Assert.Equal('a', outputData[0]); } [Theory] - [InlineData("Bad UTF8 value", PublicEncodingRules.BER, "0C02E280")] - [InlineData("Bad UTF8 value", PublicEncodingRules.CER, "0C02E280")] - [InlineData("Bad UTF8 value", PublicEncodingRules.DER, "0C02E280")] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "04024869")] + [InlineData("Bad UTF8 value", AsnEncodingRules.BER, "0C02E280")] + [InlineData("Bad UTF8 value", AsnEncodingRules.CER, "0C02E280")] + [InlineData("Bad UTF8 value", AsnEncodingRules.DER, "0C02E280")] + [InlineData("Wrong Tag", AsnEncodingRules.BER, "04024869")] public static void GetUTF8String_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( + Assert.Throws( () => reader.ReadCharacterString(UniversalTagNumber.UTF8String)); } [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "0C")] - [InlineData("Missing Length", PublicEncodingRules.CER, "0C")] - [InlineData("Missing Length", PublicEncodingRules.DER, "0C")] - [InlineData("Missing Contents", PublicEncodingRules.BER, "0C01")] - [InlineData("Missing Contents", PublicEncodingRules.CER, "0C01")] - [InlineData("Missing Contents", PublicEncodingRules.DER, "0C01")] - [InlineData("Missing Contents - Constructed", PublicEncodingRules.BER, "2C01")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.BER, "2C80")] - [InlineData("Missing Contents - Constructed Indef", PublicEncodingRules.CER, "2C80")] - [InlineData("Length Too Long", PublicEncodingRules.BER, "0C034869")] - [InlineData("Length Too Long", PublicEncodingRules.CER, "0C034869")] - [InlineData("Length Too Long", PublicEncodingRules.DER, "0C034869")] - [InlineData("Definite Constructed Form", PublicEncodingRules.CER, "2C03040149")] - [InlineData("Definite Constructed Form", PublicEncodingRules.DER, "2C03040149")] - [InlineData("Indefinite Constructed Form - Short Payload", PublicEncodingRules.CER, "2C800401490000")] - [InlineData("Indefinite Constructed Form", PublicEncodingRules.DER, "2C800401490000")] - [InlineData("No nested content", PublicEncodingRules.CER, "2C800000")] - [InlineData("No EoC", PublicEncodingRules.BER, "2C80" + "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.BER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.CER, "04024869")] - [InlineData("Wrong Tag - Primitive", PublicEncodingRules.DER, "04024869")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.BER, "240404024869")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.BER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed Indef", PublicEncodingRules.CER, "2480" + "04024869" + "0000")] - [InlineData("Wrong Tag - Constructed", PublicEncodingRules.DER, "240404024869")] - [InlineData("Nested Bad Tag", PublicEncodingRules.BER, "2C04" + "0C024869")] - [InlineData("Nested context-specific", PublicEncodingRules.BER, "2C04800400FACE")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.BER, "2C80800400FACE0000")] - [InlineData("Nested context-specific (indef)", PublicEncodingRules.CER, "2C80800400FACE0000")] - [InlineData("Nested Length Too Long", PublicEncodingRules.BER, "2C07" + ("2402" + "0403") + "040149")] - [InlineData("Nested Simple Length Too Long", PublicEncodingRules.BER, "2C03" + "040548656C6C6F")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.BER, "2C8020000000")] - [InlineData("Constructed EndOfContents", PublicEncodingRules.CER, "2C8020000000")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.BER, "2C80000100")] - [InlineData("NonEmpty EndOfContents", PublicEncodingRules.CER, "2C80000100")] - [InlineData("LongLength EndOfContents", PublicEncodingRules.BER, "2C80008100")] - [InlineData("Bad UTF8 value", PublicEncodingRules.BER, "0C02E280")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "0C")] + [InlineData("Missing Length", AsnEncodingRules.CER, "0C")] + [InlineData("Missing Length", AsnEncodingRules.DER, "0C")] + [InlineData("Missing Contents", AsnEncodingRules.BER, "0C01")] + [InlineData("Missing Contents", AsnEncodingRules.CER, "0C01")] + [InlineData("Missing Contents", AsnEncodingRules.DER, "0C01")] + [InlineData("Missing Contents - Constructed", AsnEncodingRules.BER, "2C01")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.BER, "2C80")] + [InlineData("Missing Contents - Constructed Indef", AsnEncodingRules.CER, "2C80")] + [InlineData("Length Too Long", AsnEncodingRules.BER, "0C034869")] + [InlineData("Length Too Long", AsnEncodingRules.CER, "0C034869")] + [InlineData("Length Too Long", AsnEncodingRules.DER, "0C034869")] + [InlineData("Definite Constructed Form", AsnEncodingRules.CER, "2C03040149")] + [InlineData("Definite Constructed Form", AsnEncodingRules.DER, "2C03040149")] + [InlineData("Indefinite Constructed Form - Short Payload", AsnEncodingRules.CER, "2C800401490000")] + [InlineData("Indefinite Constructed Form", AsnEncodingRules.DER, "2C800401490000")] + [InlineData("No nested content", AsnEncodingRules.CER, "2C800000")] + [InlineData("No EoC", AsnEncodingRules.BER, "2C80" + "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.BER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.CER, "04024869")] + [InlineData("Wrong Tag - Primitive", AsnEncodingRules.DER, "04024869")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.BER, "240404024869")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.BER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed Indef", AsnEncodingRules.CER, "2480" + "04024869" + "0000")] + [InlineData("Wrong Tag - Constructed", AsnEncodingRules.DER, "240404024869")] + [InlineData("Nested Bad Tag", AsnEncodingRules.BER, "2C04" + "0C024869")] + [InlineData("Nested context-specific", AsnEncodingRules.BER, "2C04800400FACE")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.BER, "2C80800400FACE0000")] + [InlineData("Nested context-specific (indef)", AsnEncodingRules.CER, "2C80800400FACE0000")] + [InlineData("Nested Length Too Long", AsnEncodingRules.BER, "2C07" + ("2402" + "0403") + "040149")] + [InlineData("Nested Simple Length Too Long", AsnEncodingRules.BER, "2C03" + "040548656C6C6F")] + [InlineData("Constructed Null", AsnEncodingRules.BER, "2C8020000000")] + [InlineData("Constructed Null", AsnEncodingRules.CER, "2C8020000000")] + [InlineData("NonEmpty Null", AsnEncodingRules.BER, "2C80000100")] + [InlineData("NonEmpty Null", AsnEncodingRules.CER, "2C80000100")] + [InlineData("LongLength Null", AsnEncodingRules.BER, "2C80008100")] + [InlineData("Bad UTF8 value", AsnEncodingRules.BER, "0C02E280")] public static void TryCopyUTF8String_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; @@ -451,7 +453,7 @@ public static void TryCopyUTF8String_Throws_CER_NestedTooLong() input[5] = 0xE9; // EOC implicit since the byte[] initializes to zeros - TryCopyUTF8String_Throws_Helper(PublicEncodingRules.CER, input); + TryCopyUTF8String_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -489,7 +491,7 @@ public static void TryCopyUTF8String_Throws_CER_NestedTooShortIntermediate() input[1011] = 0x02; // EOC implicit since the byte[] initializes to zeros - TryCopyUTF8String_Throws_Helper(PublicEncodingRules.CER, input); + TryCopyUTF8String_Throws_Helper(AsnEncodingRules.CER, input); } [Fact] @@ -517,9 +519,9 @@ public static void TryCopyUTF8StringBytes_Success_CER_MaxPrimitiveLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryCopyCharacterStringBytes( - UniversalTagNumber.UTF8String, + bool success = reader.TryReadCharacterStringBytes( output, + new Asn1Tag(UniversalTagNumber.UTF8String), out int bytesWritten); Assert.True(success, "reader.TryCopyUTF8StringBytes"); @@ -585,9 +587,9 @@ public static void TryCopyUTF8StringBytes_Success_CER_MinConstructedLength() AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); - bool success = reader.TryCopyCharacterStringBytes( - UniversalTagNumber.UTF8String, + bool success = reader.TryReadCharacterStringBytes( output, + new Asn1Tag(UniversalTagNumber.UTF8String), out int bytesWritten); Assert.True(success, "reader.TryCopyUTF8StringBytes"); @@ -599,67 +601,66 @@ public static void TryCopyUTF8StringBytes_Success_CER_MinConstructedLength() } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) { byte[] inputData = { 0x0C, 2, (byte)'e', (byte)'l' }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); const UniversalTagNumber EncodingType = UniversalTagNumber.UTF8String; AssertExtensions.Throws( "expectedTag", - () => reader.TryReadPrimitiveCharacterStringBytes(Asn1Tag.Null, EncodingType, out _)); + () => reader.TryReadPrimitiveCharacterStringBytes(Asn1Tag.Null, out _)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), EncodingType, out _)); + Assert.Throws( + () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out _)); Assert.True(reader.HasData, "HasData after wrong tag"); - Assert.True(reader.TryReadPrimitiveCharacterStringBytes(EncodingType, out ReadOnlyMemory value)); + Assert.True(reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(EncodingType), out ReadOnlyMemory value)); Assert.Equal("656C", value.ByteArrayToHex()); Assert.False(reader.HasData, "HasData after read"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) { byte[] inputData = { 0x87, 2, (byte)'h', (byte)'i' }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); const UniversalTagNumber EncodingType = UniversalTagNumber.UTF8String; AssertExtensions.Throws( "expectedTag", - () => reader.TryReadPrimitiveCharacterStringBytes(Asn1Tag.Null, EncodingType, out _)); + () => reader.TryReadPrimitiveCharacterStringBytes(Asn1Tag.Null, out _)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.TryReadPrimitiveCharacterStringBytes(EncodingType, out _)); + Assert.Throws( + () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(EncodingType), out _)); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( - () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.Application, 0), EncodingType, out _)); + Assert.Throws( + () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.Application, 0), out _)); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( - () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), EncodingType, out _)); + Assert.Throws( + () => reader.TryReadPrimitiveCharacterStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out _)); Assert.True(reader.HasData, "HasData after wrong custom tag value"); Assert.True( reader.TryReadPrimitiveCharacterStringBytes( new Asn1Tag(TagClass.ContextSpecific, 7), - EncodingType, out ReadOnlyMemory value)); Assert.Equal("6869", value.ByteArrayToHex()); @@ -667,35 +668,33 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER, "0C026869", PublicTagClass.Universal, 12)] - [InlineData(PublicEncodingRules.CER, "0C026869", PublicTagClass.Universal, 12)] - [InlineData(PublicEncodingRules.DER, "0C026869", PublicTagClass.Universal, 12)] - [InlineData(PublicEncodingRules.BER, "80023132", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C023132", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A46023132", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "0C026869", TagClass.Universal, 12)] + [InlineData(AsnEncodingRules.CER, "0C026869", TagClass.Universal, 12)] + [InlineData(AsnEncodingRules.DER, "0C026869", TagClass.Universal, 12)] + [InlineData(AsnEncodingRules.BER, "80023132", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C023132", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A46023132", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryReadPrimitiveCharacterStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, true), - UniversalTagNumber.UTF8String, + new Asn1Tag(tagClass, tagValue, true), out ReadOnlyMemory val1)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryReadPrimitiveCharacterStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, false), - UniversalTagNumber.UTF8String, + new Asn1Tag(tagClass, tagValue, false), out ReadOnlyMemory val2)); Assert.False(reader.HasData); diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs new file mode 100644 index 00000000000000..ff9e333e2addcd --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs @@ -0,0 +1,299 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadUtcTime + { + [Theory] + // A, B2, C2 + [InlineData(AsnEncodingRules.BER, "17113137303930383130333530332D30373030", 2017, 9, 8, 10, 35, 3, -7, 0)] + [InlineData(AsnEncodingRules.BER, "17113137303930383130333530332D30303530", 2017, 9, 8, 10, 35, 3, 0, -50)] + [InlineData(AsnEncodingRules.BER, "17113137303930383130333530332B30373030", 2017, 9, 8, 10, 35, 3, 7, 0)] + [InlineData(AsnEncodingRules.BER, "17113030303130313030303030302B30303030", 2000, 1, 1, 0, 0, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "17113030303130313030303030302D31343030", 2000, 1, 1, 0, 0, 0, -14, 0)] + // A, B2, C1 (only legal form for CER or DER) + [InlineData(AsnEncodingRules.BER, "170D3132303130323233353935395A", 2012, 1, 2, 23, 59, 59, 0, 0)] + [InlineData(AsnEncodingRules.CER, "170D3439313233313233353935395A", 2049, 12, 31, 23, 59, 59, 0, 0)] + [InlineData(AsnEncodingRules.DER, "170D3530303130323132333435365A", 1950, 1, 2, 12, 34, 56, 0, 0)] + // A, B1, C2 + [InlineData(AsnEncodingRules.BER, "170F313730393038313033352D30373030", 2017, 9, 8, 10, 35, 0, -7, 0)] + [InlineData(AsnEncodingRules.BER, "170F323730393038313033352B30393132", 2027, 9, 8, 10, 35, 0, 9, 12)] + // A, B1, C1 + [InlineData(AsnEncodingRules.BER, "170B313230313032323335395A", 2012, 1, 2, 23, 59, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "170B343931323331323335395A", 2049, 12, 31, 23, 59, 0, 0, 0)] + // BER Constructed form + [InlineData( + AsnEncodingRules.BER, + "3780" + + "04023132" + + "04023031" + + "2480" + "040130" + "040132" + "0000" + + "040432333539" + + "04830000015A" + + "0000", + 2012, 1, 2, 23, 59, 0, 0, 0)] + public static void ParseTime_Valid( + AsnEncodingRules ruleSet, + string inputHex, + int year, + int month, + int day, + int hour, + int minute, + int second, + int offsetHour, + int offsetMinute) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + DateTimeOffset value = reader.ReadUtcTime(); + + Assert.Equal(year, value.Year); + Assert.Equal(month, value.Month); + Assert.Equal(day, value.Day); + Assert.Equal(hour, value.Hour); + Assert.Equal(minute, value.Minute); + Assert.Equal(second, value.Second); + Assert.Equal(0, value.Millisecond); + Assert.Equal(new TimeSpan(offsetHour, offsetMinute, 0), value.Offset); + } + + [Fact] + public static void ParseTime_InvalidValue_LegalString() + { + byte[] inputData = "17113030303030303030303030302D31353030".HexToByteArray(); + + var exception = Assert.Throws( + () => + { + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + reader.ReadUtcTime(); + }); + + Assert.NotNull(exception.InnerException); + Assert.IsType(exception.InnerException); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum(int maximum, int interpretedYear) + { + byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + DateTimeOffset value = reader.ReadUtcTime(maximum); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum_FromOptions(int maximum, int interpretedYear) + { + byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); + + AsnReaderOptions options = new AsnReaderOptions { UtcTimeTwoDigitYearMax = maximum }; + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER, options); + DateTimeOffset value = reader.ReadUtcTime(maximum); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum_FromOptions_CustomTag(int maximum, int interpretedYear) + { + byte[] inputData = "820D3132303130323233353935395A".HexToByteArray(); + + AsnReaderOptions options = new AsnReaderOptions { UtcTimeTwoDigitYearMax = maximum }; + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER, options); + DateTimeOffset value = reader.ReadUtcTime(maximum, new Asn1Tag(TagClass.ContextSpecific, 2)); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData("A,B2,C2", AsnEncodingRules.CER, "17113137303930383130333530332D30373030")] + [InlineData("A,B2,C2", AsnEncodingRules.DER, "17113137303930383130333530332D30373030")] + [InlineData("A,B1,C2", AsnEncodingRules.CER, "170F313730393038313033352D30373030")] + [InlineData("A,B1,C2", AsnEncodingRules.DER, "170F313730393038313033352D30373030")] + [InlineData("A,B1,C1", AsnEncodingRules.CER, "170B313230313032323335395A")] + [InlineData("A,B1,C1", AsnEncodingRules.DER, "170B313230313032323335395A")] + [InlineData("A,B1,C1-NotZ", AsnEncodingRules.BER, "170B313230313032323335392B")] + [InlineData("A,B1,C2-NotPlusMinus", AsnEncodingRules.BER, "170F313730393038313033352C30373030")] + [InlineData("A,B2,C2-NotPlusMinus", AsnEncodingRules.BER, "17113137303930383130333530332C30373030")] + [InlineData("A,B2,C2-MinuteOutOfRange", AsnEncodingRules.BER, "17113030303030303030303030302D31353630")] + [InlineData("A,B1,C2-MinuteOutOfRange", AsnEncodingRules.BER, "170F303030303030303030302D31353630")] + [InlineData("A1,B2,C1-NotZ", AsnEncodingRules.DER, "170D3530303130323132333435365B")] + [InlineData("A,B2,C2-MissingDigit", AsnEncodingRules.BER, "17103137303930383130333530332C303730")] + [InlineData("A,B2,C2-TooLong", AsnEncodingRules.BER, "17123137303930383130333530332B3037303030")] + [InlineData("WrongTag", AsnEncodingRules.BER, "1A0D3132303130323233353935395A")] + public static void ReadUtcTime_Throws( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadUtcTime()); + } + + [Fact] + public static void ReadUtcTime_WayTooBig_Throws() + { + // Need to exceed the length that the shared pool will return for 17: + byte[] inputData = new byte[4097+4]; + inputData[0] = 0x17; + inputData[1] = 0x82; + inputData[2] = 0x10; + inputData[3] = 0x01; + + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Assert.Throws(() => reader.ReadUtcTime()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = "170D3530303130323132333435365A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadUtcTime(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.ReadUtcTime()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + const int TwoDigitYearMax = 2052; + byte[] inputData = "850D3530303130323132333435365A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadUtcTime(expectedTag: Asn1Tag.Null)); + Assert.True(reader.HasData, "HasData after bad universal tag"); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadUtcTime(TwoDigitYearMax, expectedTag: Asn1Tag.Null)); + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadUtcTime()); + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadUtcTime(TwoDigitYearMax)); + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.Application, 5))); + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadUtcTime(TwoDigitYearMax, expectedTag: new Asn1Tag(TagClass.Application, 5))); + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Throws( + () => reader.ReadUtcTime(TwoDigitYearMax, expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5))); + Assert.False(reader.HasData, "HasData after reading value"); + + reader = new AsnReader(inputData, ruleSet); + + Assert.Equal( + new DateTimeOffset(2050, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.ReadUtcTime(TwoDigitYearMax, expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5))); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "170D3530303130323132333435365A", TagClass.Universal, 23)] + [InlineData(AsnEncodingRules.CER, "170D3530303130323132333435365A", TagClass.Universal, 23)] + [InlineData(AsnEncodingRules.DER, "170D3530303130323132333435365A", TagClass.Universal, 23)] + [InlineData(AsnEncodingRules.BER, "800D3530303130323132333435365A", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C0D3530303130323132333435365A", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A460D3530303130323132333435365A", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + DateTimeOffset val1 = reader.ReadUtcTime(expectedTag: new Asn1Tag(tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + DateTimeOffset val2 = reader.ReadUtcTime(expectedTag: new Asn1Tag(tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs similarity index 85% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs index 0cd9ef5c0d8dc6..058213ec38430e 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { public static class ReaderStateTests { @@ -14,7 +13,7 @@ public static void HasDataAndThrowIfNotEmpty() { AsnReader reader = new AsnReader(new byte[] { 0x01, 0x01, 0x00 }, AsnEncodingRules.BER); Assert.True(reader.HasData); - Assert.Throws(() => reader.ThrowIfNotEmpty()); + Assert.Throws(() => reader.ThrowIfNotEmpty()); // Consume the current value and move on. reader.ReadEncodedValue(); diff --git a/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj new file mode 100644 index 00000000000000..beb4397ba6c512 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj @@ -0,0 +1,55 @@ + + + true + $(NetCoreAppCurrent);$(NetFrameworkCurrent) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CommonTest\System\Security\Cryptography\ByteUtils.cs + + + diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs b/src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs similarity index 81% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs index 08d3a52e89d9f9..c20633d7f2be2c 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { - public abstract partial class Asn1WriterTests : Asn1ReaderTests + public abstract partial class Asn1WriterTests { internal static void Verify(AsnWriter writer, string expectedHex) { @@ -43,6 +42,14 @@ internal static void Verify(AsnWriter writer, string expectedHex) Assert.Equal(encoded.Length, bytesWritten); Assert.True(dest.Slice(0, bytesWritten).SequenceEqual(encoded), "dest.SequenceEqual(encoded2) (overly big)"); Assert.Equal(254, encoded2[encoded.Length]); + + Assert.True(writer.EncodedValueEquals(encoded)); + Assert.False(writer.EncodedValueEquals(encoded2)); + Assert.True(writer.EncodedValueEquals(encoded2.AsSpan(0, encoded.Length))); + Assert.False(writer.EncodedValueEquals(encoded2.AsSpan(1, encoded.Length))); + + encoded2[encoded.Length - 1] ^= 0xFF; + Assert.False(writer.EncodedValueEquals(encoded2.AsSpan(0, encoded.Length))); } internal static unsafe string Stringify(Asn1Tag tag) diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs b/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs new file mode 100644 index 00000000000000..03c1eeb532b91b --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs @@ -0,0 +1,322 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Formats.Asn1.Tests.Reader; +using System.Numerics; +using Test.Cryptography; +using Xunit; +using X509KeyUsageCSharpStyle = System.Formats.Asn1.Tests.Reader.ReadNamedBitList.X509KeyUsageCSharpStyle; + +namespace System.Formats.Asn1.Tests.Writer +{ + public static class ComprehensiveWriteTest + { + [Fact] + public static void WriteMicrosoftDotComCert() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + // Certificate + using (writer.PushSequence()) + { + // tbsCertificate + using (writer.PushSequence()) + { + // version ([0] EXPLICIT INTEGER) + Asn1Tag context0 = new Asn1Tag(TagClass.ContextSpecific, 0, true); + + using (writer.PushSequence(context0)) + { + writer.WriteInteger(2); + } + + BigInteger serialValue = BigInteger.Parse("82365655871428336739211871484630851433"); + writer.WriteInteger(serialValue); + + // signature (algorithm) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + } + + // issuer + using (writer.PushSequence()) + { + WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.10", "Symantec Corporation", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.11", "Symantec Trust Network", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.3", "Symantec Class 3 EV SSL CA - G3", UniversalTagNumber.PrintableString); + } + + // validity + using (writer.PushSequence()) + { + writer.WriteUtcTime(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero)); + writer.WriteUtcTime(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero)); + } + + // subject + using (writer.PushSequence()) + { + WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.3", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.2", "Washington", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.15", "Private Organization", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.5", "600413485", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.17", "98052", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.8", "Washington", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.7", "Redmond", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.9", "1 Microsoft Way", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.10", "Microsoft Corporation", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.11", "MSCOM", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.3", "www.microsoft.com", UniversalTagNumber.UTF8String); + } + + // subjectPublicKeyInfo + using (writer.PushSequence()) + { + // subjectPublicKeyInfo.algorithm + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.1"); + writer.WriteNull(); + } + + AsnWriter publicKeyWriter = new AsnWriter(AsnEncodingRules.DER); + + using (publicKeyWriter.PushSequence()) + { + BigInteger modulus = BigInteger.Parse( + "207545550571844404676608632512851454930111394466749205318948660756381" + + "523214360115124048083611193260260272384440199925180817531535965931647" + + "037093368608713442955529617501657176146245891571745113402870077189045" + + "116705181899983704226178882882602868159586789723579670915035003754974" + + "985730226756711782751711104985926458681071638525996766798322809764200" + + "941677343791419428587801897366593842552727222686457866144928124161967" + + "521735393182823375650694786333059783380738262856873316471830589717911" + + "537307419734834201104082715701367336140572971505716740825623220507359" + + "42929758463490933054115079473593821332264673455059897928082590541"); + publicKeyWriter.WriteInteger(modulus); + publicKeyWriter.WriteInteger(65537); + } + + // subjectPublicKeyInfo.subjectPublicKey + writer.WriteBitString(publicKeyWriter.Encode()); + } + + // extensions ([3] EXPLICIT Extensions) + Asn1Tag context3 = new Asn1Tag(TagClass.ContextSpecific, 3); + + using (writer.PushSequence(context3)) + { + using (writer.PushSequence()) + { + Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); + + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.17"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "www.microsoft.com", + dnsName); + + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "wwwqa.microsoft.com", + dnsName); + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.19"); + // Empty sequence as the payload for a non-CA basic constraint. + writer.WriteOctetString(new byte[] { 0x30, 0x00 }); + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.15"); + // critical: true + writer.WriteBoolean(true); + + using (writer.PushOctetString()) + { + // This extension doesn't use a sequence at all, just Named Bit List. + writer.WriteNamedBitList( + X509KeyUsageCSharpStyle.DigitalSignature | + X509KeyUsageCSharpStyle.KeyEncipherment); + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.37"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.3.1"); + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.3.2"); + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.32"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.16.840.1.113733.1.7.23.6"); + + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.2.1"); + + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "https://d.symcb.com/cps"); + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.2.2"); + + using (writer.PushSequence()) + { + writer.WriteCharacterString( + UniversalTagNumber.VisibleString, + "https://d.symcb.com/rpa"); + } + } + } + } + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.35"); + + Asn1Tag keyIdentifier = context0; + byte[] authorityKeyIdentifier = "0159ABE7DD3A0B59A66463D6CF200757D591E76A".HexToByteArray(); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + writer.WriteOctetString(authorityKeyIdentifier, keyIdentifier); + } + } + + Asn1Tag generalNameUriChoice = new Asn1Tag(TagClass.ContextSpecific, 6); + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.31"); + + Asn1Tag distributionPointChoice = context0; + Asn1Tag fullNameChoice = context0; + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + using (writer.PushSequence(distributionPointChoice)) + { + using (writer.PushSequence(fullNameChoice)) + { + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "http://sr.symcb.com/sr.crl", + generalNameUriChoice); + } + } + } + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.1.1"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "http://sr.symcd.com", + generalNameUriChoice); + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2"); + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "http://sr.symcb.com/sr.crt", + generalNameUriChoice); + } + } + } + } + } + } + + // signatureAlgorithm + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + } + + // signature + byte[] containsSignature = ( + "010203040506070809" + + "15F8505B627ED7F9F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC" + + "20ACF728AAFA7A1A1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D6" + + "45BFCF840A4A3FDD988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A" + + "64A9C5FB96932BA70059CE92BD278B41299FD213471BD8165F924285AE3ECD66" + + "6C703885DCA65D24DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D" + + "4A56ADB21B5822549918015647B5F8AC131CC5EB24534D172BC60218A88B65BC" + + "F71C7F388CE3E0EF697B4203720483BB5794455B597D80D48CD3A1D73CBBC609" + + "C058767D1FF060A609D7E3D4317079AF0CD0A8A49251AB129157F9894A036487" + + "090807060504030201").HexToByteArray(); + + writer.WriteBitString(containsSignature.AsSpan(9, 256)); + } + + Assert.Equal( + ComprehensiveReadTests.MicrosoftDotComSslCertBytes.ByteArrayToHex(), + writer.Encode().ByteArrayToHex()); + + Assert.True(writer.EncodedValueEquals(ComprehensiveReadTests.MicrosoftDotComSslCertBytes)); + } + + private static void WriteRdn(AsnWriter writer, string oid, string value, UniversalTagNumber valueType) + { + using (writer.PushSetOf()) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier(oid); + writer.WriteCharacterString(valueType, value); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs new file mode 100644 index 00000000000000..b3cb9e798a4214 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class PushPopOctetString : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopOctetString()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + writer.PopOctetString(); + + Assert.Throws( + () => writer.PopOctetString()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + writer.PopOctetString(); + + Assert.Throws( + () => writer.PopOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + Assert.Throws( + () => writer.PopOctetString()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + + Assert.Throws( + () => writer.PopOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(UniversalTagNumber.OctetString)); + writer.PopOctetString(); + + Verify(writer, "0400"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(TagClass.Private, 5)); + writer.PopOctetString(new Asn1Tag(TagClass.Private, 5, true)); + + Verify(writer, "C500"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + writer.PopOctetString(new Asn1Tag(UniversalTagNumber.OctetString)); + + Verify(writer, "0400"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopOctetString(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1100_0000); + string tagHex = tag.ToString("X2"); + string expectedHex = tagHex + "00"; + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void LargePayload_1000(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (writer.PushOctetString(new Asn1Tag(TagClass.ContextSpecific, 9))) + { + byte[] tmp = new byte[496]; + writer.WriteOctetString(tmp); + writer.WriteOctetString(tmp, new Asn1Tag(TagClass.ContextSpecific, 10)); + } + + string zeroHex496Bytes = new string('0', 496 * 2); + + string expectedHex = + // Tag + "89" + + // Length + "8203E8" + + // First written content + "048201F0" + zeroHex496Bytes + + // Second written content + "8A8201F0" + zeroHex496Bytes; + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void LargePayload_1001(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (writer.PushOctetString(new Asn1Tag(TagClass.Private, 9))) + { + byte[] tmp = new byte[497]; + writer.WriteOctetString(tmp.AsSpan(0, 496)); + writer.WriteOctetString(tmp, new Asn1Tag(TagClass.Application, 10)); + } + + string zeroHex496Bytes = new string('0', 496 * 2); + + string expectedHex; + + if (ruleSet == AsnEncodingRules.CER) + { + // This moved into the constructed encoding form. + // Tag + expectedHex = + // Tag + "E9" + + // Indefinite length + "80" + + // Definite octet string + "04" + + // 1000 bytes + "8203E8" + + // First written content + "048201F0" + zeroHex496Bytes + + // Second written content tag, length, and first 498 payload bytes + "4A8201F1" + zeroHex496Bytes + + // Second definite octet string, 1 byte, { 0x00 } payload + "040100" + + // End indefinite length + "0000"; + } + else + { + expectedHex = + // Tag + "C9" + + // Length + "8203E9" + + // First written content + "048201F0" + zeroHex496Bytes + + // Second written content + "4A8201F1" + zeroHex496Bytes + "00"; + } + + Verify(writer, expectedHex); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs new file mode 100644 index 00000000000000..85d86768bd0c45 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs @@ -0,0 +1,602 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class PushPopSequence : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSequence()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(); + + Assert.Throws( + () => writer.PopSequence()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(); + + Assert.Throws( + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + Assert.Throws( + () => writer.PopSequence()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + + Assert.Throws( + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); + writer.PopSequence(); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "30800000"); + } + else + { + Verify(writer, "3000"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.Private, 5)); + writer.PopSequence(new Asn1Tag(TagClass.Private, 5, true)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "E5800000"); + } + else + { + Verify(writer, "E500"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "30800000"); + } + else + { + Verify(writer, "3000"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopSequence(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == AsnEncodingRules.CER ? "800000" : "00"; + + Verify(writer, tagHex + rest); + } + + [Fact] + public static void BER_WritesDefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "3000"); + } + + [Fact] + public static void CER_WritesIndefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "30800000"); + } + + [Fact] + public static void DER_WritesDefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "3000"); + } + + [Fact] + public static void BER_WritesDefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "EF00"); + } + + [Fact] + public static void CER_WritesIndefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "7F5B800000"); + } + + [Fact] + public static void DER_WritesDefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "BE00"); + } + + private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) + { + writer.PushSequence(); + { + writer.PushSequence(alt); + writer.PopSequence(alt); + + writer.PushSequence(); + { + writer.PushSequence(alt); + { + writer.PushSequence(); + writer.PopSequence(); + } + + writer.PopSequence(alt); + } + + writer.PopSequence(); + } + + writer.PopSequence(); + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + + TestNested(writer, alt, "300AFF7F003005FF7F023000"); + } + + [Fact] + public static void CER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); + + TestNested(writer, alt, "3080AC8000003080AC8030800000000000000000"); + } + + [Fact] + public static void DER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); + + TestNested(writer, alt, "30086500300465023000"); + } + + private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) + { + writer.PushSequence(); + + // F00DF00D...F00DF00D + byte[] contentBytes = new byte[126]; + + for (int i = 0; i < contentBytes.Length; i += 2) + { + contentBytes[i] = 0xF0; + contentBytes[i + 1] = 0x0D; + } + + writer.WriteOctetString(contentBytes); + writer.PopSequence(); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void SimpleContentShift(AsnEncodingRules ruleSet) + { + const string ExpectedHex = + "308180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; + + AsnWriter writer = new AsnWriter(ruleSet); + SimpleContentShiftCore(writer, ExpectedHex); + } + + [Fact] + public static void SimpleContentShift_CER() + { + const string ExpectedHex = + "3080" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "0000"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + SimpleContentShiftCore(writer, ExpectedHex); + } + + private static void WriteRSAPublicKeyCore(AsnEncodingRules ruleSet, string expectedHex) + { + AsnWriter innerWriter = new AsnWriter(ruleSet); + byte[] paddedBigEndianN = ( + "00" + + "AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED369731156" + + "20968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925BCE" + + "624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8CBB" + + "5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF18" + + "7B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C94" + + "AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF2B" + + "82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6D9" + + "FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD8847").HexToByteArray(); + + // Now it's padded little-endian. + Array.Reverse(paddedBigEndianN); + BigInteger n = new BigInteger(paddedBigEndianN); + const long e = 8589935681; + + innerWriter.PushSequence(); + innerWriter.WriteInteger(n); + innerWriter.WriteInteger(e); + innerWriter.PopSequence(); + + AsnWriter outerWriter = new AsnWriter(ruleSet); + // RSAPublicKey + outerWriter.PushSequence(); + + // AlgorithmIdentifier + outerWriter.PushSequence(); + outerWriter.WriteObjectIdentifier("1.2.840.113549.1.1.1"); + outerWriter.WriteNull(); + outerWriter.PopSequence(); + + outerWriter.WriteBitString(innerWriter.Encode()); + outerWriter.PopSequence(); + + Verify(outerWriter, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void WriteRSAPublicKey(AsnEncodingRules ruleSet) + { + const string ExpectedHex = + // CONSTRUCTED SEQUENCE + "30820124" + + // CONSTRUCTED SEQUENCE + "300D" + + // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) + "06092A864886F70D010101" + + // NULL + "0500" + + // BIT STRING + "03820111" + + // 0 unused bits + "00" + + // sneaky inspection of the payload bytes + // CONSTRUCTED SEQUENCE + "3082010C" + + // INTEGER (n) + "02820101" + + "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + + "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + + "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + + "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + + "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + + "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + + "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + + "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + + "47" + + // INTEGER (e) + "02050200000441"; + + WriteRSAPublicKeyCore(ruleSet, ExpectedHex); + } + + [Fact] + public static void WriteRSAPublicKey_CER() + { + const string ExpectedHex = + // CONSTRUCTED SEQUENCE + "3080" + + // CONSTRUCTED SEQUENCE + "3080" + + // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) + "06092A864886F70D010101" + + // NULL + "0500" + + // End-of-Contents + "0000" + + // BIT STRING + "03820111" + + // 0 unused bits + "00" + + // sneaky inspection of the payload bytes + // CONSTRUCTED SEQUENCE + "3080" + + // INTEGER (n) + "02820101" + + "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + + "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + + "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + + "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + + "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + + "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + + "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + + "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + + "47" + + // INTEGER (e) + "02050200000441" + + // End-of-Contents + "0000" + + // (no EoC for the BIT STRING) + // End-of-Contents + "0000"; + + WriteRSAPublicKeyCore(AsnEncodingRules.CER, ExpectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, true)] + public static void CannotEncodeWhileUnbalanced(AsnEncodingRules ruleSet, bool customTag) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSequence(); + } + + int written = -5; + + Assert.Throws(() => writer.GetEncodedLength()); + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); + + byte[] buf = new byte[10]; + Assert.Throws(() => writer.TryEncode(buf, out written)); + Assert.Throws(() => writer.EncodedValueEquals(buf)); + Assert.Equal(-5, written); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => writer.PushSequence(Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_PopSequence(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + writer.PushSetOf(tag); + + Assert.Throws( + () => writer.PopSequence(tag)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_IdempotentDispose(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + + using (var scope = writer.PushSequence(tag)) + { + writer.PopSequence(tag); + + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_IdempotentDispose_Complex(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + expectedHex += expectedHex; + + using (var scope = writer.PushSequence(tag)) + { + writer.PopSequence(tag); + + writer.PushSequence(tag); + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + writer.PopSequence(tag); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_DisposeThrowsIfDeepContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + var scope = writer.PushSequence(); + writer.PushSetOf(); + + Assert.Throws(() => scope.Dispose()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_DisposeSilentNotContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (var scope = writer.PushSequence()) + { + writer.PopSequence(); + + // Since this has a different write offset, the equals is false, so no exception. + writer.PushSequence(); + writer.PushSequence(); + + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs new file mode 100644 index 00000000000000..aaf55ab1836849 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs @@ -0,0 +1,585 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class PushPopSetOf : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSetOf()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(); + + Assert.Throws( + () => writer.PopSetOf()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(); + + Assert.Throws( + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + Assert.Throws( + () => writer.PopSetOf()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + + Assert.Throws( + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + writer.PopSetOf(); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "31800000"); + } + else + { + Verify(writer, "3100"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.Private, 5)); + writer.PopSetOf(new Asn1Tag(TagClass.Private, 5, true)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "E5800000"); + } + else + { + Verify(writer, "E500"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "31800000"); + } + else + { + Verify(writer, "3100"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == AsnEncodingRules.CER ? "800000" : "00"; + + Verify(writer, tagHex + rest); + } + + [Fact] + public static void BER_WritesDefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "3100"); + } + + [Fact] + public static void CER_WritesIndefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "31800000"); + } + + [Fact] + public static void DER_WritesDefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "3100"); + } + + [Fact] + public static void BER_WritesDefinite_CustomTag__Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "EF00"); + } + + [Fact] + public static void CER_WritesIndefinite_CustomTag__Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "7F5B800000"); + } + + [Fact] + public static void DER_WritesDefinite_CustomTag__Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "BE00"); + } + + private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) + { + // Written in pre-sorted order, since sorting is a different test. + writer.PushSetOf(); + { + writer.PushSetOf(); + { + writer.PushSetOf(alt); + { + writer.PushSetOf(); + writer.PopSetOf(); + } + + writer.PopSetOf(alt); + } + + writer.PopSetOf(); + + writer.PushSetOf(alt); + writer.PopSetOf(alt); + } + + writer.PopSetOf(); + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + + TestNested(writer, alt, "310A3105FF7F023100FF7F00"); + } + + [Fact] + public static void CER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); + + TestNested(writer, alt, "31803180AC803180000000000000AC8000000000"); + } + + [Fact] + public static void DER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); + + TestNested(writer, alt, "31083104650231006500"); + } + + private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) + { + writer.PushSetOf(); + + // F00DF00D...F00DF00D + byte[] contentBytes = new byte[126]; + + for (int i = 0; i < contentBytes.Length; i += 2) + { + contentBytes[i] = 0xF0; + contentBytes[i + 1] = 0x0D; + } + + writer.WriteOctetString(contentBytes); + writer.PopSetOf(); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void SimpleContentShift(AsnEncodingRules ruleSet) + { + const string ExpectedHex = + "318180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; + + AsnWriter writer = new AsnWriter(ruleSet); + SimpleContentShiftCore(writer, ExpectedHex); + } + + [Fact] + public static void SimpleContentShift_CER() + { + const string ExpectedHex = + "3180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "0000"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + SimpleContentShiftCore(writer, ExpectedHex); + } + + private static void ValidateDataSorting(AsnEncodingRules ruleSet, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + + // 02 01 FF + writer.WriteInteger(-1); + // 02 01 00 + writer.WriteInteger(0); + // 02 02 00 FF + writer.WriteInteger(255); + // 01 01 FF + writer.WriteBoolean(true); + // 45 01 00 + writer.WriteBoolean(false, new Asn1Tag(TagClass.Application, 5)); + // 02 01 7F + writer.WriteInteger(127); + // 02 01 80 + writer.WriteInteger(sbyte.MinValue); + // 02 02 00 FE + writer.WriteInteger(254); + // 02 01 00 + writer.WriteInteger(0); + + writer.PopSetOf(); + + // The correct sort order (CER, DER) is + // Universal Boolean: true + // Universal Integer: 0 + // Universal Integer: 0 + // Universal Integer: 127 + // Universal Integer: -128 + // Universal Integer: -1 + // Universal Integer: 254 + // Universal Integer: 255 + // Application 5 (Boolean): false + + // This test would be + // + // GrabBag ::= SET OF GrabBagItem + // + // GrabBagItem ::= CHOICE ( + // value INTEGER + // bool BOOLEAN + // grr [APPLICATION 5] IMPLICIT BOOLEAN + // ) + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_DoesNotSort() + { + const string ExpectedHex = + "311D" + + "0201FF" + + "020100" + + "020200FF" + + "0101FF" + + "450100" + + "02017F" + + "020180" + + "020200FE" + + "020100"; + + ValidateDataSorting(AsnEncodingRules.BER, ExpectedHex); + } + + [Fact] + public static void CER_SortsData() + { + const string ExpectedHex = + "3180" + + "0101FF" + + "020100" + + "020100" + + "02017F" + + "020180" + + "0201FF" + + "020200FE" + + "020200FF" + + "450100" + + "0000"; + + ValidateDataSorting(AsnEncodingRules.CER, ExpectedHex); + } + + [Fact] + public static void DER_SortsData() + { + const string ExpectedHex = + "311D" + + "0101FF" + + "020100" + + "020100" + + "02017F" + + "020180" + + "0201FF" + + "020200FE" + + "020200FF" + + "450100"; + + ValidateDataSorting(AsnEncodingRules.DER, ExpectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, true)] + public static void CannotEncodeWhileUnbalanced(AsnEncodingRules ruleSet, bool customTag) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSetOf(); + } + + int written = -5; + + Assert.Throws(() => writer.GetEncodedLength()); + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); + + byte[] buf = new byte[10]; + Assert.Throws(() => writer.TryEncode(buf, out written)); + Assert.Throws(() => writer.EncodedValueEquals(buf)); + Assert.Equal(-5, written); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => writer.PushSetOf(Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_PopSetOf(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + writer.PushSequence(tag); + + Assert.Throws( + () => writer.PopSetOf(tag)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_IdempotentDispose(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + + using (var scope = writer.PushSetOf(tag)) + { + writer.PopSetOf(tag); + + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_IdempotentDispose_Complex(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + expectedHex += expectedHex; + + using (var scope = writer.PushSetOf(tag)) + { + writer.PopSetOf(tag); + + writer.PushSetOf(tag); + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + writer.PopSetOf(tag); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_DisposeThrowsIfDeepContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + var scope = writer.PushSetOf(); + writer.PushSequence(); + + Assert.Throws(() => scope.Dispose()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_DisposeSilentNotContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (var scope = writer.PushSetOf()) + { + writer.PopSetOf(); + + // Since this has a different write offset, the equals is false, so no exception. + writer.PushSetOf(); + writer.PushSequence(); + + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs b/src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs new file mode 100644 index 00000000000000..67c675fe92dbfe --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public static class SimpleWriterTests + { + [Theory] + [InlineData(-1)] + [InlineData(3)] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + public static void ValidateRuleSet(int value) + { + AssertExtensions.Throws( + "ruleSet", + () => new AsnWriter((AsnEncodingRules)value)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EncodeEmpty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + byte[] encoded = writer.Encode(); + Assert.Same(Array.Empty(), encoded); + + Assert.True(writer.TryEncode(Span.Empty, out int written)); + Assert.Equal(0, written); + Assert.True(writer.EncodedValueEquals(ReadOnlySpan.Empty)); + + Span negativeTest = stackalloc byte[] { 5, 0 }; + Assert.False(writer.EncodedValueEquals(negativeTest)); + } + + [Fact] + public static void DisposeDefaultScope() + { + AsnWriter.Scope scope = default; + // Assert.NoThrow + scope.Dispose(); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_Empty(AsnEncodingRules ruleSet) + { + AsnWriter empty = new AsnWriter(ruleSet); + AsnWriter dest = new AsnWriter(AsnEncodingRules.BER); + + Assert.Throws(() => empty.CopyTo(dest)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_TwoValues(AsnEncodingRules ruleSet) + { + AsnWriter source = new AsnWriter(ruleSet); + AsnWriter dest = new AsnWriter(AsnEncodingRules.BER); + + source.WriteBoolean(false); + source.WriteBoolean(true); + + Assert.Throws(() => source.CopyTo(dest)); + } + + [Fact] + public static void CopyTo_IncompatibleRules() + { + AsnWriter cer = new AsnWriter(AsnEncodingRules.CER); + AsnWriter der = new AsnWriter(AsnEncodingRules.DER); + + using (cer.PushSequence()) + using (der.PushSequence()) + { + cer.WriteBoolean(false); + der.WriteBoolean(true); + } + + Assert.Throws(() => cer.CopyTo(der)); + Assert.Throws(() => der.CopyTo(cer)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_Success(AsnEncodingRules ruleSet) + { + AsnWriter source = new AsnWriter(ruleSet); + AsnWriter dest = new AsnWriter(AsnEncodingRules.BER); + + Assert.True(source.EncodedValueEquals(dest)); + + source.WriteBoolean(false); + Assert.False(source.EncodedValueEquals(dest)); + + source.CopyTo(dest); + Assert.True(source.EncodedValueEquals(dest)); + + Assert.Equal(source.Encode(), dest.Encode()); + + source.CopyTo(dest); + Assert.False(source.EncodedValueEquals(dest)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EncodedValueEquals_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "other", + () => writer.EncodedValueEquals((AsnWriter)null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "destination", + () => writer.CopyTo(null)); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs similarity index 88% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs index 890e04ff40e338..aeb39deffb3e69 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteBMPString : WriteCharacterString { @@ -67,13 +66,13 @@ internal override void WriteString(AsnWriter writer, string s) => writer.WriteCharacterString(UniversalTagNumber.BMPString, s); internal override void WriteString(AsnWriter writer, Asn1Tag tag, string s) => - writer.WriteCharacterString(tag, UniversalTagNumber.BMPString, s); + writer.WriteCharacterString(UniversalTagNumber.BMPString, s, tag); internal override void WriteSpan(AsnWriter writer, ReadOnlySpan s) => writer.WriteCharacterString(UniversalTagNumber.BMPString, s); internal override void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s) => - writer.WriteCharacterString(tag, UniversalTagNumber.BMPString, s); + writer.WriteCharacterString(UniversalTagNumber.BMPString, s, tag); internal override Asn1Tag StandardTag => new Asn1Tag(UniversalTagNumber.BMPString); @@ -214,32 +213,32 @@ public void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, strin base.VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(input, expectedPayloadHex); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_String_Null(PublicEncodingRules ruleSet) => + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_String_Null(AsnEncodingRules ruleSet) => base.VerifyWrite_String_Null_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) => + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_String_Null_CustomTag(AsnEncodingRules ruleSet) => base.VerifyWrite_String_Null_CustomTag_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) => - base.VerifyWrite_EndOfContents_String_Helper(ruleSet); + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_Null_String(AsnEncodingRules ruleSet) => + base.VerifyWrite_Null_String_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) => - base.VerifyWrite_EndOfContents_Span_Helper(ruleSet); + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_Null_Span(AsnEncodingRules ruleSet) => + base.VerifyWrite_Null_Span_Helper(ruleSet); [Theory] [MemberData(nameof(CERSegmentedCases))] @@ -290,17 +289,5 @@ public void VerifyWrite_String_NonEncodable(string input) => [MemberData(nameof(InvalidInputs))] public void VerifyWrite_Span_NonEncodable(string input) => base.VerifyWrite_Span_NonEncodable_Helper(input); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_Span(bool empty) => - base.WriteAfterDispose_Span_Helper(empty); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_String(bool empty) => - base.WriteAfterDispose_String_Helper(empty); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs new file mode 100644 index 00000000000000..d70c5adf2745ad --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteBitString : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteEmpty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(ReadOnlySpan.Empty); + + Verify(writer, "030100"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 1, 1, "030201")] + [InlineData(AsnEncodingRules.CER, 2, 1, "030301")] + [InlineData(AsnEncodingRules.DER, 3, 1, "030401")] + [InlineData(AsnEncodingRules.BER, 126, 0, "037F00")] + [InlineData(AsnEncodingRules.CER, 127, 3, "03818003")] + [InlineData(AsnEncodingRules.BER, 999, 0, "038203E800")] + [InlineData(AsnEncodingRules.CER, 999, 0, "038203E800")] + [InlineData(AsnEncodingRules.DER, 999, 0, "038203E800")] + [InlineData(AsnEncodingRules.BER, 1000, 0, "038203E900")] + [InlineData(AsnEncodingRules.DER, 1000, 0, "038203E900")] + [InlineData(AsnEncodingRules.BER, 2000, 0, "038207D100")] + [InlineData(AsnEncodingRules.DER, 2000, 0, "038207D100")] + public void WritePrimitive(AsnEncodingRules ruleSet, int length, int unusedBitCount, string hexStart) + { + string payloadHex = new string('0', 2 * length); + string expectedHex = hexStart + payloadHex; + byte[] data = new byte[length]; + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(data, unusedBitCount); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(1000, 1, "2380038203E800", "030201")] + [InlineData(999*2, 3, "2380038203E800", "038203E803")] + public void WriteSegmentedCER(int length, int unusedBitCount, string hexStart, string hexStart2) + { + string payload1Hex = new string('8', 999 * 2); + string payload2Hex = new string('8', (length - 999) * 2); + string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; + byte[] data = new byte[length]; + + for (int i = 0; i < data.Length; i++) + { + data[i] = 0x88; + } + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteBitString(data, unusedBitCount); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, false)] + [InlineData(AsnEncodingRules.CER, 0, false)] + [InlineData(AsnEncodingRules.DER, 0, false)] + [InlineData(AsnEncodingRules.BER, 999, false)] + [InlineData(AsnEncodingRules.CER, 999, false)] + [InlineData(AsnEncodingRules.DER, 999, false)] + [InlineData(AsnEncodingRules.BER, 1000, false)] + [InlineData(AsnEncodingRules.CER, 1000, true)] + [InlineData(AsnEncodingRules.DER, 1000, false)] + [InlineData(AsnEncodingRules.BER, 1998, false)] + [InlineData(AsnEncodingRules.CER, 1998, true)] + [InlineData(AsnEncodingRules.BER, 4096, false)] + [InlineData(AsnEncodingRules.CER, 4096, true)] + [InlineData(AsnEncodingRules.DER, 4096, false)] + public void VerifyWriteBitString_PrimitiveOrConstructed( + AsnEncodingRules ruleSet, + int payloadLength, + bool expectConstructed) + { + byte[] data = new byte[payloadLength]; + + Asn1Tag[] tagsToTry = + { + new Asn1Tag(UniversalTagNumber.BitString), + new Asn1Tag(UniversalTagNumber.BitString, isConstructed: true), + new Asn1Tag(TagClass.Private, 87), + new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), + }; + + byte[] answerBuf = new byte[payloadLength + 100]; + + foreach (Asn1Tag toTry in tagsToTry) + { + Asn1Tag writtenTag; + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(data, tag: toTry); + + Assert.True(writer.TryEncode(answerBuf, out _)); + Assert.True(Asn1Tag.TryDecode(answerBuf, out writtenTag, out _)); + + if (expectConstructed) + { + Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + else + { + Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + + Assert.Equal(toTry.TagClass, writtenTag.TagClass); + Assert.Equal(toTry.TagValue, writtenTag.TagValue); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "FF", false)] + [InlineData(AsnEncodingRules.BER, 1, "FE", false)] + [InlineData(AsnEncodingRules.CER, 1, "FE", false)] + [InlineData(AsnEncodingRules.DER, 1, "FE", false)] + [InlineData(AsnEncodingRules.BER, 1, "FF", true)] + [InlineData(AsnEncodingRules.CER, 1, "FF", true)] + [InlineData(AsnEncodingRules.DER, 1, "FF", true)] + [InlineData(AsnEncodingRules.BER, 7, "C0", true)] + [InlineData(AsnEncodingRules.CER, 7, "C0", true)] + [InlineData(AsnEncodingRules.DER, 7, "C0", true)] + [InlineData(AsnEncodingRules.BER, 7, "80", false)] + [InlineData(AsnEncodingRules.CER, 7, "80", false)] + [InlineData(AsnEncodingRules.DER, 7, "80", false)] + [InlineData(AsnEncodingRules.DER, 7, "40", true)] + [InlineData(AsnEncodingRules.DER, 6, "40", false)] + [InlineData(AsnEncodingRules.DER, 6, "C0", false)] + [InlineData(AsnEncodingRules.DER, 6, "20", true)] + [InlineData(AsnEncodingRules.DER, 5, "20", false)] + [InlineData(AsnEncodingRules.DER, 5, "A0", false)] + [InlineData(AsnEncodingRules.DER, 5, "10", true)] + [InlineData(AsnEncodingRules.DER, 4, "10", false)] + [InlineData(AsnEncodingRules.DER, 4, "90", false)] + [InlineData(AsnEncodingRules.DER, 4, "30", false)] + [InlineData(AsnEncodingRules.DER, 4, "08", true)] + [InlineData(AsnEncodingRules.DER, 4, "88", true)] + [InlineData(AsnEncodingRules.DER, 3, "08", false)] + [InlineData(AsnEncodingRules.DER, 3, "A8", false)] + [InlineData(AsnEncodingRules.DER, 3, "04", true)] + [InlineData(AsnEncodingRules.DER, 3, "14", true)] + [InlineData(AsnEncodingRules.DER, 2, "04", false)] + [InlineData(AsnEncodingRules.DER, 2, "0C", false)] + [InlineData(AsnEncodingRules.DER, 2, "FC", false)] + [InlineData(AsnEncodingRules.DER, 2, "02", true)] + [InlineData(AsnEncodingRules.DER, 2, "82", true)] + [InlineData(AsnEncodingRules.DER, 2, "FE", true)] + [InlineData(AsnEncodingRules.DER, 1, "02", false)] + [InlineData(AsnEncodingRules.DER, 1, "82", false)] + [InlineData(AsnEncodingRules.DER, 1, "80", false)] + public static void WriteBitString_UnusedBitCount_MustBeValid( + AsnEncodingRules ruleSet, + int unusedBitCount, + string inputHex, + bool expectThrow) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnWriter writer = new AsnWriter(ruleSet); + + if (expectThrow) + { + AssertExtensions.Throws( + "unusedBitCount", + () => writer.WriteBitString(inputBytes, unusedBitCount)); + + AssertExtensions.Throws( + "unusedBitCount", + () => writer.WriteBitString( + inputBytes, + unusedBitCount, + new Asn1Tag(TagClass.ContextSpecific, 3))); + + return; + } + + byte[] output = new byte[512]; + writer.WriteBitString(inputBytes, unusedBitCount); + Assert.True(writer.TryEncode(output, out int bytesWritten)); + + // This assumes that inputBytes is never more than 999 (and avoids CER constructed forms) + Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); + + writer.WriteBitString(inputBytes, unusedBitCount, new Asn1Tag(TagClass.ContextSpecific, 9)); + Assert.True(writer.TryEncode(output, out bytesWritten)); + + Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, -1)] + [InlineData(AsnEncodingRules.CER, -1)] + [InlineData(AsnEncodingRules.DER, -1)] + [InlineData(AsnEncodingRules.BER, -2)] + [InlineData(AsnEncodingRules.CER, -2)] + [InlineData(AsnEncodingRules.DER, -2)] + [InlineData(AsnEncodingRules.BER, 8)] + [InlineData(AsnEncodingRules.CER, 8)] + [InlineData(AsnEncodingRules.DER, 8)] + [InlineData(AsnEncodingRules.BER, 9)] + [InlineData(AsnEncodingRules.CER, 9)] + [InlineData(AsnEncodingRules.DER, 9)] + [InlineData(AsnEncodingRules.BER, 1048576)] + [InlineData(AsnEncodingRules.CER, 1048576)] + [InlineData(AsnEncodingRules.DER, 1048576)] + public static void UnusedBitCounts_Bounds(AsnEncodingRules ruleSet, int unusedBitCount) + { + byte[] data = new byte[5]; + + AsnWriter writer = new AsnWriter(ruleSet); + + ArgumentOutOfRangeException exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(data, unusedBitCount)); + + Assert.Equal(unusedBitCount, exception.ActualValue); + + exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(data, unusedBitCount, new Asn1Tag(TagClass.ContextSpecific, 5))); + + Assert.Equal(unusedBitCount, exception.ActualValue); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EmptyData_Requires0UnusedBits(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); + + Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 1, contextTag)); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 7, contextTag)); + + writer.WriteBitString(ReadOnlySpan.Empty, 0); + writer.WriteBitString(ReadOnlySpan.Empty, 0, contextTag); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, TagClass.Universal, 3, "030100")] + [InlineData(AsnEncodingRules.CER, TagClass.Universal, 3, "030100")] + [InlineData(AsnEncodingRules.DER, TagClass.Universal, 3, "030100")] + [InlineData(AsnEncodingRules.BER, TagClass.Private, 1, "C10100")] + [InlineData(AsnEncodingRules.CER, TagClass.Application, 5, "450100")] + [InlineData(AsnEncodingRules.DER, TagClass.ContextSpecific, 32, "9F200100")] + public static void EmptyData_Allows0UnusedBits( + AsnEncodingRules ruleSet, + TagClass tagClass, + int tagValue, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (tagClass == TagClass.Universal) + { + Debug.Assert(tagValue == (int)UniversalTagNumber.BitString); + writer.WriteBitString(ReadOnlySpan.Empty, 0); + } + else + { + writer.WriteBitString(ReadOnlySpan.Empty, 0, new Asn1Tag(tagClass, tagValue)); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteBitString_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(ReadOnlySpan.Empty, tag: Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(new byte[1], tag: Asn1Tag.Null)); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs new file mode 100644 index 00000000000000..b558b84737e288 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteBoolean : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER, false, "010100")] + [InlineData(AsnEncodingRules.BER, true, "0101FF")] + [InlineData(AsnEncodingRules.CER, false, "010100")] + [InlineData(AsnEncodingRules.CER, true, "0101FF")] + [InlineData(AsnEncodingRules.DER, false, "010100")] + [InlineData(AsnEncodingRules.DER, true, "0101FF")] + public void VerifyWriteBoolean(AsnEncodingRules ruleSet, bool value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false, "830100")] + [InlineData(AsnEncodingRules.BER, true, "8301FF")] + [InlineData(AsnEncodingRules.CER, false, "830100")] + [InlineData(AsnEncodingRules.CER, true, "8301FF")] + [InlineData(AsnEncodingRules.DER, false, "830100")] + [InlineData(AsnEncodingRules.DER, true, "8301FF")] + public void VerifyWriteBoolean_Context3(AsnEncodingRules ruleSet, bool value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value, new Asn1Tag(TagClass.ContextSpecific, 3)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.DER, true)] + public void VerifyWriteBoolean_Null(AsnEncodingRules ruleSet, bool value) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBoolean(value, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.DER, true)] + public void VerifyWriteBoolean_ConstructedIgnored(AsnEncodingRules ruleSet, bool value) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value, new Asn1Tag(TagClass.ContextSpecific, 7, true)); + writer.WriteBoolean(value, new Asn1Tag(UniversalTagNumber.Boolean, true)); + + if (value) + { + Verify(writer, "8701FF0101FF"); + } + else + { + Verify(writer, "870100010100"); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs new file mode 100644 index 00000000000000..c29905370c8547 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs @@ -0,0 +1,421 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public abstract class WriteCharacterString : Asn1WriterTests + { + internal abstract void WriteString(AsnWriter writer, string s); + internal abstract void WriteString(AsnWriter writer, Asn1Tag tag, string s); + + internal abstract void WriteSpan(AsnWriter writer, ReadOnlySpan s); + internal abstract void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s); + + internal abstract Asn1Tag StandardTag { get; } + + protected const string GettysburgAddress = + "Four score and seven years ago our fathers brought forth on this continent, a new nation, " + + "conceived in Liberty, and dedicated to the proposition that all men are created equal.\r\n" + + "\r\n" + + "Now we are engaged in a great civil war, testing whether that nation, or any nation so " + + "conceived and so dedicated, can long endure. We are met on a great battle-field of that " + + "war. We have come to dedicate a portion of that field, as a final resting place for those " + + "who here gave their lives that that nation might live. It is altogether fitting and proper " + + "that we should do this.\r\n" + + "\r\n" + + "But, in a larger sense, we can not dedicate-we can not consecrate-we can not hallow-this " + + "ground. The brave men, living and dead, who struggled here, have consecrated it, far above " + + "our poor power to add or detract. The world will little note, nor long remember what we say " + + "here, but it can never forget what they did here. It is for us the living, rather, to be " + + "dedicated here to the unfinished work which they who fought here have thus far so nobly " + + "advanced. It is rather for us to be here dedicated to the great task remaining before " + + "us-that from these honored dead we take increased devotion to that cause for which they " + + "gave the last full measure of devotion-that we here highly resolve that these dead shall " + + "not have died in vain-that this nation, under God, shall have a new birth of freedom-and " + + "that government of the people, by the people, for the people, shall not perish from the earth."; + + protected void VerifyWrite_BER_String_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_String_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 14); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 19); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 2); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, int.MaxValue >> 1); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 30); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 31); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 1701, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_String_Null_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "value", + () => WriteString(writer, null)); + } + + protected void VerifyWrite_String_Null_CustomTag_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "value", + () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); + } + + protected void VerifyWrite_Null_String_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => WriteString(writer, Asn1Tag.Null, "hi")); + } + + protected void VerifyWrite_Null_Span_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => WriteSpan(writer, Asn1Tag.Null, "hi".AsSpan())); + } + + private void VerifyWrite_CERSegmented_Helper(AsnWriter writer, string tagHex, int contentByteCount) + { + int div = Math.DivRem(contentByteCount, 1000, out int rem); + + // tag, length(80), div full segments at 1004 bytes each, and the end of contents. + int encodedSize = (tagHex.Length / 2) + 1 + 1004 * div + 2; + + if (rem != 0) + { + // tag, contents (length TBD) + encodedSize += 1 + rem; + + if (encodedSize < 0x80) + encodedSize++; + else if (encodedSize <= 0xFF) + encodedSize += 2; + else + encodedSize += 3; + } + + byte[] encoded = writer.Encode(); + + Assert.Equal(tagHex, encoded.AsSpan(0, tagHex.Length / 2).ByteArrayToHex()); + Assert.Equal("0000", encoded.AsSpan(encoded.Length - 2).ByteArrayToHex()); + Assert.Equal(encodedSize, encoded.Length); + } + + protected void VerifyWrite_CERSegmented_String_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteString(writer, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_String_CustomTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); + string tagHex = Stringify(tag); + + WriteString(writer, tag, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_String_ConstructedTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteString(writer, tag, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_String_CustomPrimitiveTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); + Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); + string tagHex = Stringify(constr); + + WriteString(writer, prim, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_CustomTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, tag, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_ConstructedTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, tag, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); + Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); + string tagHex = Stringify(constr); + + WriteSpan(writer, prim, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_String_NonEncodable_Helper(string input) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Assert.Throws(() => WriteString(writer, input)); + } + + protected void VerifyWrite_Span_NonEncodable_Helper(string input) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Assert.Throws(() => WriteSpan(writer, input.AsSpan())); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs new file mode 100644 index 00000000000000..03c1e92ffe59e5 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteEncodedValue : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyNoEmptyValues(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "value", + () => writer.WriteEncodedValue(ReadOnlySpan.Empty)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyCurrentEncodingMattersForLength(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + byte[] wideNull = { 0x05, 0x81, 0x00 }; + + if (ruleSet == AsnEncodingRules.BER) + { + writer.WriteEncodedValue(wideNull); + Verify(writer, "058100"); + } + else + { + AssertExtensions.Throws( + "value", + () => writer.WriteEncodedValue(wideNull)); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyTrailingBytesDisallowed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + byte[] nullAndARogueByte = { 0x05, 0x00, 0x00 }; + + AssertExtensions.Throws( + "value", + () => writer.WriteEncodedValue(nullAndARogueByte)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteComplexEncodedValue(AsnEncodingRules ruleSet) + { + string inputHex = + "3082027A020100300D06092A864886F70D010101050004820264308202600201" + + "0002818200BCACB1A5349D7B35A580AC3B3998EB15EBF900ECB329BF1F75717A" + + "00B2199C8A18D791B592B7EC52BD5AF2DB0D3B635F0595753DFF7BA7C9872DBF" + + "7E3226DEF44A07CA568D1017992C2B41BFE5EC3570824CF1F4B15919FED513FD" + + "A56204AF2034A2D08FF04C2CCA49D168FA03FA2FA32FCCD3484C15F0A2E5467C" + + "76FC760B5509020301000102818110D7A0A704F69EE247D31FECCC84324E1B69" + + "B7B3A97DAB4639636716EB4F1E7A7463BFE9D3BE4FDE05F1B9B6A4AC7DBF247E" + + "364051CF5DC7BF65ADCFABD5ECF6A2B627171F6798541F1BF11CAC9AA56A6B2B" + + "C9C1082616651AB1AE6C02E10C7C8802C24A6B4D181087FD241D0753782CF4CD" + + "0355F8FD15791B49C90022BE3CE45502410E15300A9D34BA37B6BDA831BC6727" + + "B2F7F6D0EFB7B33A99C9AF28CFD625E245A54F251B784C4791ADA585ADB711D9" + + "300A3D52B450CC307F55D31E1217B9FFD74502410D65C60DE8B6F54A7756FD1C" + + "CBA76CE41EF446D024031EE9C5A40931B07336CFED35A8EE580E19DB8592CB0F" + + "266EC69028EB9E98E3E84FF1A459A8A26860A610F502410D9DB4BE7E730D9D72" + + "A57B2AE3738571C7C82F09A7BEB5E91D94AACC10CCBE33027B3C708BE68CC830" + + "71BA87545B00782F5E4D49A4595886B56F9342810848725502410CF6FBDDE1E1" + + "8B2570AF2169883A90C9809AEB1BE87D8CA0B4BDB497FD24C15A1D36DC2F29CF" + + "1B7EAF980A20B31467DA817EE18F1A9D691F71E7C1A4C8551EDF310241010CE9" + + "936E96FBADF87240CC419D01081BB67C981D44314E58583AC7FE9379EA0272E6" + + "C4C7C14638E1D5ECE7840DDB15A12D7054A418F8764FA54CE134EBD2635E"; + + string expectedOutput = "30820282" + "A082027E" + inputHex; + + byte[] input = inputHex.HexToByteArray(); + AsnWriter writer = new AsnWriter(ruleSet); + + using (writer.PushSequence()) + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + writer.WriteEncodedValue(input); + } + + Verify(writer, expectedOutput); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs new file mode 100644 index 00000000000000..2ea07868854bbd --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs @@ -0,0 +1,373 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Formats.Asn1.Tests.Reader; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteEnumerated : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.SByteBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.SByteBacked.Pillow, true, "9E01EF")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.SByteBacked.Fluff, false, "0A0153")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.SByteBacked.Fluff, true, "9E0153")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.SByteBacked)(-127), true, "9E0181")] + public void VerifyWriteEnumerated_SByte( + AsnEncodingRules ruleSet, + ReadEnumerated.SByteBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 30)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ByteBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.ByteBacked.NotFluffy, true, "9A010B")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.ByteBacked.Fluff, false, "0A010C")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ByteBacked.Fluff, true, "9A010C")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ByteBacked)253, false, "0A0200FD")] + public void VerifyWriteEnumerated_Byte( + AsnEncodingRules ruleSet, + ReadEnumerated.ByteBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 26)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ShortBacked.Zero, true, "DF81540100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.ShortBacked.Pillow, true, "DF815402FC00")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.ShortBacked.Fluff, false, "0A020209")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ShortBacked.Fluff, true, "DF8154020209")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ShortBacked)25321, false, "0A0262E9")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ShortBacked)(-12345), false, "0A02CFC7")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ShortBacked)(-1), true, "DF815401FF")] + public void VerifyWriteEnumerated_Short( + AsnEncodingRules ruleSet, + ReadEnumerated.ShortBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Private, 212)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, true, "4D0100")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.UShortBacked.Fluff, false, "0A03008000")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UShortBacked.Fluff, true, "4D03008000")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.UShortBacked)11, false, "0A010B")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.UShortBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.UShortBacked)ushort.MaxValue, true, "4D0300FFFF")] + public void VerifyWriteEnumerated_UShort( + AsnEncodingRules ruleSet, + ReadEnumerated.UShortBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Application, 13)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.IntBacked.Zero, true, "5F81FF7F0100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.IntBacked.Pillow, true, "5F81FF7F03FEFFFF")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.IntBacked.Fluff, false, "0A03010001")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.IntBacked.Fluff, true, "5F81FF7F03010001")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.IntBacked)25321, false, "0A0262E9")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.IntBacked)(-12345), false, "0A02CFC7")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.IntBacked)(-1), true, "5F81FF7F01FF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.IntBacked)int.MinValue, true, "5F81FF7F0480000000")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.IntBacked)int.MaxValue, false, "0A047FFFFFFF")] + public void VerifyWriteEnumerated_Int( + AsnEncodingRules ruleSet, + ReadEnumerated.IntBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Application, short.MaxValue)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, true, "9F610100")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.UIntBacked.Fluff, false, "0A050080000005")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UIntBacked.Fluff, true, "9F61050080000005")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.UIntBacked)11, false, "0A010B")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.UIntBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.UIntBacked)ushort.MaxValue, true, "9F610300FFFF")] + public void VerifyWriteEnumerated_UInt( + AsnEncodingRules ruleSet, + ReadEnumerated.UIntBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 97)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.LongBacked.Zero, true, "800100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.LongBacked.Pillow, true, "8005FF00000000")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.LongBacked.Fluff, false, "0A050200000441")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.LongBacked.Fluff, true, "80050200000441")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.LongBacked)25321, false, "0A0262E9")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.LongBacked)(-12345), false, "0A02CFC7")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.LongBacked)(-1), true, "8001FF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.LongBacked)int.MinValue, true, "800480000000")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.LongBacked)int.MaxValue, false, "0A047FFFFFFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.LongBacked)long.MinValue, false, "0A088000000000000000")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.LongBacked)long.MaxValue, true, "80087FFFFFFFFFFFFFFF")] + public void VerifyWriteEnumerated_Long( + AsnEncodingRules ruleSet, + ReadEnumerated.LongBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 0)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, true, "C10100")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.ULongBacked.Fluff, false, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ULongBacked.Fluff, true, "C10900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ULongBacked)11, false, "0A010B")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.ULongBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.ULongBacked)ushort.MaxValue, true, "C10300FFFF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ULongBacked)long.MaxValue, true, "C1087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.ULongBacked)ulong.MaxValue, false, "0A0900FFFFFFFFFFFFFFFF")] + public void VerifyWriteEnumerated_ULong( + AsnEncodingRules ruleSet, + ReadEnumerated.ULongBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Private, 1)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyFlagsBased(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue(OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue( + OpenFlags.IncludeArchived, + new Asn1Tag(TagClass.ContextSpecific, 13))); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue((Enum)OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue( + (Enum)OpenFlags.IncludeArchived, + new Asn1Tag(TagClass.ContextSpecific, 13))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue(ReadEnumerated.IntBacked.Pillow, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue((Enum)ReadEnumerated.IntBacked.Pillow, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_NonNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "value", + () => writer.WriteEnumeratedValue(null)); + + AssertExtensions.Throws( + "value", + () => writer.WriteEnumeratedValue( + null, + new Asn1Tag(TagClass.ContextSpecific, 1))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.UIntBacked.Fluff); + + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.SByteBacked.Fluff); + + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.ULongBacked.Fluff); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object_WithTag(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.UIntBacked.Fluff, tag); + + tag = new Asn1Tag(TagClass.Private, 4); + + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.SByteBacked.Fluff, tag); + + tag = new Asn1Tag(TagClass.Application, 75); + + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.ULongBacked.Fluff, tag); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteEnumeratedValue_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteEnumeratedValue( + ReadEnumerated.ULongBacked.Fluff, + new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true)); + + writer.WriteEnumeratedValue( + (Enum)ReadEnumerated.SByteBacked.Fluff, + new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + + Verify(writer, "0A0900FACEF00DCAFEBEEF" + "800153"); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs similarity index 58% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs index c195061c5ca4fc..715f5edac31681 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteGeneralizedTime : Asn1WriterTests { @@ -135,12 +134,10 @@ public void VerifyWriteGeneralizedTime_BER( bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteGeneralizedTime(input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteGeneralizedTime(input, omitFractionalSeconds: omitFractionalSeconds); - Verify(writer, "18" + expectedHexPayload); - } + Verify(writer, "18" + expectedHexPayload); } [Theory] @@ -150,13 +147,11 @@ public void VerifyWriteGeneralizedTime_BER_CustomTag( bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); - writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); + writer.WriteGeneralizedTime(input, omitFractionalSeconds, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + Verify(writer, Stringify(tag) + expectedHexPayload); } [Theory] @@ -166,12 +161,10 @@ public void VerifyWriteGeneralizedTime_CER( bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteGeneralizedTime(input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteGeneralizedTime(input, omitFractionalSeconds); - Verify(writer, "18" + expectedHexPayload); - } + Verify(writer, "18" + expectedHexPayload); } [Theory] @@ -181,13 +174,11 @@ public void VerifyWriteGeneralizedTime_CER_CustomTag( bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); - writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); + writer.WriteGeneralizedTime(input, omitFractionalSeconds, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + Verify(writer, Stringify(tag) + expectedHexPayload); } [Theory] @@ -197,12 +188,10 @@ public void VerifyWriteGeneralizedTime_DER( bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteGeneralizedTime(input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteGeneralizedTime(input, omitFractionalSeconds); - Verify(writer, "18" + expectedHexPayload); - } + Verify(writer, "18" + expectedHexPayload); } [Theory] @@ -212,86 +201,43 @@ public void VerifyWriteGeneralizedTime_DER_CustomTag( bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteGeneralizedTime(input, omitFractionalSeconds, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + Verify(writer, Stringify(tag) + expectedHexPayload); } [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, true)] - public void VerifyWriteGeneralizedTime_EndOfContents( - PublicEncodingRules ruleSet, + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, true)] + public void VerifyWriteGeneralizedTime_Null( + AsnEncodingRules ruleSet, bool omitFractionalSeconds) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteGeneralizedTime(Asn1Tag.EndOfContents, DateTimeOffset.Now, omitFractionalSeconds)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteGeneralizedTime_IgnoresConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); + AsnWriter writer = new AsnWriter(ruleSet); - writer.WriteGeneralizedTime(new Asn1Tag(UniversalTagNumber.GeneralizedTime, true), value); - writer.WriteGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 3, true), value); - Verify(writer, "180F32303137313131363137333530315A" + "830F32303137313131363137333530315A"); - } + AssertExtensions.Throws( + "tag", + () => writer.WriteGeneralizedTime(DateTimeOffset.Now, omitFractionalSeconds, Asn1Tag.Null)); } [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteGeneralizedTime_IgnoresConstructed(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteGeneralizedTime(DateTimeOffset.UtcNow)); - - Assert.Throws( - () => writer.WriteGeneralizedTime(DateTimeOffset.UtcNow, true)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteGeneralizedTime(Asn1Tag.Integer, DateTimeOffset.Now)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteGeneralizedTime(Asn1Tag.Integer, DateTimeOffset.Now, true)); - - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 18); - - Assert.Throws( - () => writer.WriteGeneralizedTime(tag, DateTimeOffset.Now)); + AsnWriter writer = new AsnWriter(ruleSet); + DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); - Assert.Throws( - () => writer.WriteGeneralizedTime(tag, DateTimeOffset.Now, true)); - } + writer.WriteGeneralizedTime(value, tag: new Asn1Tag(UniversalTagNumber.GeneralizedTime, true)); + writer.WriteGeneralizedTime(value, tag: new Asn1Tag(TagClass.ContextSpecific, 3, true)); + Verify(writer, "180F32303137313131363137333530315A" + "830F32303137313131363137333530315A"); } } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs similarity index 87% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs index 607ef4c7fafa82..31165b91a51c12 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteIA5String : WriteCharacterString { @@ -61,13 +60,13 @@ internal override void WriteString(AsnWriter writer, string s) => writer.WriteCharacterString(UniversalTagNumber.IA5String, s); internal override void WriteString(AsnWriter writer, Asn1Tag tag, string s) => - writer.WriteCharacterString(tag, UniversalTagNumber.IA5String, s); + writer.WriteCharacterString(UniversalTagNumber.IA5String, s, tag); internal override void WriteSpan(AsnWriter writer, ReadOnlySpan s) => writer.WriteCharacterString(UniversalTagNumber.IA5String, s); internal override void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s) => - writer.WriteCharacterString(tag, UniversalTagNumber.IA5String, s); + writer.WriteCharacterString(UniversalTagNumber.IA5String, s, tag); internal override Asn1Tag StandardTag => new Asn1Tag(UniversalTagNumber.IA5String); @@ -208,32 +207,32 @@ public void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, strin base.VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(input, expectedPayloadHex); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_String_Null(PublicEncodingRules ruleSet) => + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_String_Null(AsnEncodingRules ruleSet) => base.VerifyWrite_String_Null_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) => + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_String_Null_CustomTag(AsnEncodingRules ruleSet) => base.VerifyWrite_String_Null_CustomTag_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) => - base.VerifyWrite_EndOfContents_String_Helper(ruleSet); + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_Null_String(AsnEncodingRules ruleSet) => + base.VerifyWrite_Null_String_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) => - base.VerifyWrite_EndOfContents_Span_Helper(ruleSet); + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_Null_Span(AsnEncodingRules ruleSet) => + base.VerifyWrite_Null_Span_Helper(ruleSet); [Theory] [MemberData(nameof(CERSegmentedCases))] @@ -284,17 +283,5 @@ public void VerifyWrite_String_NonEncodable(string input) => [MemberData(nameof(InvalidInputs))] public void VerifyWrite_Span_NonEncodable(string input) => base.VerifyWrite_Span_NonEncodable_Helper(input); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_Span(bool empty) => - base.WriteAfterDispose_Span_Helper(empty); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_String(bool empty) => - base.WriteAfterDispose_String_Helper(empty); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs new file mode 100644 index 00000000000000..7ea14a99ccf2ce --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs @@ -0,0 +1,454 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteInteger : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "020100")] + [InlineData(AsnEncodingRules.CER, 0, "020100")] + [InlineData(AsnEncodingRules.DER, 0, "020100")] + [InlineData(AsnEncodingRules.BER, -1, "0201FF")] + [InlineData(AsnEncodingRules.CER, -1, "0201FF")] + [InlineData(AsnEncodingRules.DER, -1, "0201FF")] + [InlineData(AsnEncodingRules.BER, -2, "0201FE")] + [InlineData(AsnEncodingRules.DER, sbyte.MinValue, "020180")] + [InlineData(AsnEncodingRules.BER, sbyte.MinValue + 1, "020181")] + [InlineData(AsnEncodingRules.CER, sbyte.MinValue - 1, "0202FF7F")] + [InlineData(AsnEncodingRules.DER, sbyte.MinValue - 2, "0202FF7E")] + [InlineData(AsnEncodingRules.BER, -256, "0202FF00")] + [InlineData(AsnEncodingRules.CER, -257, "0202FEFF")] + [InlineData(AsnEncodingRules.DER, short.MinValue, "02028000")] + [InlineData(AsnEncodingRules.BER, short.MinValue + 1, "02028001")] + [InlineData(AsnEncodingRules.CER, short.MinValue + byte.MaxValue, "020280FF")] + [InlineData(AsnEncodingRules.DER, short.MinValue - 1, "0203FF7FFF")] + [InlineData(AsnEncodingRules.BER, short.MinValue - 2, "0203FF7FFE")] + [InlineData(AsnEncodingRules.CER, -65281, "0203FF00FF")] + [InlineData(AsnEncodingRules.DER, -8388608, "0203800000")] + [InlineData(AsnEncodingRules.BER, -8388607, "0203800001")] + [InlineData(AsnEncodingRules.CER, -8388609, "0204FF7FFFFF")] + [InlineData(AsnEncodingRules.DER, -16777216, "0204FF000000")] + [InlineData(AsnEncodingRules.BER, -16777217, "0204FEFFFFFF")] + [InlineData(AsnEncodingRules.CER, int.MinValue, "020480000000")] + [InlineData(AsnEncodingRules.DER, int.MinValue + 1, "020480000001")] + [InlineData(AsnEncodingRules.BER, (long)int.MinValue - 1, "0205FF7FFFFFFF")] + [InlineData(AsnEncodingRules.CER, (long)int.MinValue - 2, "0205FF7FFFFFFE")] + [InlineData(AsnEncodingRules.DER, -4294967296, "0205FF00000000")] + [InlineData(AsnEncodingRules.BER, -4294967295, "0205FF00000001")] + [InlineData(AsnEncodingRules.CER, -4294967294, "0205FF00000002")] + [InlineData(AsnEncodingRules.DER, -4294967297, "0205FEFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, -549755813888, "02058000000000")] + [InlineData(AsnEncodingRules.CER, -549755813887, "02058000000001")] + [InlineData(AsnEncodingRules.DER, -549755813889, "0206FF7FFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, -549755813890, "0206FF7FFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, -140737488355328, "0206800000000000")] + [InlineData(AsnEncodingRules.DER, -140737488355327, "0206800000000001")] + [InlineData(AsnEncodingRules.BER, -140737488355329, "0207FF7FFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, -281474976710656, "0207FF000000000000")] + [InlineData(AsnEncodingRules.DER, -281474976710655, "0207FF000000000001")] + [InlineData(AsnEncodingRules.BER, -281474976710657, "0207FEFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, -36028797018963968, "020780000000000000")] + [InlineData(AsnEncodingRules.DER, -36028797018963967, "020780000000000001")] + [InlineData(AsnEncodingRules.DER, -36028797018963969, "0208FF7FFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, -36028797018963970, "0208FF7FFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, -72057594037927936, "0208FF00000000000000")] + [InlineData(AsnEncodingRules.DER, -72057594037927935, "0208FF00000000000001")] + [InlineData(AsnEncodingRules.BER, -72057594037927937, "0208FEFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, long.MinValue + 1, "02088000000000000001")] + [InlineData(AsnEncodingRules.DER, long.MinValue, "02088000000000000000")] + [InlineData(AsnEncodingRules.BER, 1, "020101")] + [InlineData(AsnEncodingRules.CER, 127, "02017F")] + [InlineData(AsnEncodingRules.DER, 126, "02017E")] + [InlineData(AsnEncodingRules.BER, 128, "02020080")] + [InlineData(AsnEncodingRules.CER, 129, "02020081")] + [InlineData(AsnEncodingRules.DER, 254, "020200FE")] + [InlineData(AsnEncodingRules.BER, 255, "020200FF")] + [InlineData(AsnEncodingRules.CER, 256, "02020100")] + [InlineData(AsnEncodingRules.DER, 32767, "02027FFF")] + [InlineData(AsnEncodingRules.BER, 32766, "02027FFE")] + [InlineData(AsnEncodingRules.CER, 32768, "0203008000")] + [InlineData(AsnEncodingRules.DER, 32769, "0203008001")] + [InlineData(AsnEncodingRules.BER, 65535, "020300FFFF")] + [InlineData(AsnEncodingRules.CER, 65534, "020300FFFE")] + [InlineData(AsnEncodingRules.DER, 65536, "0203010000")] + [InlineData(AsnEncodingRules.BER, 65537, "0203010001")] + [InlineData(AsnEncodingRules.CER, 8388607, "02037FFFFF")] + [InlineData(AsnEncodingRules.DER, 8388606, "02037FFFFE")] + [InlineData(AsnEncodingRules.BER, 8388608, "020400800000")] + [InlineData(AsnEncodingRules.CER, 8388609, "020400800001")] + [InlineData(AsnEncodingRules.DER, 16777215, "020400FFFFFF")] + [InlineData(AsnEncodingRules.BER, 16777214, "020400FFFFFE")] + [InlineData(AsnEncodingRules.CER, 16777216, "020401000000")] + [InlineData(AsnEncodingRules.DER, 16777217, "020401000001")] + [InlineData(AsnEncodingRules.BER, 2147483647, "02047FFFFFFF")] + [InlineData(AsnEncodingRules.CER, 2147483646, "02047FFFFFFE")] + [InlineData(AsnEncodingRules.DER, 2147483648, "02050080000000")] + [InlineData(AsnEncodingRules.BER, 2147483649, "02050080000001")] + [InlineData(AsnEncodingRules.BER, 4294967295, "020500FFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 4294967294, "020500FFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 4294967296, "02050100000000")] + [InlineData(AsnEncodingRules.BER, 4294967297, "02050100000001")] + [InlineData(AsnEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 549755813888, "0206008000000000")] + [InlineData(AsnEncodingRules.CER, 549755813889, "0206008000000001")] + [InlineData(AsnEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 1099511627776, "0206010000000000")] + [InlineData(AsnEncodingRules.DER, 1099511627777, "0206010000000001")] + [InlineData(AsnEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 140737488355328, "020700800000000000")] + [InlineData(AsnEncodingRules.BER, 140737488355329, "020700800000000001")] + [InlineData(AsnEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 281474976710656, "020701000000000000")] + [InlineData(AsnEncodingRules.CER, 281474976710657, "020701000000000001")] + [InlineData(AsnEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 36028797018963968, "02080080000000000000")] + [InlineData(AsnEncodingRules.DER, 36028797018963969, "02080080000000000001")] + [InlineData(AsnEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 72057594037927936, "02080100000000000000")] + [InlineData(AsnEncodingRules.BER, 72057594037927937, "02080100000000000001")] + [InlineData(AsnEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Long(AsnEncodingRules ruleSet, long value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "020100")] + [InlineData(AsnEncodingRules.CER, 0, "020100")] + [InlineData(AsnEncodingRules.DER, 0, "020100")] + [InlineData(AsnEncodingRules.BER, 1, "020101")] + [InlineData(AsnEncodingRules.CER, 127, "02017F")] + [InlineData(AsnEncodingRules.DER, 126, "02017E")] + [InlineData(AsnEncodingRules.BER, 128, "02020080")] + [InlineData(AsnEncodingRules.CER, 129, "02020081")] + [InlineData(AsnEncodingRules.DER, 254, "020200FE")] + [InlineData(AsnEncodingRules.BER, 255, "020200FF")] + [InlineData(AsnEncodingRules.CER, 256, "02020100")] + [InlineData(AsnEncodingRules.DER, 32767, "02027FFF")] + [InlineData(AsnEncodingRules.BER, 32766, "02027FFE")] + [InlineData(AsnEncodingRules.CER, 32768, "0203008000")] + [InlineData(AsnEncodingRules.DER, 32769, "0203008001")] + [InlineData(AsnEncodingRules.BER, 65535, "020300FFFF")] + [InlineData(AsnEncodingRules.CER, 65534, "020300FFFE")] + [InlineData(AsnEncodingRules.DER, 65536, "0203010000")] + [InlineData(AsnEncodingRules.BER, 65537, "0203010001")] + [InlineData(AsnEncodingRules.CER, 8388607, "02037FFFFF")] + [InlineData(AsnEncodingRules.DER, 8388606, "02037FFFFE")] + [InlineData(AsnEncodingRules.BER, 8388608, "020400800000")] + [InlineData(AsnEncodingRules.CER, 8388609, "020400800001")] + [InlineData(AsnEncodingRules.DER, 16777215, "020400FFFFFF")] + [InlineData(AsnEncodingRules.BER, 16777214, "020400FFFFFE")] + [InlineData(AsnEncodingRules.CER, 16777216, "020401000000")] + [InlineData(AsnEncodingRules.DER, 16777217, "020401000001")] + [InlineData(AsnEncodingRules.BER, 2147483647, "02047FFFFFFF")] + [InlineData(AsnEncodingRules.CER, 2147483646, "02047FFFFFFE")] + [InlineData(AsnEncodingRules.DER, 2147483648, "02050080000000")] + [InlineData(AsnEncodingRules.BER, 2147483649, "02050080000001")] + [InlineData(AsnEncodingRules.BER, 4294967295, "020500FFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 4294967294, "020500FFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 4294967296, "02050100000000")] + [InlineData(AsnEncodingRules.BER, 4294967297, "02050100000001")] + [InlineData(AsnEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 549755813888, "0206008000000000")] + [InlineData(AsnEncodingRules.CER, 549755813889, "0206008000000001")] + [InlineData(AsnEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 1099511627776, "0206010000000000")] + [InlineData(AsnEncodingRules.DER, 1099511627777, "0206010000000001")] + [InlineData(AsnEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 140737488355328, "020700800000000000")] + [InlineData(AsnEncodingRules.BER, 140737488355329, "020700800000000001")] + [InlineData(AsnEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 281474976710656, "020701000000000000")] + [InlineData(AsnEncodingRules.CER, 281474976710657, "020701000000000001")] + [InlineData(AsnEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 36028797018963968, "02080080000000000000")] + [InlineData(AsnEncodingRules.DER, 36028797018963969, "02080080000000000001")] + [InlineData(AsnEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 72057594037927936, "02080100000000000000")] + [InlineData(AsnEncodingRules.BER, 72057594037927937, "02080100000000000001")] + [InlineData(AsnEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 9223372036854775808, "0209008000000000000000")] + [InlineData(AsnEncodingRules.CER, 9223372036854775809, "0209008000000000000001")] + [InlineData(AsnEncodingRules.DER, ulong.MaxValue, "020900FFFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, ulong.MaxValue-1, "020900FFFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_ULong(AsnEncodingRules ruleSet, ulong value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0", "020100")] + [InlineData(AsnEncodingRules.CER, "127", "02017F")] + [InlineData(AsnEncodingRules.DER, "128", "02020080")] + [InlineData(AsnEncodingRules.BER, "32767", "02027FFF")] + [InlineData(AsnEncodingRules.CER, "32768", "0203008000")] + [InlineData(AsnEncodingRules.DER, "9223372036854775807", "02087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, "9223372036854775808", "0209008000000000000000")] + [InlineData(AsnEncodingRules.CER, "18446744073709551615", "020900FFFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, "18446744073709551616", "0209010000000000000000")] + [InlineData(AsnEncodingRules.BER, "1339673755198158349044581307228491520", "02100102030405060708090A0B0C0D0E0F00")] + [InlineData(AsnEncodingRules.CER, "320182027492359845421654932427609477120", "021100F0E0D0C0B0A090807060504030201000")] + [InlineData(AsnEncodingRules.DER, "-1339673755198158349044581307228491520", "0210FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_BigInteger(AsnEncodingRules ruleSet, string decimalValue, string expectedHex) + { + BigInteger value = BigInteger.Parse(decimalValue); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "470100")] + [InlineData(AsnEncodingRules.CER, long.MinValue + 1, "47088000000000000001")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "47087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Application7_Long(AsnEncodingRules ruleSet, long value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.Application, 7)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "890100")] + [InlineData(AsnEncodingRules.CER, 9223372036854775809, "8909008000000000000001")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "89087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Context9_ULong(AsnEncodingRules ruleSet, ulong value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.ContextSpecific, 9)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0", "D00100")] + [InlineData(AsnEncodingRules.BER, "1339673755198158349044581307228491520", "D0100102030405060708090A0B0C0D0E0F00")] + [InlineData(AsnEncodingRules.CER, "320182027492359845421654932427609477120", "D01100F0E0D0C0B0A090807060504030201000")] + [InlineData(AsnEncodingRules.DER, "-1339673755198158349044581307228491520", "D010FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_Private16_BigInteger( + AsnEncodingRules ruleSet, + string decimalValue, + string expectedHex) + { + BigInteger value = BigInteger.Parse(decimalValue); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.Private, 16)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00")] + [InlineData("01")] + [InlineData("80")] + [InlineData("FF")] + [InlineData("0080")] + [InlineData("00FF")] + [InlineData("8000")] + [InlineData("00F0E0D0C0B0A090807060504030201000")] + [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_EncodedBytes(string valueHex) + { + string expectedHex = "02" + (valueHex.Length / 2).ToString("X2") + valueHex; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteInteger(valueHex.HexToByteArray()); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00")] + [InlineData("01")] + [InlineData("80")] + [InlineData("FF")] + [InlineData("0080")] + [InlineData("00FF")] + [InlineData("8000")] + [InlineData("00F0E0D0C0B0A090807060504030201000")] + [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_Context4_EncodedBytes(string valueHex) + { + string expectedHex = "84" + (valueHex.Length / 2).ToString("X2") + valueHex; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteInteger(valueHex.HexToByteArray(), new Asn1Tag(TagClass.ContextSpecific, 4)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00", false)] + [InlineData("7F", false)] + [InlineData("80", true)] + [InlineData("8002030405060708090A0B0C0D0E0F10", true)] + [InlineData("7F02030405060708090A0B0C0D0E0F10", false)] + [InlineData("FFFF", true)] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE", true)] + [InlineData("FF80", true)] + public void VerifyWriteUnsignedInteger(string valueHex, bool gainsPaddingByte) + { + int contentLength = (valueHex.Length / 2) + (gainsPaddingByte ? 1 : 0); + string expectedHex = $"02{contentLength:X2}{(gainsPaddingByte ? "00" : "")}{valueHex}"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteIntegerUnsigned(valueHex.HexToByteArray()); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00", false)] + [InlineData("7F", false)] + [InlineData("80", true)] + [InlineData("8002030405060708090A0B0C0D0E0F10", true)] + [InlineData("7F02030405060708090A0B0C0D0E0F10", false)] + [InlineData("FFFF", true)] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE", true)] + [InlineData("FF80", true)] + public void VerifyWriteUnsignedInteger_Private7(string valueHex, bool gainsPaddingByte) + { + int contentLength = (valueHex.Length / 2) + (gainsPaddingByte ? 1 : 0); + string expectedHex = $"C7{contentLength:X2}{(gainsPaddingByte ? "00" : "")}{valueHex}"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteIntegerUnsigned(valueHex.HexToByteArray(), new Asn1Tag(TagClass.Private, 7)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + [InlineData("FFFF")] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] + [InlineData("FF80")] + public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + AssertExtensions.Throws( + "value", + () => writer.WriteInteger(valueHex.HexToByteArray())); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + [InlineData("FFFF")] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] + [InlineData("FF80")] + public void VerifyWriteInteger_Application3_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); + + AssertExtensions.Throws( + "value", + () => writer.WriteInteger(valueHex.HexToByteArray(), tag)); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + public void VerifyWriteIntegerUnsigned_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + AssertExtensions.Throws( + "value", + () => writer.WriteIntegerUnsigned(valueHex.HexToByteArray())); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + public void VerifyWriteIntegerUnsigned_Application3_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); + + AssertExtensions.Throws( + "value", + () => writer.WriteIntegerUnsigned(valueHex.HexToByteArray(), tag)); + } + + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteInteger_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(0L, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(0UL, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(BigInteger.Zero, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteInteger_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(0L, new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true)); + writer.WriteInteger(0L, new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + writer.WriteInteger(0UL, new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true)); + writer.WriteInteger(0UL, new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + writer.WriteInteger(BigInteger.Zero, new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true)); + writer.WriteInteger(BigInteger.Zero, new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + + Verify(writer, "020100800100020100800100020100800100"); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs new file mode 100644 index 00000000000000..d6501f6b4e5ec4 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs @@ -0,0 +1,383 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Formats.Asn1.Tests.Reader; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteNamedBitList : Asn1WriterTests + { + [Theory] + [InlineData( + AsnEncodingRules.BER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.CER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.DER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.BER, + "0309000000000000000003", + ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] + [InlineData( + AsnEncodingRules.CER, + "0309010000000080000002", + ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] + [InlineData( + AsnEncodingRules.DER, + "030204B0", + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] + public static void VerifyWriteNamedBitList( + AsnEncodingRules ruleSet, + string expectedHex, + Enum value) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData( + AsnEncodingRules.BER, + "C00100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.CER, + "410100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.DER, + "820100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.BER, + "C009000000000000000003", + ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] + [InlineData( + AsnEncodingRules.CER, + "4109010000000080000002", + ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] + [InlineData( + AsnEncodingRules.DER, + "820204B0", + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] + public static void VerifyWriteNamedBitList_WithTag( + AsnEncodingRules ruleSet, + string expectedHex, + Enum value) + { + int ruleSetVal = (int)ruleSet; + TagClass tagClass = (TagClass)(byte)(ruleSetVal << 6); + + if (tagClass == TagClass.Universal) + tagClass = TagClass.Private; + + Asn1Tag tag = new Asn1Tag(tagClass, ruleSetVal); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(value, tag); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; + + genWriter.WriteNamedBitList(flagsValue); + objWriter.WriteNamedBitList((Enum)flagsValue); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic_WithTag(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; + + genWriter.WriteNamedBitList(flagsValue, tag); + objWriter.WriteNamedBitList((Enum)flagsValue, tag); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_NonNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((Enum)null)); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((Enum)null, new Asn1Tag(TagClass.ContextSpecific, 1))); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((BitArray)null)); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((BitArray)null, new Asn1Tag(TagClass.Private, 2))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_FlagsEnumRequired(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList(AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList( + AsnEncodingRules.BER, + new Asn1Tag(TagClass.ContextSpecific, 1))); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList((Enum)AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList( + (Enum)AsnEncodingRules.BER, + new Asn1Tag(TagClass.ContextSpecific, 1))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNamedBitList( + StringSplitOptions.RemoveEmptyEntries, + Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNamedBitList( + (Enum)StringSplitOptions.RemoveEmptyEntries, + Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + BitArray bits = new BitArray(18); + bits.Set(1, true); + bits.Set(15, true); + + writer.WriteNamedBitList(bits, new Asn1Tag(TagClass.Application, 4)); + + Verify(writer, "440406400100"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_Empty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + BitArray bits = new BitArray(0); + + writer.WriteNamedBitList(bits); + + Verify(writer, "030100"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_EveryPattern(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + byte[] allTheBytes = new byte[256]; + + for (int i = 0; i < allTheBytes.Length; i++) + { + allTheBytes[i] = (byte)i; + } + + BitArray bits = new BitArray(allTheBytes); + writer.WriteNamedBitList(bits, new Asn1Tag(TagClass.Private, 491)); + + const string ExpectedHex = + // Tag + "DF836B" + + // Length + "820101" + + // Unused bit count + "00" + + // Reversed bits for byte patterns 0x00-0x1F + "008040C020A060E0109050D030B070F0088848C828A868E8189858D838B878F8" + + // Reversed bits for byte patterns 0x20-0x3F + "048444C424A464E4149454D434B474F40C8C4CCC2CAC6CEC1C9C5CDC3CBC7CFC" + + // Reversed bits for byte patterns 0x40-0x5F + "028242C222A262E2129252D232B272F20A8A4ACA2AAA6AEA1A9A5ADA3ABA7AFA" + + // Reversed bits for byte patterns 0x60-0x7F + "068646C626A666E6169656D636B676F60E8E4ECE2EAE6EEE1E9E5EDE3EBE7EFE" + + // Reversed bits for byte patterns 0x80-0x9F + "018141C121A161E1119151D131B171F1098949C929A969E9199959D939B979F9" + + // Reversed bits for byte patterns 0xA0-0xBF + "058545C525A565E5159555D535B575F50D8D4DCD2DAD6DED1D9D5DDD3DBD7DFD" + + // Reversed bits for byte patterns 0xC0-0xDF + "038343C323A363E3139353D333B373F30B8B4BCB2BAB6BEB1B9B5BDB3BBB7BFB" + + // Reversed bits for byte patterns 0xE0-0xFF + "078747C727A767E7179757D737B777F70F8F4FCF2FAF6FEF1F9F5FDF3FBF7FFF"; + + Verify(writer, ExpectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_7992Bits(AsnEncodingRules ruleSet) + { + BitArray array = new BitArray(7992); + array.Set(4, true); + array.Set(7990, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array, new Asn1Tag(TagClass.ContextSpecific, 4)); + + string expectedHex = "848203E80008" + new string('0', 1994) + "02"; + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_7993Bits(AsnEncodingRules ruleSet) + { + BitArray array = new BitArray(7993); + array.Set(7992, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array, new Asn1Tag(TagClass.ContextSpecific, 5)); + + string expectedHex; + + if (ruleSet == AsnEncodingRules.CER) + { + expectedHex = "A580038203E8" + new string('0', 2000) + "03020780" + "0000"; + } + else + { + expectedHex = "858203E907" + new string('0', 1998) + "80"; + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_KeyUsage_OneByte(AsnEncodingRules ruleSet) + { + // KeyUsage ::= BIT STRING { + // digitalSignature (0), + // nonRepudiation (1), + // keyEncipherment (2), + // dataEncipherment (3), + // keyAgreement (4), + // keyCertSign (5), + // cRLSign (6), + // encipherOnly (7), + // decipherOnly (8) } + + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, + critical: false); + + BitArray array = new BitArray(7); + array.Set(6, true); + array.Set(5, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array); + + Verify(writer, kuExt.RawData.ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_KeyUsage_TwoByte(AsnEncodingRules ruleSet) + { + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyAgreement | X509KeyUsageFlags.DecipherOnly, + critical: false); + + BitArray array = new BitArray(9); + array.Set(4, true); + array.Set(8, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array); + + Verify(writer, kuExt.RawData.ByteArrayToHex()); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs new file mode 100644 index 00000000000000..659965c59af5eb --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteNull : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNull(); + + Verify(writer, "0500"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull_OctetString(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNull(Asn1Tag.PrimitiveOctetString)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNull(new Asn1Tag(TagClass.ContextSpecific, 7, true)); + writer.WriteNull(new Asn1Tag(UniversalTagNumber.Null, true)); + + Verify(writer, "87000500"); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs new file mode 100644 index 00000000000000..7616d3d1fd6281 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs @@ -0,0 +1,235 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteObjectIdentifier : Asn1WriterTests + { + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_String( + AsnEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier(oidValue); + + Verify(writer, expectedHex); + } + + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_Span( + AsnEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier(oidValue.AsSpan()); + + Verify(writer, expectedHex); + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_String( + string description, + AsnEncodingRules ruleSet, + string nonOidValue) + { + _ = description; + AsnWriter writer = new AsnWriter(ruleSet); + + if (nonOidValue == null) + { + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue)); + } + else + { + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue)); + } + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_Span( + string description, + AsnEncodingRules ruleSet, + string nonOidValue) + { + _ = description; + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue.AsSpan())); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_String(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier("1.3.14.3.2.26", new Asn1Tag(TagClass.ContextSpecific, 3)); + + Verify(writer, "83052B0E03021A"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_Span(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier("1.3.14.3.2.26".AsSpan(), new Asn1Tag(TagClass.Application, 2)); + + Verify(writer, "42052B0E03021A"); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WriteObjectIdentifier_NullString(bool defaultTag) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + + AssertExtensions.Throws( + "oidValue", + () => + { + if (defaultTag) + { + writer.WriteObjectIdentifier((string)null); + } + else + { + writer.WriteObjectIdentifier((string)null, new Asn1Tag(TagClass.ContextSpecific, 6)); + } + }); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier("1.1", Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier("1.1".AsSpan(), Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + const string OidValue = "1.1"; + Asn1Tag constructedOid = new Asn1Tag(UniversalTagNumber.ObjectIdentifier, isConstructed: true); + Asn1Tag constructedContext0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); + + writer.WriteObjectIdentifier(OidValue, constructedOid); + writer.WriteObjectIdentifier(OidValue, constructedContext0); + writer.WriteObjectIdentifier(OidValue.AsSpan(), constructedOid); + writer.WriteObjectIdentifier(OidValue.AsSpan(), constructedContext0); + + Verify(writer, "060129800129060129800129"); + } + + public static IEnumerable ValidOidData { get; } = + new object[][] + { + new object[] + { + AsnEncodingRules.BER, + "0.0", + "060100", + }, + new object[] + { + AsnEncodingRules.CER, + "1.0", + "060128", + }, + new object[] + { + AsnEncodingRules.DER, + "2.0", + "060150", + }, + new object[] + { + AsnEncodingRules.BER, + "1.3.14.3.2.26", + "06052B0E03021A", + }, + new object[] + { + AsnEncodingRules.CER, + "2.999.19427512891.25", + "06088837C8AFE1A43B19", + }, + new object[] + { + AsnEncodingRules.DER, + "1.2.840.113549.1.1.10", + "06092A864886F70D01010A", + }, + new object[] + { + // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and + // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + // this is + // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } + AsnEncodingRules.DER, + "2.25.329800735698586629295641978511506172918.3", + "06156983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", + }, + }; + + public static IEnumerable InvalidOidData { get; } = + new object[][] + { + new object[] { "Null", AsnEncodingRules.BER, null }, + new object[] { "Empty string", AsnEncodingRules.BER, "" }, + new object[] { "No period", AsnEncodingRules.CER, "1" }, + new object[] { "No second RID", AsnEncodingRules.DER, "1." }, + new object[] { "Invalid first RID", AsnEncodingRules.BER, "3.0" }, + new object[] { "Invalid first RID - multichar", AsnEncodingRules.CER, "27.0" }, + new object[] { "Double zero - First RID", AsnEncodingRules.DER, "00.0" }, + new object[] { "Leading zero - First RID", AsnEncodingRules.BER, "01.0" }, + new object[] { "Double zero - second RID", AsnEncodingRules.CER, "0.00" }, + new object[] { "Leading zero - second RID", AsnEncodingRules.DER, "0.01" }, + new object[] { "Double-period", AsnEncodingRules.BER, "0..0" }, + new object[] { "Ends with period - second RID", AsnEncodingRules.BER, "0.0." }, + new object[] { "Ends with period - third RID", AsnEncodingRules.BER, "0.1.30." }, + new object[] { "Double zero - third RID", AsnEncodingRules.CER, "0.1.00" }, + new object[] { "Leading zero - third RID", AsnEncodingRules.DER, "0.1.023" }, + new object[] { "Invalid character first position", AsnEncodingRules.BER, "a.1.23" }, + new object[] { "Invalid character second position", AsnEncodingRules.CER, "0,1.23" }, + new object[] { "Invalid character second rid", AsnEncodingRules.DER, "0.1q.23" }, + new object[] { "Invalid character third rid", AsnEncodingRules.BER, "0.1.23q" }, + }; + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs new file mode 100644 index 00000000000000..465df77e16897d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteOctetString : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteEmpty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(ReadOnlySpan.Empty); + + Verify(writer, "0400"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 1, "0401")] + [InlineData(AsnEncodingRules.CER, 2, "0402")] + [InlineData(AsnEncodingRules.DER, 3, "0403")] + [InlineData(AsnEncodingRules.BER, 126, "047E")] + [InlineData(AsnEncodingRules.CER, 127, "047F")] + [InlineData(AsnEncodingRules.DER, 128, "048180")] + [InlineData(AsnEncodingRules.BER, 1000, "048203E8")] + [InlineData(AsnEncodingRules.CER, 1000, "048203E8")] + [InlineData(AsnEncodingRules.DER, 1000, "048203E8")] + [InlineData(AsnEncodingRules.BER, 1001, "048203E9")] + [InlineData(AsnEncodingRules.DER, 1001, "048203E9")] + [InlineData(AsnEncodingRules.BER, 2001, "048207D1")] + [InlineData(AsnEncodingRules.DER, 2001, "048207D1")] + public void WritePrimitive(AsnEncodingRules ruleSet, int length, string hexStart) + { + string payloadHex = new string('0', 2 * length); + string expectedHex = hexStart + payloadHex; + byte[] data = new byte[length]; + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(data); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(1001, "2480048203E8", "0401")] + [InlineData(1999, "2480048203E8", "048203E7")] + [InlineData(2000, "2480048203E8", "048203E8")] + public void WriteSegmentedCER(int length, string hexStart, string hexStart2) + { + string payload1Hex = new string('8', 2000); + string payload2Hex = new string('8', (length - 1000) * 2); + string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; + byte[] data = new byte[length]; + + for (int i = 0; i < data.Length; i++) + { + data[i] = 0x88; + } + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteOctetString(data); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, false)] + [InlineData(AsnEncodingRules.CER, 0, false)] + [InlineData(AsnEncodingRules.DER, 0, false)] + [InlineData(AsnEncodingRules.BER, 999, false)] + [InlineData(AsnEncodingRules.CER, 999, false)] + [InlineData(AsnEncodingRules.DER, 999, false)] + [InlineData(AsnEncodingRules.BER, 1000, false)] + [InlineData(AsnEncodingRules.CER, 1000, false)] + [InlineData(AsnEncodingRules.DER, 1000, false)] + [InlineData(AsnEncodingRules.BER, 1001, false)] + [InlineData(AsnEncodingRules.CER, 1001, true)] + [InlineData(AsnEncodingRules.DER, 1001, false)] + [InlineData(AsnEncodingRules.BER, 1998, false)] + [InlineData(AsnEncodingRules.CER, 1998, true)] + [InlineData(AsnEncodingRules.DER, 1998, false)] + [InlineData(AsnEncodingRules.BER, 1999, false)] + [InlineData(AsnEncodingRules.CER, 1999, true)] + [InlineData(AsnEncodingRules.DER, 1999, false)] + [InlineData(AsnEncodingRules.BER, 2000, false)] + [InlineData(AsnEncodingRules.CER, 2000, true)] + [InlineData(AsnEncodingRules.DER, 2000, false)] + [InlineData(AsnEncodingRules.BER, 2001, false)] + [InlineData(AsnEncodingRules.CER, 2001, true)] + [InlineData(AsnEncodingRules.DER, 2001, false)] + [InlineData(AsnEncodingRules.BER, 4096, false)] + [InlineData(AsnEncodingRules.CER, 4096, true)] + [InlineData(AsnEncodingRules.DER, 4096, false)] + public void VerifyWriteOctetString_PrimitiveOrConstructed( + AsnEncodingRules ruleSet, + int payloadLength, + bool expectConstructed) + { + byte[] data = new byte[payloadLength]; + + Asn1Tag[] tagsToTry = + { + new Asn1Tag(UniversalTagNumber.OctetString), + new Asn1Tag(UniversalTagNumber.OctetString, isConstructed: true), + new Asn1Tag(TagClass.Private, 87), + new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), + }; + + byte[] answerBuf = new byte[payloadLength + 100]; + + foreach (Asn1Tag toTry in tagsToTry) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(data, toTry); + + Assert.True(writer.TryEncode(answerBuf, out _)); + Assert.True(Asn1Tag.TryDecode(answerBuf, out Asn1Tag writtenTag, out _)); + + if (expectConstructed) + { + Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + else + { + Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + + Assert.Equal(toTry.TagClass, writtenTag.TagClass); + Assert.Equal(toTry.TagValue, writtenTag.TagValue); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteOctetString_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(ReadOnlySpan.Empty, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(new byte[1], Asn1Tag.Null)); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs new file mode 100644 index 00000000000000..202622470dd00c --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs @@ -0,0 +1,201 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteUtcTime : Asn1WriterTests + { + public static IEnumerable TestCases { get; } = new object[][] + { + new object[] + { + new DateTimeOffset(2017, 10, 16, 8, 24, 3, TimeSpan.FromHours(-7)), + "0D3137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(1817, 10, 16, 21, 24, 3, TimeSpan.FromHours(6)), + "0D3137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero), + "0D3030303130313030303030305A", + }, + }; + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_BER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_BER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); + writer.WriteUtcTime(input, tag); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_CER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_CER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); + writer.WriteUtcTime(input, tag); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_DER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_DER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteUtcTime(input, tag); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteUtcTime_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteUtcTime(DateTimeOffset.Now, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteUtcTime_IgnoresConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); + + writer.WriteUtcTime(value, new Asn1Tag(UniversalTagNumber.UtcTime, true)); + writer.WriteUtcTime(value, new Asn1Tag(TagClass.ContextSpecific, 3, true)); + Verify(writer, "170D3137313131363137333530315A" + "830D3137313131363137333530315A"); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_RespectsYearMax_DER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Assert.Equal(0, writer.GetEncodedLength()); + + AssertExtensions.Throws( + "value", + () => writer.WriteUtcTime(input, input.Year - 1)); + + Assert.Equal(0, writer.GetEncodedLength()); + + writer.WriteUtcTime(input, input.Year); + Assert.Equal(15, writer.GetEncodedLength()); + + writer.WriteUtcTime(input, input.Year + 99); + Assert.Equal(30, writer.GetEncodedLength()); + + writer.Reset(); + + _ = expectedHexPayload; + AssertExtensions.Throws( + "value", + () => writer.WriteUtcTime(input, input.Year + 100)); + + Assert.Equal(0, writer.GetEncodedLength()); + } + + [Fact] + public void VerifyWriteUtcTime_RespectsYearMax_UniversalLimit() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 11); + + // 1950 after ToUniversal + writer.WriteUtcTime( + new DateTimeOffset(1949, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), + 2049, + tag); + + Assert.Equal(15, writer.GetEncodedLength()); + + // 1949 after ToUniversal + AssertExtensions.Throws( + "value", + () => + writer.WriteUtcTime( + new DateTimeOffset(1950, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), + 2049, + tag)); + + Assert.Equal(15, writer.GetEncodedLength()); + + // 2050 after ToUniversal + AssertExtensions.Throws( + "value", + () => + writer.WriteUtcTime( + new DateTimeOffset(2049, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), + 2049, + tag)); + + Assert.Equal(15, writer.GetEncodedLength()); + + // 1950 after ToUniversal + writer.WriteUtcTime( + new DateTimeOffset(2050, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), + 2049, + tag); + + Assert.Equal(30, writer.GetEncodedLength()); + + string hex = + Stringify(tag) + "0D3530303130313037313131395A" + + Stringify(tag) + "0D3439313233313139313131395A"; + + Verify(writer, hex); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs similarity index 87% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs index 9caa0d8adcf3fb..f60613708adabe 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteUtf8String : WriteCharacterString { @@ -60,13 +59,13 @@ internal override void WriteString(AsnWriter writer, string s) => writer.WriteCharacterString(UniversalTagNumber.UTF8String, s); internal override void WriteString(AsnWriter writer, Asn1Tag tag, string s) => - writer.WriteCharacterString(tag, UniversalTagNumber.UTF8String, s); + writer.WriteCharacterString(UniversalTagNumber.UTF8String, s, tag); internal override void WriteSpan(AsnWriter writer, ReadOnlySpan s) => writer.WriteCharacterString(UniversalTagNumber.UTF8String, s); internal override void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s) => - writer.WriteCharacterString(tag, UniversalTagNumber.UTF8String, s); + writer.WriteCharacterString(UniversalTagNumber.UTF8String, s, tag); internal override Asn1Tag StandardTag => new Asn1Tag(UniversalTagNumber.UTF8String); @@ -207,32 +206,32 @@ public void VerifyWrite_DER_Span_CustomTag_ClearsConstructed(string input, strin base.VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(input, expectedPayloadHex); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_String_Null(PublicEncodingRules ruleSet) => + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_String_Null(AsnEncodingRules ruleSet) => base.VerifyWrite_String_Null_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_String_Null_CustomTag(PublicEncodingRules ruleSet) => + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_String_Null_CustomTag(AsnEncodingRules ruleSet) => base.VerifyWrite_String_Null_CustomTag_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_EndOfContents_String(PublicEncodingRules ruleSet) => - base.VerifyWrite_EndOfContents_String_Helper(ruleSet); + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_Null_String(AsnEncodingRules ruleSet) => + base.VerifyWrite_Null_String_Helper(ruleSet); [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWrite_EndOfContents_Span(PublicEncodingRules ruleSet) => - base.VerifyWrite_EndOfContents_Span_Helper(ruleSet); + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWrite_Null_Span(AsnEncodingRules ruleSet) => + base.VerifyWrite_Null_Span_Helper(ruleSet); [Theory] [MemberData(nameof(CERSegmentedCases))] @@ -275,17 +274,5 @@ public void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag(string input, int c base.VerifyWrite_CERSegmented_Span_CustomPrimitiveTag_Helper(input, contentByteCount); // UTF8 has no non-encodable values. - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_Span(bool empty) => - base.WriteAfterDispose_Span_Helper(empty); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_String(bool empty) => - base.WriteAfterDispose_String_Helper(empty); } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 0a126fc83ceb2b..bfc527e0cb6f90 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -573,6 +573,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs index 71c22caff3915b..877b4ac947d82c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Security.Cryptography.Asn1; using Internal.Cryptography; @@ -934,14 +935,14 @@ public override bool TryExportEncryptedPkcs8PrivateKey( ReadOnlySpan.Empty, passwordBytes); - using (AsnWriter pkcs8PrivateKey = WritePkcs8()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = WritePkcs8(); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( passwordBytes, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } public override bool TryExportEncryptedPkcs8PrivateKey( @@ -958,34 +959,29 @@ public override bool TryExportEncryptedPkcs8PrivateKey( password, ReadOnlySpan.Empty); - using (AsnWriter pkcs8PrivateKey = WritePkcs8()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = WritePkcs8(); + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( password, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } public override bool TryExportPkcs8PrivateKey( Span destination, out int bytesWritten) { - using (AsnWriter writer = WritePkcs8()) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = WritePkcs8(); + return writer.TryEncode(destination, out bytesWritten); } public override bool TryExportSubjectPublicKeyInfo( Span destination, out int bytesWritten) { - using (AsnWriter writer = WriteSubjectPublicKeyInfo()) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = WriteSubjectPublicKeyInfo(); + return writer.TryEncode(destination, out bytesWritten); } private unsafe AsnWriter WritePkcs8() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs index fd7c034f7be31d..2e1fcc73f47538 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Formats.Asn1; using Internal.Cryptography; using System.Security.Cryptography.Asn1; @@ -197,14 +198,14 @@ public override unsafe bool TryExportEncryptedPkcs8PrivateKey( { try { - using (AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters)) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( passwordBytes, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -233,14 +234,14 @@ public override unsafe bool TryExportEncryptedPkcs8PrivateKey( { try { - using (AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters)) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( password, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -259,10 +260,8 @@ public override unsafe bool TryExportPkcs8PrivateKey( { try { - using (AsnWriter writer = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters); + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -277,10 +276,8 @@ public override bool TryExportSubjectPublicKeyInfo( { ECParameters ecParameters = ExportParameters(false); - using (AsnWriter writer = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(ecParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(ecParameters); + return writer.TryEncode(destination, out bytesWritten); } public override unsafe void ImportEncryptedPkcs8PrivateKey( @@ -403,10 +400,8 @@ public virtual unsafe byte[] ExportECPrivateKey() { try { - using (AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters)) - { - return writer.Encode(); - } + AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters); + return writer.Encode(); } finally { @@ -423,10 +418,8 @@ public virtual unsafe bool TryExportECPrivateKey(Span destination, out int { try { - using (AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters); + return writer.TryEncode(destination, out bytesWritten); } finally { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs index 59b52bd0eb36db..590af7907bbd08 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs @@ -5,6 +5,7 @@ using Internal.Cryptography; using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Security.Cryptography.Asn1; @@ -1022,14 +1023,14 @@ public override unsafe bool TryExportEncryptedPkcs8PrivateKey( { try { - using (AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters)) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( passwordBytes, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -1058,14 +1059,14 @@ public override unsafe bool TryExportEncryptedPkcs8PrivateKey( { try { - using (AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters)) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( password, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -1084,10 +1085,8 @@ public override unsafe bool TryExportPkcs8PrivateKey( { try { - using (AsnWriter writer = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters); + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -1102,10 +1101,8 @@ public override bool TryExportSubjectPublicKeyInfo( { ECParameters ecParameters = ExportParameters(false); - using (AsnWriter writer = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(ecParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(ecParameters); + return writer.TryEncode(destination, out bytesWritten); } public override unsafe void ImportEncryptedPkcs8PrivateKey( @@ -1228,10 +1225,8 @@ public virtual unsafe byte[] ExportECPrivateKey() { try { - using (AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters)) - { - return writer.Encode(); - } + AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters); + return writer.Encode(); } finally { @@ -1248,10 +1243,8 @@ public virtual unsafe bool TryExportECPrivateKey(Span destination, out int { try { - using (AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters); + return writer.TryEncode(destination, out bytesWritten); } finally { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs index 56b25ff7788256..a44884219d47ac 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Buffers; +using System.Formats.Asn1; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; @@ -288,34 +289,26 @@ public virtual bool VerifyData(ReadOnlySpan data, ReadOnlySpan signa public virtual byte[] ExportRSAPrivateKey() { - using (AsnWriter pkcs1PrivateKey = WritePkcs1PrivateKey()) - { - return pkcs1PrivateKey.Encode(); - } + AsnWriter pkcs1PrivateKey = WritePkcs1PrivateKey(); + return pkcs1PrivateKey.Encode(); } public virtual bool TryExportRSAPrivateKey(Span destination, out int bytesWritten) { - using (AsnWriter pkcs1PrivateKey = WritePkcs1PrivateKey()) - { - return pkcs1PrivateKey.TryEncode(destination, out bytesWritten); - } + AsnWriter pkcs1PrivateKey = WritePkcs1PrivateKey(); + return pkcs1PrivateKey.TryEncode(destination, out bytesWritten); } public virtual byte[] ExportRSAPublicKey() { - using (AsnWriter pkcs1PublicKey = WritePkcs1PublicKey()) - { - return pkcs1PublicKey.Encode(); - } + AsnWriter pkcs1PublicKey = WritePkcs1PublicKey(); + return pkcs1PublicKey.Encode(); } public virtual bool TryExportRSAPublicKey(Span destination, out int bytesWritten) { - using (AsnWriter pkcs1PublicKey = WritePkcs1PublicKey()) - { - return pkcs1PublicKey.TryEncode(destination, out bytesWritten); - } + AsnWriter pkcs1PublicKey = WritePkcs1PublicKey(); + return pkcs1PublicKey.TryEncode(destination, out bytesWritten); } public override unsafe bool TryExportSubjectPublicKeyInfo(Span destination, out int bytesWritten) @@ -343,10 +336,8 @@ public override unsafe bool TryExportSubjectPublicKeyInfo(Span destination continue; } - using (AsnWriter writer = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(rented.AsSpan(0, pkcs1Size))) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(rented.AsSpan(0, pkcs1Size)); + return writer.TryEncode(destination, out bytesWritten); } finally { @@ -358,10 +349,8 @@ public override unsafe bool TryExportSubjectPublicKeyInfo(Span destination public override bool TryExportPkcs8PrivateKey(Span destination, out int bytesWritten) { - using (AsnWriter writer = WritePkcs8PrivateKey()) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = WritePkcs8PrivateKey(); + return writer.TryEncode(destination, out bytesWritten); } private unsafe AsnWriter WritePkcs8PrivateKey() @@ -414,14 +403,14 @@ public override bool TryExportEncryptedPkcs8PrivateKey( password, ReadOnlySpan.Empty); - using (AsnWriter pkcs8PrivateKey = WritePkcs8PrivateKey()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = WritePkcs8PrivateKey(); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( password, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } public override bool TryExportEncryptedPkcs8PrivateKey( @@ -438,14 +427,14 @@ public override bool TryExportEncryptedPkcs8PrivateKey( ReadOnlySpan.Empty, passwordBytes); - using (AsnWriter pkcs8PrivateKey = WritePkcs8PrivateKey()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( + AsnWriter pkcs8PrivateKey = WritePkcs8PrivateKey(); + + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8( passwordBytes, pkcs8PrivateKey, - pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + pbeParameters); + + return writer.TryEncode(destination, out bytesWritten); } private AsnWriter WritePkcs1PublicKey() @@ -494,57 +483,80 @@ public override unsafe void ImportSubjectPublicKeyInfo(ReadOnlySpan source public virtual unsafe void ImportRSAPublicKey(ReadOnlySpan source, out int bytesRead) { - fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) - { - AsnReader reader = new AsnReader(manager.Memory, AsnEncodingRules.BER); - ReadOnlyMemory firstValue = reader.PeekEncodedValue(); - int localRead = firstValue.Length; + AsnDecoder.ReadEncodedValue( + source, + AsnEncodingRules.BER, + out _, + out _, + out int localRead); - AlgorithmIdentifierAsn ignored = default; - RSAKeyFormatHelper.ReadRsaPublicKey(firstValue, ignored, out RSAParameters rsaParameters); + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + { + using (MemoryManager manager = new PointerMemoryManager(ptr, localRead)) + { + AlgorithmIdentifierAsn ignored = default; + RSAKeyFormatHelper.ReadRsaPublicKey(manager.Memory, ignored, out RSAParameters rsaParameters); - ImportParameters(rsaParameters); + ImportParameters(rsaParameters); - bytesRead = localRead; + bytesRead = localRead; + } } } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } public virtual unsafe void ImportRSAPrivateKey(ReadOnlySpan source, out int bytesRead) { - fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + try { - using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) + AsnDecoder.ReadEncodedValue( + source, + AsnEncodingRules.BER, + out _, + out _, + out int firstValueLength); + + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) { - AsnReader reader = new AsnReader(manager.Memory, AsnEncodingRules.BER); - ReadOnlyMemory firstValue = reader.PeekEncodedValue(); - int localRead = firstValue.Length; - - AlgorithmIdentifierAsn ignored = default; - RSAKeyFormatHelper.FromPkcs1PrivateKey(firstValue, ignored, out RSAParameters rsaParameters); - - fixed (byte* dPin = rsaParameters.D) - fixed (byte* pPin = rsaParameters.P) - fixed (byte* qPin = rsaParameters.Q) - fixed (byte* dpPin = rsaParameters.DP) - fixed (byte* dqPin = rsaParameters.DQ) - fixed (byte* qInvPin = rsaParameters.InverseQ) + using (MemoryManager manager = new PointerMemoryManager(ptr, firstValueLength)) { - try - { - ImportParameters(rsaParameters); - } - finally + ReadOnlyMemory firstValue = manager.Memory; + int localRead = firstValue.Length; + + AlgorithmIdentifierAsn ignored = default; + RSAKeyFormatHelper.FromPkcs1PrivateKey(firstValue, ignored, out RSAParameters rsaParameters); + + fixed (byte* dPin = rsaParameters.D) + fixed (byte* pPin = rsaParameters.P) + fixed (byte* qPin = rsaParameters.Q) + fixed (byte* dpPin = rsaParameters.DP) + fixed (byte* dqPin = rsaParameters.DQ) + fixed (byte* qInvPin = rsaParameters.InverseQ) { - ClearPrivateParameters(rsaParameters); + try + { + ImportParameters(rsaParameters); + } + finally + { + ClearPrivateParameters(rsaParameters); + } } - } - bytesRead = localRead; + bytesRead = localRead; + } } } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } public override unsafe void ImportPkcs8PrivateKey(ReadOnlySpan source, out int bytesRead) diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj b/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj index 046835f221bc72..1b56985a3a7141 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj @@ -363,6 +363,7 @@ + @@ -376,4 +377,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs index 2802eade16633a..d673fa52c2e798 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Text; @@ -139,7 +140,7 @@ internal class AppleAsnFormatter : AsnFormatter return output.ToString(); } - catch (CryptographicException) + catch (AsnContentException) { return null; } diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj b/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj index 5c496e61e53674..d8283880ea00f0 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj @@ -122,6 +122,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs deleted file mode 100644 index a609c6661e195d..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public abstract partial class Asn1ReaderTests - { - public enum PublicTagClass : byte - { - Universal = TagClass.Universal, - Application = TagClass.Application, - ContextSpecific = TagClass.ContextSpecific, - Private = TagClass.Private, - } - - public enum PublicEncodingRules - { - BER = AsnEncodingRules.BER, - CER = AsnEncodingRules.CER, - DER = AsnEncodingRules.DER, - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs deleted file mode 100644 index 02d97d4b2dfb50..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadBoolean : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, false, 3, "010100")] - [InlineData(PublicEncodingRules.BER, true, 3, "010101")] - // Padded length - [InlineData(PublicEncodingRules.BER, true, 4, "01810101")] - [InlineData(PublicEncodingRules.BER, true, 3, "0101FF0500")] - [InlineData(PublicEncodingRules.CER, false, 3, "0101000500")] - [InlineData(PublicEncodingRules.CER, true, 3, "0101FF")] - [InlineData(PublicEncodingRules.DER, false, 3, "010100")] - [InlineData(PublicEncodingRules.DER, true, 3, "0101FF0500")] - // Context Specific 0 - [InlineData(PublicEncodingRules.DER, true, 3, "8001FF0500")] - // Application 31 - [InlineData(PublicEncodingRules.DER, true, 4, "5F1F01FF0500")] - // Private 253 - [InlineData(PublicEncodingRules.CER, false, 5, "DF817D01000500")] - public static void ReadBoolean_Success( - PublicEncodingRules ruleSet, - bool expectedValue, - int expectedBytesRead, - string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Asn1Tag tag = reader.PeekTag(); - bool value; - - if (tag.TagClass == TagClass.Universal) - { - value = reader.ReadBoolean(); - } - else - { - value = reader.ReadBoolean(tag); - } - - if (inputData.Length == expectedBytesRead) - { - Assert.False(reader.HasData, "reader.HasData"); - } - else - { - Assert.True(reader.HasData, "reader.HasData"); - } - - if (expectedValue) - { - Assert.True(value, "value"); - } - else - { - Assert.False(value, "value"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 1, 1, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadBoolean(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - bool value = reader.ReadBoolean(); - Assert.False(value, "value"); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x80, 1, 0xFF }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadBoolean(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadBoolean()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - bool value = reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0)); - Assert.True(value, "value"); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0101FF", PublicTagClass.Universal, 1)] - [InlineData(PublicEncodingRules.CER, "0101FF", PublicTagClass.Universal, 1)] - [InlineData(PublicEncodingRules.DER, "0101FF", PublicTagClass.Universal, 1)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - bool val1 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - bool val2 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - - Assert.Equal(val1, val2); - } - - [Theory] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("TagOnly", PublicEncodingRules.BER, "01")] - [InlineData("TagOnly", PublicEncodingRules.CER, "01")] - [InlineData("TagOnly", PublicEncodingRules.DER, "01")] - [InlineData("MultiByte TagOnly", PublicEncodingRules.DER, "9F1F")] - [InlineData("MultiByte TagOnly", PublicEncodingRules.CER, "9F1F")] - [InlineData("MultiByte TagOnly", PublicEncodingRules.BER, "9F1F")] - [InlineData("TagAndLength", PublicEncodingRules.BER, "0101")] - [InlineData("Tag and MultiByteLength", PublicEncodingRules.BER, "01820001")] - [InlineData("TagAndLength", PublicEncodingRules.CER, "8001")] - [InlineData("TagAndLength", PublicEncodingRules.DER, "C001")] - [InlineData("MultiByteTagAndLength", PublicEncodingRules.DER, "9F2001")] - [InlineData("MultiByteTagAndLength", PublicEncodingRules.CER, "9F2001")] - [InlineData("MultiByteTagAndLength", PublicEncodingRules.BER, "9F2001")] - [InlineData("MultiByteTagAndMultiByteLength", PublicEncodingRules.BER, "9F28200001")] - [InlineData("TooShort", PublicEncodingRules.BER, "0100")] - [InlineData("TooShort", PublicEncodingRules.CER, "8000")] - [InlineData("TooShort", PublicEncodingRules.DER, "0100")] - [InlineData("TooLong", PublicEncodingRules.DER, "C0020000")] - [InlineData("TooLong", PublicEncodingRules.CER, "01020000")] - [InlineData("TooLong", PublicEncodingRules.BER, "C081020000")] - [InlineData("MissingContents", PublicEncodingRules.BER, "C001")] - [InlineData("MissingContents", PublicEncodingRules.CER, "0101")] - [InlineData("MissingContents", PublicEncodingRules.DER, "8001")] - [InlineData("NonCanonical", PublicEncodingRules.DER, "0101FE")] - [InlineData("NonCanonical", PublicEncodingRules.CER, "800101")] - [InlineData("Constructed", PublicEncodingRules.BER, "2103010101")] - [InlineData("Constructed", PublicEncodingRules.CER, "2103010101")] - [InlineData("Constructed", PublicEncodingRules.DER, "2103010101")] - [InlineData("WrongTag", PublicEncodingRules.DER, "0400")] - [InlineData("WrongTag", PublicEncodingRules.CER, "0400")] - [InlineData("WrongTag", PublicEncodingRules.BER, "0400")] - public static void ReadBoolean_Failure( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Asn1Tag tag = default(Asn1Tag); - - if (inputData.Length > 0) - { - tag = reader.PeekTag(); - } - - if (tag.TagClass == TagClass.Universal) - { - Assert.Throws(() => reader.ReadBoolean()); - } - else - { - Assert.Throws(() => reader.ReadBoolean(tag)); - } - - if (inputData.Length == 0) - { - // If we started with nothing, where did the data come from? - Assert.False(reader.HasData, "reader.HasData"); - } - else - { - // Nothing should have moved - Assert.True(reader.HasData, "reader.HasData"); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs deleted file mode 100644 index a2966ad2c32e4c..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs +++ /dev/null @@ -1,760 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadEnumerated : Asn1ReaderTests - { - public enum ByteBacked : byte - { - Zero = 0, - NotFluffy = 11, - Fluff = 12, - } - - public enum SByteBacked : sbyte - { - Zero = 0, - Fluff = 83, - Pillow = -17, - } - - public enum ShortBacked : short - { - Zero = 0, - Fluff = 521, - Pillow = -1024, - } - - public enum UShortBacked : ushort - { - Zero = 0, - Fluff = 32768, - } - - public enum IntBacked : int - { - Zero = 0, - Fluff = 0x010001, - Pillow = -Fluff, - } - - public enum UIntBacked : uint - { - Zero = 0, - Fluff = 0x80000005, - } - - public enum LongBacked : long - { - Zero = 0, - Fluff = 0x0200000441, - Pillow = -0x100000000L, - } - - public enum ULongBacked : ulong - { - Zero = 0, - Fluff = 0xFACEF00DCAFEBEEF, - } - - private static void GetExpectedValue( - PublicEncodingRules ruleSet, - TEnum expectedValue, - string inputHex) - where TEnum : struct - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - TEnum value = reader.ReadEnumeratedValue(); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, ByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, ByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, ByteBacked.Fluff, "0A010C")] - [InlineData(PublicEncodingRules.CER, ByteBacked.Fluff, "0A010C")] - [InlineData(PublicEncodingRules.DER, ByteBacked.Fluff, "0A010C")] - [InlineData(PublicEncodingRules.BER, (ByteBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (ByteBacked)128, "0A020080")] - [InlineData(PublicEncodingRules.DER, (ByteBacked)129, "0A020081")] - [InlineData(PublicEncodingRules.BER, (ByteBacked)254, "0A82000200FE")] - public static void GetExpectedValue_ByteBacked( - PublicEncodingRules ruleSet, - ByteBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, SByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, SByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, SByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, SByteBacked.Fluff, "0A0153")] - [InlineData(PublicEncodingRules.CER, SByteBacked.Fluff, "0A0153")] - [InlineData(PublicEncodingRules.DER, SByteBacked.Fluff, "0A0153")] - [InlineData(PublicEncodingRules.BER, SByteBacked.Pillow, "0A01EF")] - [InlineData(PublicEncodingRules.CER, (SByteBacked)sbyte.MinValue, "0A0180")] - [InlineData(PublicEncodingRules.DER, (SByteBacked)sbyte.MinValue + 1, "0A0181")] - [InlineData(PublicEncodingRules.BER, SByteBacked.Pillow, "0A820001EF")] - public static void GetExpectedValue_SByteBacked( - PublicEncodingRules ruleSet, - SByteBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, ShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, ShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, ShortBacked.Fluff, "0A020209")] - [InlineData(PublicEncodingRules.CER, ShortBacked.Fluff, "0A020209")] - [InlineData(PublicEncodingRules.DER, ShortBacked.Fluff, "0A020209")] - [InlineData(PublicEncodingRules.BER, ShortBacked.Pillow, "0A02FC00")] - [InlineData(PublicEncodingRules.CER, (ShortBacked)short.MinValue, "0A028000")] - [InlineData(PublicEncodingRules.DER, (ShortBacked)short.MinValue + 1, "0A028001")] - [InlineData(PublicEncodingRules.BER, ShortBacked.Pillow, "0A820002FC00")] - public static void GetExpectedValue_ShortBacked( - PublicEncodingRules ruleSet, - ShortBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, UShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, UShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, UShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, UShortBacked.Fluff, "0A03008000")] - [InlineData(PublicEncodingRules.CER, UShortBacked.Fluff, "0A03008000")] - [InlineData(PublicEncodingRules.DER, UShortBacked.Fluff, "0A03008000")] - [InlineData(PublicEncodingRules.BER, (UShortBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (UShortBacked)256, "0A020100")] - [InlineData(PublicEncodingRules.DER, (UShortBacked)0x7FED, "0A027FED")] - [InlineData(PublicEncodingRules.BER, (UShortBacked)ushort.MaxValue, "0A82000300FFFF")] - [InlineData(PublicEncodingRules.BER, (UShortBacked)0x8123, "0A820003008123")] - public static void GetExpectedValue_UShortBacked( - PublicEncodingRules ruleSet, - UShortBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, IntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, IntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, IntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, IntBacked.Fluff, "0A03010001")] - [InlineData(PublicEncodingRules.CER, IntBacked.Fluff, "0A03010001")] - [InlineData(PublicEncodingRules.DER, IntBacked.Fluff, "0A03010001")] - [InlineData(PublicEncodingRules.BER, IntBacked.Pillow, "0A03FEFFFF")] - [InlineData(PublicEncodingRules.CER, (IntBacked)int.MinValue, "0A0480000000")] - [InlineData(PublicEncodingRules.DER, (IntBacked)int.MinValue + 1, "0A0480000001")] - [InlineData(PublicEncodingRules.BER, IntBacked.Pillow, "0A820003FEFFFF")] - public static void GetExpectedValue_IntBacked( - PublicEncodingRules ruleSet, - IntBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, UIntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, UIntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, UIntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, UIntBacked.Fluff, "0A050080000005")] - [InlineData(PublicEncodingRules.CER, UIntBacked.Fluff, "0A050080000005")] - [InlineData(PublicEncodingRules.DER, UIntBacked.Fluff, "0A050080000005")] - [InlineData(PublicEncodingRules.BER, (UIntBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (UIntBacked)256, "0A020100")] - [InlineData(PublicEncodingRules.DER, (UIntBacked)0x7FED, "0A027FED")] - [InlineData(PublicEncodingRules.BER, (UIntBacked)uint.MaxValue, "0A82000500FFFFFFFF")] - [InlineData(PublicEncodingRules.BER, (UIntBacked)0x8123, "0A820003008123")] - public static void GetExpectedValue_UIntBacked( - PublicEncodingRules ruleSet, - UIntBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, LongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, LongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, LongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, LongBacked.Fluff, "0A050200000441")] - [InlineData(PublicEncodingRules.CER, LongBacked.Fluff, "0A050200000441")] - [InlineData(PublicEncodingRules.DER, LongBacked.Fluff, "0A050200000441")] - [InlineData(PublicEncodingRules.BER, LongBacked.Pillow, "0A05FF00000000")] - [InlineData(PublicEncodingRules.CER, (LongBacked)short.MinValue, "0A028000")] - [InlineData(PublicEncodingRules.DER, (LongBacked)short.MinValue + 1, "0A028001")] - [InlineData(PublicEncodingRules.BER, LongBacked.Pillow, "0A820005FF00000000")] - public static void GetExpectedValue_LongBacked( - PublicEncodingRules ruleSet, - LongBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ULongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, ULongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, ULongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.CER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.DER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (ULongBacked)256, "0A020100")] - [InlineData(PublicEncodingRules.DER, (ULongBacked)0x7FED, "0A027FED")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)uint.MaxValue, "0A82000500FFFFFFFF")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)ulong.MaxValue, "0A82000900FFFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)0x8123, "0A820003008123")] - public static void GetExpectedValue_ULongBacked( - PublicEncodingRules ruleSet, - ULongBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A020102")] - [InlineData(PublicEncodingRules.CER, "0A020102")] - [InlineData(PublicEncodingRules.DER, "0A020102")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Byte(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A020102")] - [InlineData(PublicEncodingRules.CER, "0A020102")] - [InlineData(PublicEncodingRules.DER, "0A020102")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_SByte(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Short(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_UShort(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Int(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_UInt(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Long(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_ULong(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedValue_NonEnumType(PublicEncodingRules ruleSet) - { - byte[] data = { 0x0A, 0x01, 0x00 }; - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedValue_FlagsEnum(PublicEncodingRules ruleSet) - { - byte[] data = { 0x0A, 0x01, 0x00 }; - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "tEnum", - () => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedBytes(PublicEncodingRules ruleSet) - { - const string Payload = "0102030405060708090A0B0C0D0E0F10"; - - // ENUMERATED (payload) followed by INTEGER (0) - byte[] data = ("0A10" + Payload + "020100").HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - ReadOnlyMemory contents = reader.ReadEnumeratedBytes(); - Assert.Equal(0x10, contents.Length); - Assert.Equal(Payload, contents.ByteArrayToHex()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "010100")] - [InlineData(PublicEncodingRules.CER, "010100")] - [InlineData(PublicEncodingRules.DER, "010100")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - public static void ReadEnumeratedBytes_Throws(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedBytes()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x0A, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadEnumeratedValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - ShortBacked value = reader.ReadEnumeratedValue(); - Assert.Equal((ShortBacked)0x7E, value); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 2, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadEnumeratedValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - ShortBacked value = reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 7)); - Assert.Equal((ShortBacked)0x80, value); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0A01FF", PublicTagClass.Universal, 10)] - [InlineData(PublicEncodingRules.CER, "0A01FF", PublicTagClass.Universal, 10)] - [InlineData(PublicEncodingRules.DER, "0A01FF", PublicTagClass.Universal, 10)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ShortBacked val1 = reader.ReadEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ShortBacked val2 = reader.ReadEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - - Assert.Equal(val1, val2); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs deleted file mode 100644 index 37d58824c572ff..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs +++ /dev/null @@ -1,606 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadInteger : Asn1ReaderTests - { - [Theory] - [InlineData("Constructed Encoding", PublicEncodingRules.BER, "2203020100")] - [InlineData("Constructed Encoding-Indefinite", PublicEncodingRules.BER, "228002010000")] - [InlineData("Constructed Encoding-Indefinite", PublicEncodingRules.CER, "228002010000")] - [InlineData("Constructed Encoding", PublicEncodingRules.DER, "2203020100")] - [InlineData("Wrong Universal Tag", PublicEncodingRules.BER, "030100")] - [InlineData("Bad Length", PublicEncodingRules.BER, "02030102")] - [InlineData("Incorrect Zero Encoding", PublicEncodingRules.BER, "0200")] - [InlineData("Incorrect Zero Encoding", PublicEncodingRules.CER, "0200")] - [InlineData("Incorrect Zero Encoding", PublicEncodingRules.DER, "0200")] - [InlineData("Redundant Leading 0x00", PublicEncodingRules.BER, "0202007F")] - [InlineData("Redundant Leading 0x00", PublicEncodingRules.CER, "0202007F")] - [InlineData("Redundant Leading 0x00", PublicEncodingRules.DER, "0202007F")] - [InlineData("Redundant Leading 0xFF", PublicEncodingRules.BER, "0202FF80")] - [InlineData("Redundant Leading 0xFF", PublicEncodingRules.CER, "0202FF80")] - [InlineData("Redundant Leading 0xFF", PublicEncodingRules.DER, "0202FF80")] - public static void InvalidData( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadIntegerBytes()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "02017F", sbyte.MaxValue)] - [InlineData(PublicEncodingRules.DER, "020180", sbyte.MinValue)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - public static void ReadInt8_Success( - PublicEncodingRules ruleSet, - string inputHex, - sbyte expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt8(out sbyte value); - - Assert.True(didRead, "reader.TryReadInt8"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "02020102")] - [InlineData(PublicEncodingRules.CER, "02020102")] - [InlineData(PublicEncodingRules.DER, "02020102")] - public static void ReadInt8_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt8(out sbyte value); - - Assert.False(didRead, "reader.TryReadInt8"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02017F", 0x7F)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.CER, "020200FF", 0xFF)] - public static void ReadUInt8_Success( - PublicEncodingRules ruleSet, - string inputHex, - byte expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt8(out byte value); - - Assert.True(didRead, "reader.TryReadUInt8"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - public static void ReadUInt8_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt8(out byte value); - - Assert.False(didRead, "reader.TryReadUInt8"); - Assert.Equal((byte)0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - [InlineData(PublicEncodingRules.CER, "0202FEFF", unchecked((short)0xFEFF))] - [InlineData(PublicEncodingRules.BER, "028102FEEF", unchecked((short)0xFEEF))] - [InlineData(PublicEncodingRules.BER, "0281028000", short.MinValue)] - [InlineData(PublicEncodingRules.CER, "02028000", short.MinValue)] - [InlineData(PublicEncodingRules.DER, "02027FFF", short.MaxValue)] - [InlineData(PublicEncodingRules.DER, "02026372", 0x6372)] - [InlineData(PublicEncodingRules.CER, "0202008A", 0x8A)] - [InlineData(PublicEncodingRules.CER, "02028ACE", unchecked((short)0x8ACE))] - public static void ReadInt16_Success( - PublicEncodingRules ruleSet, - string inputHex, - short expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt16(out short value); - - Assert.True(didRead, "reader.TryReadInt16"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0203010203")] - [InlineData(PublicEncodingRules.CER, "0203010203")] - [InlineData(PublicEncodingRules.DER, "0203010203")] - public static void ReadInt16_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt16(out short value); - - Assert.False(didRead, "reader.TryReadInt16"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - public static void ReadUInt16_Success( - PublicEncodingRules ruleSet, - string inputHex, - ushort expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt16(out ushort value); - - Assert.True(didRead, "reader.TryReadUInt16"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "02028000")] - public static void ReadUInt16_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt16(out ushort value); - - Assert.False(didRead, "reader.TryReadUInt16"); - Assert.Equal((ushort)0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - [InlineData(PublicEncodingRules.CER, "0202FEFF", unchecked((int)0xFFFF_FEFF))] - [InlineData(PublicEncodingRules.BER, "028102FEEF", unchecked((int)0xFFFF_FEEF))] - [InlineData(PublicEncodingRules.BER, "02810480000000", int.MinValue)] - [InlineData(PublicEncodingRules.CER, "020480000000", int.MinValue)] - [InlineData(PublicEncodingRules.DER, "02047FFFFFFF", int.MaxValue)] - [InlineData(PublicEncodingRules.DER, "02026372", 0x6372)] - [InlineData(PublicEncodingRules.CER, "0203008ACE", 0x8ACE)] - [InlineData(PublicEncodingRules.BER, "0203FACE01", unchecked((int)0xFFFA_CE01))] - [InlineData(PublicEncodingRules.BER, "02820003FACE01", unchecked((int)0xFFFA_CE01))] - public static void ReadInt32_Success( - PublicEncodingRules ruleSet, - string inputHex, - int expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt32(out int value); - - Assert.True(didRead, "reader.TryReadInt32"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "02050102030405")] - [InlineData(PublicEncodingRules.CER, "02050102030405")] - [InlineData(PublicEncodingRules.DER, "02050102030405")] - public static void ReadInt32_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt32(out int value); - - Assert.False(didRead, "reader.TryReadInt32"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] - [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] - [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] - [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] - public static void ReadUInt32_Success( - PublicEncodingRules ruleSet, - string inputHex, - uint expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt32(out uint value); - - Assert.True(didRead, "reader.TryReadUInt32"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "02028000")] - [InlineData(PublicEncodingRules.DER, "0203800000")] - [InlineData(PublicEncodingRules.DER, "020480000000")] - [InlineData(PublicEncodingRules.DER, "02050100000000")] - public static void ReadUInt32_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt32(out uint value); - - Assert.False(didRead, "reader.TryReadUInt32"); - Assert.Equal((uint)0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] - [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] - [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] - [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.DER, "02050183828180", 0x0183828180)] - [InlineData(PublicEncodingRules.DER, "0206018483828180", 0x018483828180)] - [InlineData(PublicEncodingRules.DER, "020701858483828180", 0x01858483828180)] - [InlineData(PublicEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] - [InlineData(PublicEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] - [InlineData(PublicEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - [InlineData(PublicEncodingRules.DER, "0201FE", -2)] - [InlineData(PublicEncodingRules.DER, "02028012", unchecked((long)0xFFFFFFFF_FFFF8012))] - [InlineData(PublicEncodingRules.DER, "0203818012", unchecked((long)0xFFFFFFFF_FF818012))] - [InlineData(PublicEncodingRules.DER, "020482818012", unchecked((long)0xFFFFFFFF_82818012))] - [InlineData(PublicEncodingRules.DER, "02058382818012", unchecked((long)0xFFFFFF83_82818012))] - [InlineData(PublicEncodingRules.DER, "0206848382818012", unchecked((long)0xFFFF8483_82818012))] - [InlineData(PublicEncodingRules.DER, "020785848382818012", unchecked((long)0xFF858483_82818012))] - [InlineData(PublicEncodingRules.DER, "02088685848382818012", unchecked((long)0x86858483_82818012))] - [InlineData(PublicEncodingRules.DER, "02088000000000000000", long.MinValue)] - [InlineData(PublicEncodingRules.BER, "028800000000000000088000000000000000", long.MinValue)] - public static void ReadInt64_Success( - PublicEncodingRules ruleSet, - string inputHex, - long expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt64(out long value); - - Assert.True(didRead, "reader.TryReadInt64"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0209010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0209010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0209010203040506070809")] - public static void ReadInt64_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt64(out long value); - - Assert.False(didRead, "reader.TryReadInt64"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] - [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] - [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] - [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.DER, "02050183828180", 0x0183828180)] - [InlineData(PublicEncodingRules.DER, "0206018483828180", 0x018483828180)] - [InlineData(PublicEncodingRules.DER, "020701858483828180", 0x01858483828180)] - [InlineData(PublicEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] - [InlineData(PublicEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] - [InlineData(PublicEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] - [InlineData(PublicEncodingRules.DER, "0209008000000000000000", 0x80000000_00000000)] - [InlineData(PublicEncodingRules.DER, "020900FFFFFFFFFFFFFFFF", ulong.MaxValue)] - public static void ReadUInt64_Success( - PublicEncodingRules ruleSet, - string inputHex, - ulong expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt64(out ulong value); - - Assert.True(didRead, "reader.TryReadUInt64"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "02028000")] - [InlineData(PublicEncodingRules.DER, "0203800000")] - [InlineData(PublicEncodingRules.DER, "020480000000")] - [InlineData(PublicEncodingRules.DER, "02058000000000")] - [InlineData(PublicEncodingRules.DER, "0206800000000000")] - [InlineData(PublicEncodingRules.DER, "020780000000000000")] - [InlineData(PublicEncodingRules.DER, "02088000000000000000")] - [InlineData(PublicEncodingRules.DER, "0209010000000000000000")] - public static void ReadUInt64_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt64(out ulong value); - - Assert.False(didRead, "reader.TryReadUInt64"); - Assert.Equal((uint)0, value); - } - - [Fact] - public static void ReadIntegerBytes() - { - const string Payload = "0102030405060708090A0B0C0D0E0F10"; - - // INTEGER (payload) followed by INTEGER (0) - byte[] data = ("0210" + Payload + "020100").HexToByteArray(); - AsnReader reader = new AsnReader(data, AsnEncodingRules.DER); - - ReadOnlyMemory contents = reader.ReadIntegerBytes(); - Assert.Equal(0x10, contents.Length); - Assert.Equal(Payload, contents.ByteArrayToHex()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetBigInteger(PublicEncodingRules ruleSet) - { - byte[] inputData = ( - "0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + - "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + - "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + - "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + - "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + - "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + - "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + - "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + - "DA13810B4D").HexToByteArray(); - - BigInteger expected = BigInteger.Parse( - "2075455505718444046766086325128514549301113944667492053189486607" + - "5638152321436011512404808361119326026027238444019992518081753153" + - "5965931647037093368608713442955529617501657176146245891571745113" + - "4028700771890451167051818999837042261788828826028681595867897235" + - "7967091503500375497498573022675671178275171110498592645868107163" + - "8525996766798322809764200941677343791419428587801897366593842552" + - "7272226864578661449281241619675217353931828233756506947863330597" + - "8338073826285687331647183058971791153730741973483420110408271570" + - "1367336140572971505716740825623220507359429297584634909330541150" + - "79473593821332264673455059897928082590541"); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Assert.Equal(expected, reader.ReadInteger()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetNegativeBigInteger(PublicEncodingRules ruleSet) - { - // This uses a length that doesn't line up with the array pool size so - // the fill code gets tested on netstandard. - // It's the same data as above, minus the padding and lead byte (and the - // next byte changed from 0x68 to 0x88) - byte[] inputData = ( - "0281FF8861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + - "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + - "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + - "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + - "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + - "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + - "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + - "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + - "DA13810B4D").HexToByteArray(); - - BigInteger expected = BigInteger.Parse( - "-" + - "5898547409447487884446992857601985651316300515844052200158198046" + - "7814538020408452501006415149581619776188797413593169277984980446" + - "1302361382932378450492052337986628823880000831383555853860116718" + - "5361729331647715885538858385106930514758305144777880150203212976" + - "6715081632226412951106013360243549075631850526067219857352295397" + - "2308328327377769665309386917336850273904442596855844458638806936" + - "6169824439111394938336579524651037239551388910737675470211780509" + - "8035477769907389338548451561341965157059382875181284370047601682" + - "6924486017215979427815833587119797658480104671279234402026468966" + - "86109928634475300812601680679147599027"); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Assert.Equal(expected, reader.ReadInteger()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetDiminutiveBigInteger(PublicEncodingRules ruleSet) - { - // GetBigInteger with the last byte removed. - // Since it is no longer an ArrayPool alignment size the fill code gets tested on netstandard. - byte[] inputData = ( - "0282010000A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + - "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + - "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + - "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + - "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + - "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + - "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + - "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + - "DA13810B").HexToByteArray(); - - BigInteger expected = BigInteger.Parse( - "2075455505718444046766086325128514549301113944667492053189486607" + - "5638152321436011512404808361119326026027238444019992518081753153" + - "5965931647037093368608713442955529617501657176146245891571745113" + - "4028700771890451167051818999837042261788828826028681595867897235" + - "7967091503500375497498573022675671178275171110498592645868107163" + - "8525996766798322809764200941677343791419428587801897366593842552" + - "7272226864578661449281241619675217353931828233756506947863330597" + - "8338073826285687331647183058971791153730741973483420110408271570" + - "1367336140572971505716740825623220507359429297584634909330541150" + - "79473593821332264673455059897928082590541") >> 8; - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Assert.Equal(expected, reader.ReadInteger()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 2, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadIntegerBytes(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - ReadOnlyMemory value = reader.ReadIntegerBytes(); - Assert.Equal("7E", value.ByteArrayToHex()); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 2, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadIntegerBytes(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadIntegerBytes()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - ReadOnlyMemory value = reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 7)); - Assert.Equal("0080", value.ByteArrayToHex()); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0201FF", PublicTagClass.Universal, 2)] - [InlineData(PublicEncodingRules.CER, "0201FF", PublicTagClass.Universal, 2)] - [InlineData(PublicEncodingRules.DER, "0201FF", PublicTagClass.Universal, 2)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ReadOnlyMemory val1 = reader.ReadIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ReadOnlyMemory val2 = reader.ReadIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - - Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs deleted file mode 100644 index de04782d5a6301..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - internal static class AsnReaderExtensions - { - private delegate Asn1Tag ReadTagAndLengthDelegate(out int? parsedLength, out int bytesRead); - - public static Asn1Tag ReadTagAndLength(this AsnReader reader, out int? parsedLength, out int bytesRead) - { - return ((ReadTagAndLengthDelegate) - typeof(AsnReader).GetMethod("ReadTagAndLength", BindingFlags.Instance | BindingFlags.NonPublic) - .CreateDelegate(typeof(ReadTagAndLengthDelegate), reader)).Invoke(out parsedLength, out bytesRead); - } - } - - public sealed class ReadLength : Asn1ReaderTests - { - [Theory] - [InlineData(4, 0, "0400")] - [InlineData(1, 1, "0101")] - [InlineData(4, 127, "047F")] - [InlineData(4, 128, "048180")] - [InlineData(4, 255, "0481FF")] - [InlineData(2, 256, "02820100")] - [InlineData(4, int.MaxValue, "04847FFFFFFF")] - public static void MinimalPrimitiveLength(int tagValue, int length, string inputHex) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - foreach (PublicEncodingRules rules in Enum.GetValues(typeof(PublicEncodingRules))) - { - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)rules); - - Asn1Tag tag = reader.ReadTagAndLength(out int ? parsedLength, out int bytesRead); - - Assert.Equal(inputBytes.Length, bytesRead); - Assert.False(tag.IsConstructed, "tag.IsConstructed"); - Assert.Equal(tagValue, tag.TagValue); - Assert.Equal(length, parsedLength.Value); - - // ReadTagAndLength doesn't move the _data span forward. - Assert.True(reader.HasData, "reader.HasData"); - } - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) - { - byte[] data = { 0x05, 0x00 }; - - Assert.Throws( - () => new AsnReader(data, (AsnEncodingRules)invalidRuleSetValue)); - } - - [Theory] - [InlineData("")] - [InlineData("05")] - [InlineData("0481")] - [InlineData("048201")] - [InlineData("04830102")] - [InlineData("0484010203")] - public static void ReadWithInsufficientData(string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.DER); - - Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); - } - - [Theory] - [InlineData("DER indefinite constructed", PublicEncodingRules.DER, "3080" + "0500" + "0000")] - [InlineData("0xFF-BER", PublicEncodingRules.BER, "04FF")] - [InlineData("0xFF-CER", PublicEncodingRules.CER, "04FF")] - [InlineData("0xFF-DER", PublicEncodingRules.DER, "04FF")] - [InlineData("CER definite constructed", PublicEncodingRules.CER, "30820500")] - [InlineData("BER indefinite primitive", PublicEncodingRules.BER, "0480" + "0000")] - [InlineData("CER indefinite primitive", PublicEncodingRules.CER, "0480" + "0000")] - [InlineData("DER indefinite primitive", PublicEncodingRules.DER, "0480" + "0000")] - [InlineData("DER non-minimal 0", PublicEncodingRules.DER, "048100")] - [InlineData("DER non-minimal 7F", PublicEncodingRules.DER, "04817F")] - [InlineData("DER non-minimal 80", PublicEncodingRules.DER, "04820080")] - [InlineData("CER non-minimal 0", PublicEncodingRules.CER, "048100")] - [InlineData("CER non-minimal 7F", PublicEncodingRules.CER, "04817F")] - [InlineData("CER non-minimal 80", PublicEncodingRules.CER, "04820080")] - [InlineData("BER too large", PublicEncodingRules.BER, "048480000000")] - [InlineData("CER too large", PublicEncodingRules.CER, "048480000000")] - [InlineData("DER too large", PublicEncodingRules.DER, "048480000000")] - [InlineData("BER padded too large", PublicEncodingRules.BER, "0486000080000000")] - [InlineData("BER uint.MaxValue", PublicEncodingRules.BER, "0484FFFFFFFF")] - [InlineData("CER uint.MaxValue", PublicEncodingRules.CER, "0484FFFFFFFF")] - [InlineData("DER uint.MaxValue", PublicEncodingRules.DER, "0484FFFFFFFF")] - [InlineData("BER padded uint.MaxValue", PublicEncodingRules.BER, "048800000000FFFFFFFF")] - [InlineData("BER 5 byte spread", PublicEncodingRules.BER, "04850100000000")] - [InlineData("CER 5 byte spread", PublicEncodingRules.CER, "04850100000000")] - [InlineData("DER 5 byte spread", PublicEncodingRules.DER, "04850100000000")] - [InlineData("BER padded 5 byte spread", PublicEncodingRules.BER, "0486000100000000")] - public static void InvalidLengths( - string description, - PublicEncodingRules rules, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)rules); - - Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void IndefiniteLength(PublicEncodingRules ruleSet) - { - // SEQUENCE (indefinite) - // NULL - // End-of-Contents - byte[] data = { 0x30, 0x80, 0x05, 0x00, 0x00, 0x00 }; - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); - - Assert.Equal(2, bytesRead); - Assert.False(length.HasValue, "length.HasValue"); - Assert.Equal((int)UniversalTagNumber.Sequence, tag.TagValue); - Assert.True(tag.IsConstructed, "tag.IsConstructed"); - } - - [Theory] - [InlineData(0, "0483000000")] - [InlineData(1, "048A00000000000000000001")] - [InlineData(128, "049000000000000000000000000000000080")] - public static void BerNonMinimalLength(int expectedLength, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); - - Assert.Equal(inputData.Length, bytesRead); - Assert.Equal(expectedLength, length.Value); - // ReadTagAndLength doesn't move the _data span forward. - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 4, 0, 5, "0483000000" + "0500")] - [InlineData(PublicEncodingRules.DER, 1, 1, 2, "0101" + "FF")] - [InlineData(PublicEncodingRules.CER, 0x10, null, 2, "3080" + "0500" + "0000")] - public static void ReadWithDataRemaining( - PublicEncodingRules ruleSet, - int tagValue, - int? expectedLength, - int expectedBytesRead, - string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); - - Assert.Equal(expectedBytesRead, bytesRead); - Assert.Equal(tagValue, tag.TagValue); - Assert.Equal(expectedLength, length); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs deleted file mode 100644 index b0917c450f277c..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs +++ /dev/null @@ -1,315 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadNamedBitList : Asn1ReaderTests - { - [Flags] - public enum X509KeyUsageCSharpStyle - { - None = 0, - DigitalSignature = 1, - NonRepudiation = 1 << 1, - KeyEncipherment = 1 << 2, - DataEncipherment = 1 << 3, - KeyAgreement = 1 << 4, - KeyCertSign = 1 << 5, - CrlSign = 1 << 6, - EncipherOnly = 1 << 7, - DecipherOnly = 1 << 8, - } - - [Flags] - public enum ULongFlags : ulong - { - None = 0, - Min = 1, - Mid = 1L << 32, - AlmostMax = 1L << 62, - Max = 1UL << 63, - } - - [Flags] - public enum LongFlags : long - { - None = 0, - Mid = 1L << 32, - Max = 1L << 62, - Min = long.MinValue, - } - - [Theory] - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.None), - "030100")] - [InlineData( - PublicEncodingRules.CER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), - "0303070480")] - [InlineData( - PublicEncodingRules.DER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.KeyAgreement), - "03020308")] - [InlineData( - PublicEncodingRules.BER, - typeof(LongFlags), - (long)(LongFlags.Mid | LongFlags.Max), - "0309010000000080000002")] - [InlineData( - PublicEncodingRules.CER, - typeof(LongFlags), - (long)(LongFlags.Mid | LongFlags.Min), - "0309000000000080000001")] - [InlineData( - PublicEncodingRules.DER, - typeof(LongFlags), - (long)(LongFlags.Min | LongFlags.Max), - "0309000000000000000003")] - // BER: Unused bits are unmapped, regardless of value. - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), - "030307048F")] - // BER: Trailing zeros are permitted. - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), - "03050014800000")] - // BER: Trailing 0-bits don't have to be declared "unused" - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), - "0303001480")] - public static void VerifyReadNamedBitListEncodings( - PublicEncodingRules ruleSet, - Type enumType, - long enumValue, - string inputHex) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); - Enum readValue = reader.ReadNamedBitListValue(enumType); - - Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); - } - - [Theory] - [InlineData( - PublicEncodingRules.BER, - typeof(ULongFlags), - (ulong)(ULongFlags.Mid | ULongFlags.Max), - "0309000000000080000001")] - [InlineData( - PublicEncodingRules.CER, - typeof(ULongFlags), - (ulong)(ULongFlags.Min | ULongFlags.Mid), - "0306078000000080")] - [InlineData( - PublicEncodingRules.DER, - typeof(ULongFlags), - (ulong)(ULongFlags.Min | ULongFlags.Max), - "0309008000000000000001")] - public static void VerifyReadNamedBitListEncodings_ULong( - PublicEncodingRules ruleSet, - Type enumType, - ulong enumValue, - string inputHex) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); - Enum readValue = reader.ReadNamedBitListValue(enumType); - - Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyGenericReadNamedBitList(PublicEncodingRules ruleSet) - { - string inputHex = "0306078000000080" + "0309010000000080000002"; - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - ULongFlags uLongFlags = reader.ReadNamedBitListValue(); - LongFlags longFlags = reader.ReadNamedBitListValue(); - - Assert.False(reader.HasData); - Assert.Equal(ULongFlags.Mid | ULongFlags.Min, uLongFlags); - Assert.Equal(LongFlags.Mid | LongFlags.Max, longFlags); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_RequiresFlags(PublicEncodingRules ruleSet) - { - string inputHex = "030100"; - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "tFlagsEnum", - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_DataOutOfRange(PublicEncodingRules ruleSet) - { - string inputHex = "0309000000000100000001"; - - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_ExcessiveBytes(PublicEncodingRules ruleSet) - { - string inputHex = "03050014800000"; - - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_ExcessiveBits(PublicEncodingRules ruleSet) - { - string inputHex = "0303061480"; - - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 3, 2, 1, 2 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNamedBitListValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - Assert.Equal( - X509KeyUsageCSharpStyle.CrlSign, - reader.ReadNamedBitListValue()); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 2, 2, 4 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNamedBitListValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - Assert.Equal( - X509KeyUsageCSharpStyle.KeyCertSign, - reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0303070080", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.CER, "0303070080", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.DER, "0303070080", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.BER, "8003070080", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C03070080", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4603070080", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Equal( - X509KeyUsageCSharpStyle.DecipherOnly, - reader.ReadNamedBitListValue( - new Asn1Tag((TagClass)tagClass, tagValue, true))); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Equal( - X509KeyUsageCSharpStyle.DecipherOnly, - reader.ReadNamedBitListValue( - new Asn1Tag((TagClass)tagClass, tagValue, false))); - - Assert.False(reader.HasData); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs deleted file mode 100644 index fd78f55d2c1316..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadNull : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, "0500")] - [InlineData(PublicEncodingRules.CER, "0500")] - [InlineData(PublicEncodingRules.DER, "0500")] - [InlineData(PublicEncodingRules.BER, "0583000000")] - public static void ReadNull_Success(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - reader.ReadNull(); - Assert.False(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData("Long length", PublicEncodingRules.CER, "0583000000")] - [InlineData("Long length", PublicEncodingRules.DER, "0583000000")] - [InlineData("Constructed definite length", PublicEncodingRules.BER, "2500")] - [InlineData("Constructed definite length", PublicEncodingRules.DER, "2500")] - [InlineData("Constructed indefinite length", PublicEncodingRules.BER, "25800000")] - [InlineData("Constructed indefinite length", PublicEncodingRules.CER, "25800000")] - [InlineData("No length", PublicEncodingRules.BER, "05")] - [InlineData("No length", PublicEncodingRules.CER, "05")] - [InlineData("No length", PublicEncodingRules.DER, "05")] - [InlineData("No data", PublicEncodingRules.BER, "")] - [InlineData("No data", PublicEncodingRules.CER, "")] - [InlineData("No data", PublicEncodingRules.DER, "")] - [InlineData("NonEmpty", PublicEncodingRules.BER, "050100")] - [InlineData("NonEmpty", PublicEncodingRules.CER, "050100")] - [InlineData("NonEmpty", PublicEncodingRules.DER, "050100")] - [InlineData("Incomplete length", PublicEncodingRules.BER, "0581")] - public static void ReadNull_Throws(string description, PublicEncodingRules ruleSet, string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadNull()); - } - - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 5, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - reader.ReadNull(); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadNull()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 7)); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0500", PublicTagClass.Universal, 5)] - [InlineData(PublicEncodingRules.CER, "0500", PublicTagClass.Universal, 5)] - [InlineData(PublicEncodingRules.DER, "0500", PublicTagClass.Universal, 5)] - [InlineData(PublicEncodingRules.BER, "8000", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C00", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4600", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs deleted file mode 100644 index d87cea376d3e5d..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs +++ /dev/null @@ -1,339 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadSequence : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, "3000", false, -1)] - [InlineData(PublicEncodingRules.BER, "30800000", false, -1)] - [InlineData(PublicEncodingRules.BER, "3083000000", false, -1)] - [InlineData(PublicEncodingRules.CER, "30800000", false, -1)] - [InlineData(PublicEncodingRules.DER, "3000", false, -1)] - [InlineData(PublicEncodingRules.BER, "3000" + "0500", true, -1)] - [InlineData(PublicEncodingRules.BER, "3002" + "0500", false, 5)] - [InlineData(PublicEncodingRules.CER, "3080" + "0500" + "0000", false, 5)] - [InlineData(PublicEncodingRules.CER, "3080" + "010100" + "0000" + "0500", true, 1)] - [InlineData(PublicEncodingRules.DER, "3005" + "0500" + "0101FF", false, 5)] - public static void ReadSequence_Success( - PublicEncodingRules ruleSet, - string inputHex, - bool expectDataRemaining, - int expectedSequenceTagNumber) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - AsnReader sequence = reader.ReadSequence(); - - if (expectDataRemaining) - { - Assert.True(reader.HasData, "reader.HasData"); - } - else - { - Assert.False(reader.HasData, "reader.HasData"); - } - - if (expectedSequenceTagNumber < 0) - { - Assert.False(sequence.HasData, "sequence.HasData"); - } - else - { - Assert.True(sequence.HasData, "sequence.HasData"); - - Asn1Tag firstTag = sequence.PeekTag(); - Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); - } - } - - [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "30")] - [InlineData("Missing Length", PublicEncodingRules.CER, "30")] - [InlineData("Missing Length", PublicEncodingRules.DER, "30")] - [InlineData("Primitive Encoding", PublicEncodingRules.BER, "1000")] - [InlineData("Primitive Encoding", PublicEncodingRules.CER, "1000")] - [InlineData("Primitive Encoding", PublicEncodingRules.DER, "1000")] - [InlineData("Definite Length Encoding", PublicEncodingRules.CER, "3000")] - [InlineData("Indefinite Length Encoding", PublicEncodingRules.DER, "3080" + "0000")] - [InlineData("Missing Content", PublicEncodingRules.BER, "3001")] - [InlineData("Missing Content", PublicEncodingRules.DER, "3001")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.BER, "3005" + "010100")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.DER, "3005" + "010100")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.BER, "3080")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.CER, "3080")] - [InlineData("Missing EoC", PublicEncodingRules.BER, "3080" + "010100")] - [InlineData("Missing EoC", PublicEncodingRules.CER, "3080" + "010100")] - [InlineData("Missing Outer EoC", PublicEncodingRules.BER, "3080" + "010100" + ("3080" + "0000"))] - [InlineData("Missing Outer EoC", PublicEncodingRules.CER, "3080" + "010100" + ("3080" + "0000"))] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.BER, "3100")] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.DER, "3100")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.BER, "3180" + "0000")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.CER, "3180" + "0000")] - public static void ReadSequence_Throws( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadSequence()); - } - - private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) - { - AsnReader mainReader = new AsnReader(inputData, ruleSet); - - AsnReader spkiReader = mainReader.ReadSequence(); - Assert.False(mainReader.HasData, "mainReader.HasData after reading SPKI"); - - AsnReader algorithmReader = spkiReader.ReadSequence(); - Assert.True(spkiReader.HasData, "spkiReader.HasData after reading algorithm"); - - ReadOnlyMemory publicKeyValue; - int unusedBitCount; - - if (!spkiReader.TryReadPrimitiveBitStringValue(out unusedBitCount, out publicKeyValue)) - { - // The correct answer is 65 bytes. - for (int i = 10; ; i *= 2) - { - byte[] buf = new byte[i]; - - if (spkiReader.TryCopyBitStringBytes(buf, out unusedBitCount, out int bytesWritten)) - { - publicKeyValue = new ReadOnlyMemory(buf, 0, bytesWritten); - break; - } - } - } - - Assert.False(spkiReader.HasData, "spkiReader.HasData after reading subjectPublicKey"); - Assert.True(algorithmReader.HasData, "algorithmReader.HasData before reading"); - - Oid algorithmOid = algorithmReader.ReadObjectIdentifier(); - Assert.True(algorithmReader.HasData, "algorithmReader.HasData after reading first OID"); - - Assert.Equal("1.2.840.10045.2.1", algorithmOid.Value); - - Oid curveOid = algorithmReader.ReadObjectIdentifier(); - Assert.False(algorithmReader.HasData, "algorithmReader.HasData after reading second OID"); - - Assert.Equal("1.2.840.10045.3.1.7", curveOid.Value); - - const string PublicKeyValue = - "04" + - "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + - "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; - - Assert.Equal(PublicKeyValue, publicKeyValue.ByteArrayToHex()); - Assert.Equal(0, unusedBitCount); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEcPublicKey_DefiniteLength(PublicEncodingRules ruleSet) - { - const string InputHex = - "3059" + - "3013" + - "06072A8648CE3D0201" + - "06082A8648CE3D030107" + - "0342" + - "00" + - "04" + - "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + - "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; - - byte[] inputData = InputHex.HexToByteArray(); - ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void ReadEcPublicKey_IndefiniteLength(PublicEncodingRules ruleSet) - { - const string InputHex = - "3080" + - "3080" + - "06072A8648CE3D0201" + - "06082A8648CE3D030107" + - "0000" + - "0342" + - "00" + - "04" + - "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + - "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13" + - "0000"; - - byte[] inputData = InputHex.HexToByteArray(); - ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "30020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSequence(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "308005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSequence(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A5020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSequence()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A58005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSequence()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "30030101FF", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.BER, "30800101000000", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.CER, "30800101000000", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.DER, "30030101FF", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.BER, "A0030101FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.BER, "A1800101000000", PublicTagClass.ContextSpecific, 1)] - [InlineData(PublicEncodingRules.CER, "6C800101000000", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "FF8A46030101FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val1 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, true)); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val2 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, false)); - - Assert.False(reader.HasData); - - Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs deleted file mode 100644 index b2d370cfc39a5b..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadSetOf : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, "3100", false, -1)] - [InlineData(PublicEncodingRules.BER, "31800000", false, -1)] - [InlineData(PublicEncodingRules.BER, "3183000000", false, -1)] - [InlineData(PublicEncodingRules.CER, "31800000", false, -1)] - [InlineData(PublicEncodingRules.DER, "3100", false, -1)] - [InlineData(PublicEncodingRules.BER, "3100" + "0500", true, -1)] - [InlineData(PublicEncodingRules.BER, "3102" + "0500", false, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "0500" + "0000", false, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0000" + "0500", true, 1)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", false, 1)] - [InlineData(PublicEncodingRules.DER, "3105" + "0101FF" + "0500", false, 1)] - public static void ReadSetOf_Success( - PublicEncodingRules ruleSet, - string inputHex, - bool expectDataRemaining, - int expectedSequenceTagNumber) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - AsnReader sequence = reader.ReadSetOf(); - - if (expectDataRemaining) - { - Assert.True(reader.HasData, "reader.HasData"); - } - else - { - Assert.False(reader.HasData, "reader.HasData"); - } - - if (expectedSequenceTagNumber < 0) - { - Assert.False(sequence.HasData, "sequence.HasData"); - } - else - { - Assert.True(sequence.HasData, "sequence.HasData"); - - Asn1Tag firstTag = sequence.PeekTag(); - Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); - } - } - - [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "31")] - [InlineData("Missing Length", PublicEncodingRules.CER, "31")] - [InlineData("Missing Length", PublicEncodingRules.DER, "31")] - [InlineData("Primitive Encoding", PublicEncodingRules.BER, "1100")] - [InlineData("Primitive Encoding", PublicEncodingRules.CER, "1100")] - [InlineData("Primitive Encoding", PublicEncodingRules.DER, "1100")] - [InlineData("Definite Length Encoding", PublicEncodingRules.CER, "3100")] - [InlineData("Indefinite Length Encoding", PublicEncodingRules.DER, "3180" + "0000")] - [InlineData("Missing Content", PublicEncodingRules.BER, "3101")] - [InlineData("Missing Content", PublicEncodingRules.DER, "3101")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.BER, "3105" + "010100")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.DER, "3105" + "010100")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.BER, "3180")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.CER, "3180")] - [InlineData("Missing EoC", PublicEncodingRules.BER, "3180" + "010100")] - [InlineData("Missing EoC", PublicEncodingRules.CER, "3180" + "010100")] - [InlineData("Missing Outer EoC", PublicEncodingRules.BER, "3180" + "010100" + ("3180" + "0000"))] - [InlineData("Missing Outer EoC", PublicEncodingRules.CER, "3180" + "010100" + ("3180" + "0000"))] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.BER, "3000")] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.DER, "3000")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.BER, "3080" + "0000")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.CER, "3080" + "0000")] - public static void ReadSetOf_Throws( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadSetOf()); - } - - [Theory] - // BER can read out of order (indefinite) - [InlineData(PublicEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] - // BER can read out of order (definite) - [InlineData(PublicEncodingRules.BER, "3106" + "0101FF" + "010100", true, 1)] - // CER will not read out of order - [InlineData(PublicEncodingRules.CER, "3180" + "0500" + "010100" + "0000", false, 1)] - [InlineData(PublicEncodingRules.CER, "3180" + "0101FF" + "010100" + "0000", false, 1)] - // CER is happy in order: - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0500" + "0000", true, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", true, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "010100" + "0500" + "0000", true, 5)] - // DER will not read out of order - [InlineData(PublicEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] - [InlineData(PublicEncodingRules.DER, "3105" + "0500" + "010100", false, 1)] - // DER is happy in order: - [InlineData(PublicEncodingRules.DER, "3105" + "010100" + "0500", true, 5)] - [InlineData(PublicEncodingRules.DER, "3108" + "010100" + "0101FF" + "0500", true, 5)] - [InlineData(PublicEncodingRules.DER, "3108" + "010100" + "010100" + "0500", true, 5)] - public static void ReadSetOf_DataSorting( - PublicEncodingRules ruleSet, - string inputHex, - bool expectSuccess, - int lastTagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - AsnReader setOf; - - if (expectSuccess) - { - setOf = reader.ReadSetOf(); - } - else - { - AsnReader alsoReader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => alsoReader.ReadSetOf()); - - setOf = reader.ReadSetOf(skipSortOrderValidation: true); - } - - int lastTag = -1; - - while (setOf.HasData) - { - Asn1Tag tag = setOf.PeekTag(); - lastTag = tag.TagValue; - - // Ignore the return, just drain it. - setOf.ReadEncodedValue(); - } - - Assert.Equal(lastTagValue, lastTag); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "31020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSetOf(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "318005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSetOf(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A5020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSetOf()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A58005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSetOf()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "31030101FF", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.BER, "31800101000000", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.CER, "31800101000000", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.DER, "31030101FF", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.BER, "A0030101FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.BER, "A1800101000000", PublicTagClass.ContextSpecific, 1)] - [InlineData(PublicEncodingRules.CER, "6C800101000000", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "FF8A46030101FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val1 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, true)); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val2 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, false)); - - Assert.False(reader.HasData); - - Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs deleted file mode 100644 index cfe823f1de0030..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs +++ /dev/null @@ -1,239 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadUtcTime : Asn1ReaderTests - { - [Theory] - // A, B2, C2 - [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332D30373030", 2017, 9, 8, 10, 35, 3, -7, 0)] - [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332D30303530", 2017, 9, 8, 10, 35, 3, 0, -50)] - [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332B30373030", 2017, 9, 8, 10, 35, 3, 7, 0)] - [InlineData(PublicEncodingRules.BER, "17113030303130313030303030302B30303030", 2000, 1, 1, 0, 0, 0, 0, 0)] - [InlineData(PublicEncodingRules.BER, "17113030303130313030303030302D31343030", 2000, 1, 1, 0, 0, 0, -14, 0)] - // A, B2, C1 (only legal form for CER or DER) - [InlineData(PublicEncodingRules.BER, "170D3132303130323233353935395A", 2012, 1, 2, 23, 59, 59, 0, 0)] - [InlineData(PublicEncodingRules.CER, "170D3439313233313233353935395A", 2049, 12, 31, 23, 59, 59, 0, 0)] - [InlineData(PublicEncodingRules.DER, "170D3530303130323132333435365A", 1950, 1, 2, 12, 34, 56, 0, 0)] - // A, B1, C2 - [InlineData(PublicEncodingRules.BER, "170F313730393038313033352D30373030", 2017, 9, 8, 10, 35, 0, -7, 0)] - [InlineData(PublicEncodingRules.BER, "170F323730393038313033352B30393132", 2027, 9, 8, 10, 35, 0, 9, 12)] - // A, B1, C1 - [InlineData(PublicEncodingRules.BER, "170B313230313032323335395A", 2012, 1, 2, 23, 59, 0, 0, 0)] - [InlineData(PublicEncodingRules.BER, "170B343931323331323335395A", 2049, 12, 31, 23, 59, 0, 0, 0)] - // BER Constructed form - [InlineData( - PublicEncodingRules.BER, - "3780" + - "04023132" + - "04023031" + - "2480" + "040130" + "040132" + "0000" + - "040432333539" + - "04830000015A" + - "0000", - 2012, 1, 2, 23, 59, 0, 0, 0)] - public static void ParseTime_Valid( - PublicEncodingRules ruleSet, - string inputHex, - int year, - int month, - int day, - int hour, - int minute, - int second, - int offsetHour, - int offsetMinute) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - DateTimeOffset value = reader.ReadUtcTime(); - - Assert.Equal(year, value.Year); - Assert.Equal(month, value.Month); - Assert.Equal(day, value.Day); - Assert.Equal(hour, value.Hour); - Assert.Equal(minute, value.Minute); - Assert.Equal(second, value.Second); - Assert.Equal(0, value.Millisecond); - Assert.Equal(new TimeSpan(offsetHour, offsetMinute, 0), value.Offset); - } - - [Fact] - public static void ParseTime_InvalidValue_LegalString() - { - byte[] inputData = "17113030303030303030303030302D31353030".HexToByteArray(); - - var exception = Assert.Throws( - () => - { - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - reader.ReadUtcTime(); - }); - - Assert.NotNull(exception.InnerException); - Assert.IsType(exception.InnerException); - } - - [Theory] - [InlineData(2011, 1912)] - [InlineData(2012, 2012)] - [InlineData(2013, 2012)] - [InlineData(2111, 2012)] - [InlineData(2112, 2112)] - [InlineData(2113, 2112)] - [InlineData(12, 12)] - [InlineData(99, 12)] - [InlineData(111, 12)] - public static void ReadUtcTime_TwoYearMaximum(int maximum, int interpretedYear) - { - byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - DateTimeOffset value = reader.ReadUtcTime(maximum); - - Assert.Equal(interpretedYear, value.Year); - } - - [Theory] - [InlineData("A,B2,C2", PublicEncodingRules.CER, "17113137303930383130333530332D30373030")] - [InlineData("A,B2,C2", PublicEncodingRules.DER, "17113137303930383130333530332D30373030")] - [InlineData("A,B1,C2", PublicEncodingRules.CER, "170F313730393038313033352D30373030")] - [InlineData("A,B1,C2", PublicEncodingRules.DER, "170F313730393038313033352D30373030")] - [InlineData("A,B1,C1", PublicEncodingRules.CER, "170B313230313032323335395A")] - [InlineData("A,B1,C1", PublicEncodingRules.DER, "170B313230313032323335395A")] - [InlineData("A,B1,C1-NotZ", PublicEncodingRules.BER, "170B313230313032323335392B")] - [InlineData("A,B1,C2-NotPlusMinus", PublicEncodingRules.BER, "170F313730393038313033352C30373030")] - [InlineData("A,B2,C2-NotPlusMinus", PublicEncodingRules.BER, "17113137303930383130333530332C30373030")] - [InlineData("A,B2,C2-MinuteOutOfRange", PublicEncodingRules.BER, "17113030303030303030303030302D31353630")] - [InlineData("A,B1,C2-MinuteOutOfRange", PublicEncodingRules.BER, "170F303030303030303030302D31353630")] - [InlineData("A1,B2,C1-NotZ", PublicEncodingRules.DER, "170D3530303130323132333435365B")] - [InlineData("A,B2,C2-MissingDigit", PublicEncodingRules.BER, "17103137303930383130333530332C303730")] - [InlineData("A,B2,C2-TooLong", PublicEncodingRules.BER, "17123137303930383130333530332B3037303030")] - [InlineData("WrongTag", PublicEncodingRules.BER, "1A0D3132303130323233353935395A")] - public static void ReadUtcTime_Throws( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadUtcTime()); - } - - [Fact] - public static void ReadUtcTime_WayTooBig_Throws() - { - // Need to exceed the length that the shared pool will return for 17: - byte[] inputData = new byte[4097+4]; - inputData[0] = 0x17; - inputData[1] = 0x82; - inputData[2] = 0x10; - inputData[3] = 0x01; - - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - - Assert.Throws(() => reader.ReadUtcTime()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = "170D3530303130323132333435365A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadUtcTime(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - Assert.Equal( - new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), - reader.ReadUtcTime()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = "850D3530303130323132333435365A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadUtcTime(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadUtcTime()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - Assert.Equal( - new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), - reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] - [InlineData(PublicEncodingRules.CER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] - [InlineData(PublicEncodingRules.DER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] - [InlineData(PublicEncodingRules.BER, "800D3530303130323132333435365A", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C0D3530303130323132333435365A", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A460D3530303130323132333435365A", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - DateTimeOffset val1 = reader.ReadUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - DateTimeOffset val2 = reader.ReadUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); - - Assert.False(reader.HasData); - - Assert.Equal(val1, val2); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs deleted file mode 100644 index 183c5b75ac78de..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs +++ /dev/null @@ -1,290 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -using X509KeyUsageCSharpStyle = System.Security.Cryptography.Tests.Asn1.ReadNamedBitList.X509KeyUsageCSharpStyle; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public static class ComprehensiveWriteTest - { - [Fact] - public static void WriteMicrosoftDotComCert() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - // Certificate - writer.PushSequence(); - - // tbsCertificate - writer.PushSequence(); - - // version ([0] EXPLICIT INTEGER) - Asn1Tag context0 = new Asn1Tag(TagClass.ContextSpecific, 0, true); - writer.PushSequence(context0); - writer.WriteInteger(2); - writer.PopSequence(context0); - - BigInteger serialValue = BigInteger.Parse("82365655871428336739211871484630851433"); - writer.WriteInteger(serialValue); - - // signature (algorithm) - writer.PushSequence(); - writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); - writer.WriteNull(); - writer.PopSequence(); - - // issuer - writer.PushSequence(); - WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.10", "Symantec Corporation", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.11", "Symantec Trust Network", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.3", "Symantec Class 3 EV SSL CA - G3", UniversalTagNumber.PrintableString); - writer.PopSequence(); - - // validity - writer.PushSequence(); - writer.WriteUtcTime(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero)); - writer.WriteUtcTime(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero)); - writer.PopSequence(); - - // subject - writer.PushSequence(); - WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.3", "US", UniversalTagNumber.PrintableString); - WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.2", "Washington", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.15", "Private Organization", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.5", "600413485", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.17", "98052", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.8", "Washington", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.7", "Redmond", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.9", "1 Microsoft Way", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.10", "Microsoft Corporation", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.11", "MSCOM", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.3", "www.microsoft.com", UniversalTagNumber.UTF8String); - writer.PopSequence(); - - // subjectPublicKeyInfo - writer.PushSequence(); - // subjectPublicKeyInfo.algorithm - writer.PushSequence(); - writer.WriteObjectIdentifier("1.2.840.113549.1.1.1"); - writer.WriteNull(); - writer.PopSequence(); - - using (AsnWriter publicKeyWriter = new AsnWriter(AsnEncodingRules.DER)) - { - publicKeyWriter.PushSequence(); - BigInteger modulus = BigInteger.Parse( - "207545550571844404676608632512851454930111394466749205318948660756381" + - "523214360115124048083611193260260272384440199925180817531535965931647" + - "037093368608713442955529617501657176146245891571745113402870077189045" + - "116705181899983704226178882882602868159586789723579670915035003754974" + - "985730226756711782751711104985926458681071638525996766798322809764200" + - "941677343791419428587801897366593842552727222686457866144928124161967" + - "521735393182823375650694786333059783380738262856873316471830589717911" + - "537307419734834201104082715701367336140572971505716740825623220507359" + - "42929758463490933054115079473593821332264673455059897928082590541"); - publicKeyWriter.WriteInteger(modulus); - publicKeyWriter.WriteInteger(65537); - publicKeyWriter.PopSequence(); - - // subjectPublicKeyInfo.subjectPublicKey - writer.WriteBitString(publicKeyWriter.Encode()); - writer.PopSequence(); - } - - // extensions ([3] EXPLICIT Extensions) - Asn1Tag context3 = new Asn1Tag(TagClass.ContextSpecific, 3); - writer.PushSequence(context3); - writer.PushSequence(); - - Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteCharacterString(dnsName, UniversalTagNumber.IA5String, "www.microsoft.com"); - extensionValueWriter.WriteCharacterString(dnsName, UniversalTagNumber.IA5String, "wwwqa.microsoft.com"); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.17"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.19"); - // Empty sequence as the payload for a non-CA basic constraint. - writer.WriteOctetString(new byte[] { 0x30, 0x00 }); - writer.PopSequence(); - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - // This extension doesn't use a sequence at all, just Named Bit List. - extensionValueWriter.WriteNamedBitList( - X509KeyUsageCSharpStyle.DigitalSignature | X509KeyUsageCSharpStyle.KeyEncipherment); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.15"); - // critical: true - writer.WriteBoolean(true); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.3.1"); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.3.2"); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.37"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("2.16.840.1.113733.1.7.23.6"); - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.2.1"); - extensionValueWriter.WriteCharacterString(UniversalTagNumber.IA5String, "https://d.symcb.com/cps"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.2.2"); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteCharacterString(UniversalTagNumber.VisibleString, "https://d.symcb.com/rpa"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.32"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - byte[] authorityKeyIdentifier = "0159ABE7DD3A0B59A66463D6CF200757D591E76A".HexToByteArray(); - Asn1Tag keyIdentifier = context0; - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteOctetString(keyIdentifier, authorityKeyIdentifier); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.35"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - Asn1Tag distributionPointChoice = context0; - Asn1Tag fullNameChoice = context0; - Asn1Tag generalNameUriChoice = new Asn1Tag(TagClass.ContextSpecific, 6); - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(distributionPointChoice); - extensionValueWriter.PushSequence(fullNameChoice); - extensionValueWriter.WriteCharacterString( - generalNameUriChoice, - UniversalTagNumber.IA5String, - "http://sr.symcb.com/sr.crl"); - extensionValueWriter.PopSequence(fullNameChoice); - extensionValueWriter.PopSequence(distributionPointChoice); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.31"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); - extensionValueWriter.WriteCharacterString( - generalNameUriChoice, - UniversalTagNumber.IA5String, - "http://sr.symcd.com"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2"); - extensionValueWriter.WriteCharacterString( - generalNameUriChoice, - UniversalTagNumber.IA5String, - "http://sr.symcb.com/sr.crt"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("1.3.6.1.5.5.7.1.1"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - writer.PopSequence(); - writer.PopSequence(context3); - - // tbsCertificate - writer.PopSequence(); - - // signatureAlgorithm - writer.PushSequence(); - writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); - writer.WriteNull(); - writer.PopSequence(); - - // signature - byte[] containsSignature = ( - "010203040506070809" + - "15F8505B627ED7F9F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC" + - "20ACF728AAFA7A1A1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D6" + - "45BFCF840A4A3FDD988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A" + - "64A9C5FB96932BA70059CE92BD278B41299FD213471BD8165F924285AE3ECD66" + - "6C703885DCA65D24DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D" + - "4A56ADB21B5822549918015647B5F8AC131CC5EB24534D172BC60218A88B65BC" + - "F71C7F388CE3E0EF697B4203720483BB5794455B597D80D48CD3A1D73CBBC609" + - "C058767D1FF060A609D7E3D4317079AF0CD0A8A49251AB129157F9894A036487" + - "090807060504030201").HexToByteArray(); - - writer.WriteBitString(containsSignature.AsSpan(9, 256)); - - // certificate - writer.PopSequence(); - - Assert.Equal( - ComprehensiveReadTests.MicrosoftDotComSslCertBytes.ByteArrayToHex(), - writer.Encode().ByteArrayToHex()); - } - } - - private static void WriteRdn(AsnWriter writer, string oid, string value, UniversalTagNumber valueType) - { - writer.PushSetOf(); - writer.PushSequence(); - writer.WriteObjectIdentifier(oid); - writer.WriteCharacterString(valueType, value); - writer.PopSequence(); - writer.PopSetOf(); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs deleted file mode 100644 index e66448dbf55487..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs +++ /dev/null @@ -1,639 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class PushPopSequence : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSequence()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(); - - Assert.Throws( - () => writer.PopSequence()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(); - - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustom_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - - Assert.Throws( - () => writer.PopSequence()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushPrimitive_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); - writer.PopSequence(); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "30800000"); - } - else - { - Verify(writer, "3000"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomPrimitive_PopConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.Private, 5)); - writer.PopSequence(new Asn1Tag(TagClass.Private, 5, true)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "E5800000"); - } - else - { - Verify(writer, "E500"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "30800000"); - } - else - { - Verify(writer, "3000"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomConstructed_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); - writer.PopSequence(new Asn1Tag(TagClass.Private, (int)ruleSet)); - - byte tag = (byte)((int)ruleSet | 0b1110_0000); - string tagHex = tag.ToString("X2"); - string rest = ruleSet == PublicEncodingRules.CER ? "800000" : "00"; - - Verify(writer, tagHex + rest); - } - } - - [Fact] - public static void BER_WritesDefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.PushSequence(); - writer.PopSequence(); - - Verify(writer, "3000"); - } - } - - [Fact] - public static void CER_WritesIndefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.PushSequence(); - writer.PopSequence(); - - Verify(writer, "30800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); - writer.PopSequence(); - - Verify(writer, "3000"); - } - } - - [Fact] - public static void BER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); - writer.PushSequence(tag); - writer.PopSequence(tag); - - Verify(writer, "EF00"); - } - } - - [Fact] - public static void CER_WritesIndefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); - writer.PushSequence(tag); - writer.PopSequence(tag); - - Verify(writer, "7F5B800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); - writer.PushSequence(tag); - writer.PopSequence(tag); - - Verify(writer, "BE00"); - } - } - - private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) - { - writer.PushSequence(); - { - writer.PushSequence(alt); - writer.PopSequence(alt); - - writer.PushSequence(); - { - writer.PushSequence(alt); - { - writer.PushSequence(); - writer.PopSequence(); - } - - writer.PopSequence(alt); - } - - writer.PopSequence(); - } - - writer.PopSequence(); - - Verify(writer, expectedHex); - } - - [Fact] - public static void BER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); - - TestNested(writer, alt, "300AFF7F003005FF7F023000"); - } - } - - [Fact] - public static void CER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); - - TestNested(writer, alt, "3080AC8000003080AC8030800000000000000000"); - } - } - - [Fact] - public static void DER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); - - TestNested(writer, alt, "30086500300465023000"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PushSequence()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PushSequence(new Asn1Tag(TagClass.Application, 2))); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PopSequence()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.Application, 2))); - } - } - - private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) - { - writer.PushSequence(); - - // F00DF00D...F00DF00D - byte[] contentBytes = new byte[126]; - - for (int i = 0; i < contentBytes.Length; i += 2) - { - contentBytes[i] = 0xF0; - contentBytes[i + 1] = 0x0D; - } - - writer.WriteOctetString(contentBytes); - writer.PopSequence(); - - Verify(writer, expectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void SimpleContentShift(PublicEncodingRules ruleSet) - { - const string ExpectedHex = - "308180" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - [Fact] - public static void SimpleContentShift_CER() - { - const string ExpectedHex = - "3080" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "0000"; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - private static void WriteRSAPublicKeyCore(AsnEncodingRules ruleSet, string expectedHex) - { - using (AsnWriter innerWriter = new AsnWriter(ruleSet)) - { - byte[] paddedBigEndianN = ( - "00" + - "AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED369731156" + - "20968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925BCE" + - "624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8CBB" + - "5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF18" + - "7B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C94" + - "AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF2B" + - "82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6D9" + - "FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD8847").HexToByteArray(); - - // Now it's padded little-endian. - Array.Reverse(paddedBigEndianN); - BigInteger n = new BigInteger(paddedBigEndianN); - const long e = 8589935681; - - innerWriter.PushSequence(); - innerWriter.WriteInteger(n); - innerWriter.WriteInteger(e); - innerWriter.PopSequence(); - - using (AsnWriter outerWriter = new AsnWriter(ruleSet)) - { - // RSAPublicKey - outerWriter.PushSequence(); - - // AlgorithmIdentifier - outerWriter.PushSequence(); - outerWriter.WriteObjectIdentifier("1.2.840.113549.1.1.1"); - outerWriter.WriteNull(); - outerWriter.PopSequence(); - - outerWriter.WriteBitString(innerWriter.Encode()); - outerWriter.PopSequence(); - - Verify(outerWriter, expectedHex); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void WriteRSAPublicKey(PublicEncodingRules ruleSet) - { - const string ExpectedHex = - // CONSTRUCTED SEQUENCE - "30820124" + - // CONSTRUCTED SEQUENCE - "300D" + - // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) - "06092A864886F70D010101" + - // NULL - "0500" + - // BIT STRING - "03820111" + - // 0 unused bits - "00" + - // sneaky inspection of the payload bytes - // CONSTRUCTED SEQUENCE - "3082010C" + - // INTEGER (n) - "02820101" + - "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + - "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + - "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + - "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + - "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + - "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + - "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + - "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + - "47" + - // INTEGER (e) - "02050200000441"; - - WriteRSAPublicKeyCore((AsnEncodingRules)ruleSet, ExpectedHex); - } - - [Fact] - public static void WriteRSAPublicKey_CER() - { - const string ExpectedHex = - // CONSTRUCTED SEQUENCE - "3080" + - // CONSTRUCTED SEQUENCE - "3080" + - // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) - "06092A864886F70D010101" + - // NULL - "0500" + - // End-of-Contents - "0000" + - // BIT STRING - "03820111" + - // 0 unused bits - "00" + - // sneaky inspection of the payload bytes - // CONSTRUCTED SEQUENCE - "3080" + - // INTEGER (n) - "02820101" + - "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + - "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + - "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + - "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + - "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + - "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + - "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + - "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + - "47" + - // INTEGER (e) - "02050200000441" + - // End-of-Contents - "0000" + - // (no EoC for the BIT STRING) - // End-of-Contents - "0000"; - - WriteRSAPublicKeyCore(AsnEncodingRules.CER, ExpectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, true)] - public static void CannotEncodeWhileUnbalanced(PublicEncodingRules ruleSet, bool customTag) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - } - else - { - writer.PushSequence(); - } - - int written = -5; - - Assert.Throws(() => writer.Encode()); - Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); - Assert.Equal(-5, written); - - byte[] buf = new byte[10]; - Assert.Throws(() => writer.TryEncode(buf, out written)); - Assert.Equal(-5, written); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSequence_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.PushSequence(Asn1Tag.EndOfContents)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSetOf_PopSequence(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - - writer.PushSetOf(tag); - - Assert.Throws( - () => writer.PopSequence(tag)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs deleted file mode 100644 index adb4ff956f5278..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs +++ /dev/null @@ -1,622 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class PushPopSetOf : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Assert.Throws( - () => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustom_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - - Assert.Throws( - () => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushPrimitive_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); - writer.PopSetOf(); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "31800000"); - } - else - { - Verify(writer, "3100"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomPrimitive_PopConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.Private, 5)); - writer.PopSetOf(new Asn1Tag(TagClass.Private, 5, true)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "E5800000"); - } - else - { - Verify(writer, "E500"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "31800000"); - } - else - { - Verify(writer, "3100"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomConstructed_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); - writer.PopSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet)); - - byte tag = (byte)((int)ruleSet | 0b1110_0000); - string tagHex = tag.ToString("X2"); - string rest = ruleSet == PublicEncodingRules.CER ? "800000" : "00"; - - Verify(writer, tagHex + rest); - } - } - - [Fact] - public static void BER_WritesDefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Verify(writer, "3100"); - } - } - - [Fact] - public static void CER_WritesIndefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Verify(writer, "31800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Verify(writer, "3100"); - } - } - - [Fact] - public static void BER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); - writer.PushSetOf(tag); - writer.PopSetOf(tag); - - Verify(writer, "EF00"); - } - } - - [Fact] - public static void CER_WritesIndefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); - writer.PushSetOf(tag); - writer.PopSetOf(tag); - - Verify(writer, "7F5B800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); - writer.PushSetOf(tag); - writer.PopSetOf(tag); - - Verify(writer, "BE00"); - } - } - - private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) - { - // Written in pre-sorted order, since sorting is a different test. - writer.PushSetOf(); - { - writer.PushSetOf(); - { - writer.PushSetOf(alt); - { - writer.PushSetOf(); - writer.PopSetOf(); - } - - writer.PopSetOf(alt); - } - - writer.PopSetOf(); - - writer.PushSetOf(alt); - writer.PopSetOf(alt); - } - - writer.PopSetOf(); - - Verify(writer, expectedHex); - } - - [Fact] - public static void BER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); - - TestNested(writer, alt, "310A3105FF7F023100FF7F00"); - } - } - - [Fact] - public static void CER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); - - TestNested(writer, alt, "31803180AC803180000000000000AC8000000000"); - } - } - - [Fact] - public static void DER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); - - TestNested(writer, alt, "31083104650231006500"); - } - } - - private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) - { - writer.PushSetOf(); - - // F00DF00D...F00DF00D - byte[] contentBytes = new byte[126]; - - for (int i = 0; i < contentBytes.Length; i += 2) - { - contentBytes[i] = 0xF0; - contentBytes[i + 1] = 0x0D; - } - - writer.WriteOctetString(contentBytes); - writer.PopSetOf(); - - Verify(writer, expectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void SimpleContentShift(PublicEncodingRules ruleSet) - { - const string ExpectedHex = - "318180" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - [Fact] - public static void SimpleContentShift_CER() - { - const string ExpectedHex = - "3180" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "0000"; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PushSetOf()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PushSetOf(new Asn1Tag(TagClass.Application, 2))); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.Application, 2))); - } - } - - private static void ValidateDataSorting(AsnEncodingRules ruleSet, string expectedHex) - { - using (AsnWriter writer = new AsnWriter(ruleSet)) - { - writer.PushSetOf(); - - // 02 01 FF - writer.WriteInteger(-1); - // 02 01 00 - writer.WriteInteger(0); - // 02 02 00 FF - writer.WriteInteger(255); - // 01 01 FF - writer.WriteBoolean(true); - // 45 01 00 - writer.WriteBoolean(new Asn1Tag(TagClass.Application, 5), false); - // 02 01 7F - writer.WriteInteger(127); - // 02 01 80 - writer.WriteInteger(sbyte.MinValue); - // 02 02 00 FE - writer.WriteInteger(254); - // 02 01 00 - writer.WriteInteger(0); - - writer.PopSetOf(); - - // The correct sort order (CER, DER) is - // Universal Boolean: true - // Universal Integer: 0 - // Universal Integer: 0 - // Universal Integer: 127 - // Universal Integer: -128 - // Universal Integer: -1 - // Universal Integer: 254 - // Universal Integer: 255 - // Application 5 (Boolean): false - - // This test would be - // - // GrabBag ::= SET OF GrabBagItem - // - // GrabBagItem ::= CHOICE ( - // value INTEGER - // bool BOOLEAN - // grr [APPLICATION 5] IMPLICIT BOOLEAN - // ) - - Verify(writer, expectedHex); - } - } - - [Fact] - public static void BER_DoesNotSort() - { - const string ExpectedHex = - "311D" + - "0201FF" + - "020100" + - "020200FF" + - "0101FF" + - "450100" + - "02017F" + - "020180" + - "020200FE" + - "020100"; - - ValidateDataSorting(AsnEncodingRules.BER, ExpectedHex); - } - - [Fact] - public static void CER_SortsData() - { - const string ExpectedHex = - "3180" + - "0101FF" + - "020100" + - "020100" + - "02017F" + - "020180" + - "0201FF" + - "020200FE" + - "020200FF" + - "450100" + - "0000"; - - ValidateDataSorting(AsnEncodingRules.CER, ExpectedHex); - } - - [Fact] - public static void DER_SortsData() - { - const string ExpectedHex = - "311D" + - "0101FF" + - "020100" + - "020100" + - "02017F" + - "020180" + - "0201FF" + - "020200FE" + - "020200FF" + - "450100"; - - ValidateDataSorting(AsnEncodingRules.DER, ExpectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, true)] - public static void CannotEncodeWhileUnbalanced(PublicEncodingRules ruleSet, bool customTag) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - } - else - { - writer.PushSetOf(); - } - - int written = -5; - - Assert.Throws(() => writer.Encode()); - Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); - Assert.Equal(-5, written); - - byte[] buf = new byte[10]; - Assert.Throws(() => writer.TryEncode(buf, out written)); - Assert.Equal(-5, written); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSetOf_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.PushSetOf(Asn1Tag.EndOfContents)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSequence_PopSetOf(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - - writer.PushSequence(tag); - - Assert.Throws( - () => writer.PopSetOf(tag)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs deleted file mode 100644 index dbe72ac6c66b57..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs +++ /dev/null @@ -1,366 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteBitString : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteEmpty(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(ReadOnlySpan.Empty); - - Verify(writer, "030100"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 1, 1, "030201")] - [InlineData(PublicEncodingRules.CER, 2, 1, "030301")] - [InlineData(PublicEncodingRules.DER, 3, 1, "030401")] - [InlineData(PublicEncodingRules.BER, 126, 0, "037F00")] - [InlineData(PublicEncodingRules.CER, 127, 3, "03818003")] - [InlineData(PublicEncodingRules.BER, 999, 0, "038203E800")] - [InlineData(PublicEncodingRules.CER, 999, 0, "038203E800")] - [InlineData(PublicEncodingRules.DER, 999, 0, "038203E800")] - [InlineData(PublicEncodingRules.BER, 1000, 0, "038203E900")] - [InlineData(PublicEncodingRules.DER, 1000, 0, "038203E900")] - [InlineData(PublicEncodingRules.BER, 2000, 0, "038207D100")] - [InlineData(PublicEncodingRules.DER, 2000, 0, "038207D100")] - public void WritePrimitive(PublicEncodingRules ruleSet, int length, int unusedBitCount, string hexStart) - { - string payloadHex = new string('0', 2 * length); - string expectedHex = hexStart + payloadHex; - byte[] data = new byte[length]; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(data, unusedBitCount); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(1000, 1, "2380038203E800", "030201")] - [InlineData(999*2, 3, "2380038203E800", "038203E803")] - public void WriteSegmentedCER(int length, int unusedBitCount, string hexStart, string hexStart2) - { - string payload1Hex = new string('8', 999 * 2); - string payload2Hex = new string('8', (length - 999) * 2); - string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; - byte[] data = new byte[length]; - - for (int i = 0; i < data.Length; i++) - { - data[i] = 0x88; - } - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteBitString(data, unusedBitCount); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, false)] - [InlineData(PublicEncodingRules.CER, 0, false)] - [InlineData(PublicEncodingRules.DER, 0, false)] - [InlineData(PublicEncodingRules.BER, 999, false)] - [InlineData(PublicEncodingRules.CER, 999, false)] - [InlineData(PublicEncodingRules.DER, 999, false)] - [InlineData(PublicEncodingRules.BER, 1000, false)] - [InlineData(PublicEncodingRules.CER, 1000, true)] - [InlineData(PublicEncodingRules.DER, 1000, false)] - [InlineData(PublicEncodingRules.BER, 1998, false)] - [InlineData(PublicEncodingRules.CER, 1998, true)] - [InlineData(PublicEncodingRules.BER, 4096, false)] - [InlineData(PublicEncodingRules.CER, 4096, true)] - [InlineData(PublicEncodingRules.DER, 4096, false)] - public void VerifyWriteBitString_PrimitiveOrConstructed( - PublicEncodingRules ruleSet, - int payloadLength, - bool expectConstructed) - { - byte[] data = new byte[payloadLength]; - - Asn1Tag[] tagsToTry = - { - new Asn1Tag(UniversalTagNumber.BitString), - new Asn1Tag(UniversalTagNumber.BitString, isConstructed: true), - new Asn1Tag(TagClass.Private, 87), - new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), - }; - - byte[] answerBuf = new byte[payloadLength + 100]; - - foreach (Asn1Tag toTry in tagsToTry) - { - Asn1Tag writtenTag; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(toTry, data); - - Assert.True(writer.TryEncode(answerBuf, out _)); - Assert.True(Asn1Tag.TryDecode(answerBuf, out writtenTag, out _)); - } - - if (expectConstructed) - { - Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - else - { - Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - - Assert.Equal(toTry.TagClass, writtenTag.TagClass); - Assert.Equal(toTry.TagValue, writtenTag.TagValue); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "FF", false)] - [InlineData(PublicEncodingRules.BER, 1, "FE", false)] - [InlineData(PublicEncodingRules.CER, 1, "FE", false)] - [InlineData(PublicEncodingRules.DER, 1, "FE", false)] - [InlineData(PublicEncodingRules.BER, 1, "FF", true)] - [InlineData(PublicEncodingRules.CER, 1, "FF", true)] - [InlineData(PublicEncodingRules.DER, 1, "FF", true)] - [InlineData(PublicEncodingRules.BER, 7, "C0", true)] - [InlineData(PublicEncodingRules.CER, 7, "C0", true)] - [InlineData(PublicEncodingRules.DER, 7, "C0", true)] - [InlineData(PublicEncodingRules.BER, 7, "80", false)] - [InlineData(PublicEncodingRules.CER, 7, "80", false)] - [InlineData(PublicEncodingRules.DER, 7, "80", false)] - [InlineData(PublicEncodingRules.DER, 7, "40", true)] - [InlineData(PublicEncodingRules.DER, 6, "40", false)] - [InlineData(PublicEncodingRules.DER, 6, "C0", false)] - [InlineData(PublicEncodingRules.DER, 6, "20", true)] - [InlineData(PublicEncodingRules.DER, 5, "20", false)] - [InlineData(PublicEncodingRules.DER, 5, "A0", false)] - [InlineData(PublicEncodingRules.DER, 5, "10", true)] - [InlineData(PublicEncodingRules.DER, 4, "10", false)] - [InlineData(PublicEncodingRules.DER, 4, "90", false)] - [InlineData(PublicEncodingRules.DER, 4, "30", false)] - [InlineData(PublicEncodingRules.DER, 4, "08", true)] - [InlineData(PublicEncodingRules.DER, 4, "88", true)] - [InlineData(PublicEncodingRules.DER, 3, "08", false)] - [InlineData(PublicEncodingRules.DER, 3, "A8", false)] - [InlineData(PublicEncodingRules.DER, 3, "04", true)] - [InlineData(PublicEncodingRules.DER, 3, "14", true)] - [InlineData(PublicEncodingRules.DER, 2, "04", false)] - [InlineData(PublicEncodingRules.DER, 2, "0C", false)] - [InlineData(PublicEncodingRules.DER, 2, "FC", false)] - [InlineData(PublicEncodingRules.DER, 2, "02", true)] - [InlineData(PublicEncodingRules.DER, 2, "82", true)] - [InlineData(PublicEncodingRules.DER, 2, "FE", true)] - [InlineData(PublicEncodingRules.DER, 1, "02", false)] - [InlineData(PublicEncodingRules.DER, 1, "82", false)] - [InlineData(PublicEncodingRules.DER, 1, "80", false)] - public static void WriteBitString_UnusedBitCount_MustBeValid( - PublicEncodingRules ruleSet, - int unusedBitCount, - string inputHex, - bool expectThrow) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (expectThrow) - { - Assert.Throws( - () => writer.WriteBitString(inputBytes, unusedBitCount)); - - Assert.Throws( - () => writer.WriteBitString( - new Asn1Tag(TagClass.ContextSpecific, 3), - inputBytes, - unusedBitCount)); - - return; - } - - byte[] output = new byte[512]; - writer.WriteBitString(inputBytes, unusedBitCount); - Assert.True(writer.TryEncode(output, out int bytesWritten)); - - // This assumes that inputBytes is never more than 999 (and avoids CER constructed forms) - Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); - - writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 9), inputBytes, unusedBitCount); - Assert.True(writer.TryEncode(output, out bytesWritten)); - - Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, -1)] - [InlineData(PublicEncodingRules.CER, -1)] - [InlineData(PublicEncodingRules.DER, -1)] - [InlineData(PublicEncodingRules.BER, -2)] - [InlineData(PublicEncodingRules.CER, -2)] - [InlineData(PublicEncodingRules.DER, -2)] - [InlineData(PublicEncodingRules.BER, 8)] - [InlineData(PublicEncodingRules.CER, 8)] - [InlineData(PublicEncodingRules.DER, 8)] - [InlineData(PublicEncodingRules.BER, 9)] - [InlineData(PublicEncodingRules.CER, 9)] - [InlineData(PublicEncodingRules.DER, 9)] - [InlineData(PublicEncodingRules.BER, 1048576)] - [InlineData(PublicEncodingRules.CER, 1048576)] - [InlineData(PublicEncodingRules.DER, 1048576)] - public static void UnusedBitCounts_Bounds(PublicEncodingRules ruleSet, int unusedBitCount) - { - byte[] data = new byte[5]; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - ArgumentOutOfRangeException exception = AssertExtensions.Throws( - nameof(unusedBitCount), - () => writer.WriteBitString(data, unusedBitCount)); - - Assert.Equal(unusedBitCount, exception.ActualValue); - - exception = AssertExtensions.Throws( - nameof(unusedBitCount), - () => writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 5), data, unusedBitCount)); - - Assert.Equal(unusedBitCount, exception.ActualValue); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void EmptyData_Requires0UnusedBits(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); - - Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); - - Assert.Throws( - () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 1)); - - Assert.Throws( - () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 7)); - - writer.WriteBitString(ReadOnlySpan.Empty, 0); - writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 0); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, PublicTagClass.Universal, 3, "030100")] - [InlineData(PublicEncodingRules.CER, PublicTagClass.Universal, 3, "030100")] - [InlineData(PublicEncodingRules.DER, PublicTagClass.Universal, 3, "030100")] - [InlineData(PublicEncodingRules.BER, PublicTagClass.Private, 1, "C10100")] - [InlineData(PublicEncodingRules.CER, PublicTagClass.Application, 5, "450100")] - [InlineData(PublicEncodingRules.DER, PublicTagClass.ContextSpecific, 32, "9F200100")] - public static void EmptyData_Allows0UnusedBits( - PublicEncodingRules ruleSet, - PublicTagClass tagClass, - int tagValue, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - - if (tagClass == PublicTagClass.Universal) - { - Debug.Assert(tagValue == (int)UniversalTagNumber.BitString); - writer.WriteBitString(ReadOnlySpan.Empty, 0); - } - else - { - writer.WriteBitString(new Asn1Tag((TagClass)tagClass, tagValue), ReadOnlySpan.Empty, 0); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteBitString_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.EndOfContents, new byte[1])); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty)); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); - - AssertExtensions.Throws( - "unusedBitCount", - () => writer.WriteBitString(ReadOnlySpan.Empty, 9)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.Boolean, ReadOnlySpan.Empty)); - - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 11); - - Assert.Throws( - () => writer.WriteBitString(tag, ReadOnlySpan.Empty)); - - Assert.Throws( - () => writer.WriteBitString(tag, ReadOnlySpan.Empty, 1)); - - AssertExtensions.Throws( - "unusedBitCount", - () => writer.WriteBitString(tag, ReadOnlySpan.Empty, 9)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs deleted file mode 100644 index 556d3478f91dcd..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteBoolean : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, false, "010100")] - [InlineData(PublicEncodingRules.BER, true, "0101FF")] - [InlineData(PublicEncodingRules.CER, false, "010100")] - [InlineData(PublicEncodingRules.CER, true, "0101FF")] - [InlineData(PublicEncodingRules.DER, false, "010100")] - [InlineData(PublicEncodingRules.DER, true, "0101FF")] - public void VerifyWriteBoolean(PublicEncodingRules ruleSet, bool value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false, "830100")] - [InlineData(PublicEncodingRules.BER, true, "8301FF")] - [InlineData(PublicEncodingRules.CER, false, "830100")] - [InlineData(PublicEncodingRules.CER, true, "8301FF")] - [InlineData(PublicEncodingRules.DER, false, "830100")] - [InlineData(PublicEncodingRules.DER, true, "8301FF")] - public void VerifyWriteBoolean_Context3(PublicEncodingRules ruleSet, bool value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 3), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.DER, true)] - public void VerifyWriteBoolean_EndOfContents(PublicEncodingRules ruleSet, bool value) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.EndOfContents, value)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.DER, true)] - public void VerifyWriteBoolean_ConstructedIgnored(PublicEncodingRules ruleSet, bool value) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 7, true), value); - writer.WriteBoolean(new Asn1Tag(UniversalTagNumber.Boolean, true), value); - - if (value) - { - Verify(writer, "8701FF0101FF"); - } - else - { - Verify(writer, "870100010100"); - } - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteBoolean(false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.Integer, false)); - - Assert.Throws( - () => writer.WriteBoolean(new Asn1Tag(TagClass.Private, 3), false)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs deleted file mode 100644 index 5637e9dff85936..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs +++ /dev/null @@ -1,548 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using System.Text; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public abstract class WriteCharacterString : Asn1WriterTests - { - internal abstract void WriteString(AsnWriter writer, string s); - internal abstract void WriteString(AsnWriter writer, Asn1Tag tag, string s); - - internal abstract void WriteSpan(AsnWriter writer, ReadOnlySpan s); - internal abstract void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s); - - internal abstract Asn1Tag StandardTag { get; } - - protected const string GettysburgAddress = - "Four score and seven years ago our fathers brought forth on this continent, a new nation, " + - "conceived in Liberty, and dedicated to the proposition that all men are created equal.\r\n" + - "\r\n" + - "Now we are engaged in a great civil war, testing whether that nation, or any nation so " + - "conceived and so dedicated, can long endure. We are met on a great battle-field of that " + - "war. We have come to dedicate a portion of that field, as a final resting place for those " + - "who here gave their lives that that nation might live. It is altogether fitting and proper " + - "that we should do this.\r\n" + - "\r\n" + - "But, in a larger sense, we can not dedicate-we can not consecrate-we can not hallow-this " + - "ground. The brave men, living and dead, who struggled here, have consecrated it, far above " + - "our poor power to add or detract. The world will little note, nor long remember what we say " + - "here, but it can never forget what they did here. It is for us the living, rather, to be " + - "dedicated here to the unfinished work which they who fought here have thus far so nobly " + - "advanced. It is rather for us to be here dedicated to the great task remaining before " + - "us-that from these honored dead we take increased devotion to that cause for which they " + - "gave the last full measure of devotion-that we here highly resolve that these dead shall " + - "not have died in vain-that this nation, under God, shall have a new birth of freedom-and " + - "that government of the people, by the people, for the people, shall not perish from the earth."; - - protected void VerifyWrite_BER_String_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_String_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 14); - WriteString(writer, tag, input); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 19); - WriteString(writer, tag, input); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 2); - WriteString(writer, tag, input); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteSpan(writer, input.AsSpan()); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, int.MaxValue >> 1); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteSpan(writer, input.AsSpan()); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 30); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteSpan(writer, input.AsSpan()); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 31); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteString(writer, tag, input); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteString(writer, tag, input); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteString(writer, tag, input); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 1701, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteString(writer, tag, input); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 11, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteString(writer, tag, input); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteString(writer, tag, input); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_String_Null_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "str", - () => WriteString(writer, null)); - } - } - - protected void VerifyWrite_String_Null_CustomTag_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "str", - () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); - } - } - - protected void VerifyWrite_EndOfContents_String_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => WriteString(writer, Asn1Tag.EndOfContents, "hi")); - } - } - - protected void VerifyWrite_EndOfContents_Span_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => WriteSpan(writer, Asn1Tag.EndOfContents, "hi".AsSpan())); - } - } - - private void VerifyWrite_CERSegmented(AsnWriter writer, string tagHex, int contentByteCount) - { - int div = Math.DivRem(contentByteCount, 1000, out int rem); - - // tag, length(80), div full segments at 1004 bytes each, and the end of contents. - int encodedSize = (tagHex.Length / 2) + 1 + 1004 * div + 2; - - if (rem != 0) - { - // tag, contents (length TBD) - encodedSize += 1 + rem; - - if (encodedSize < 0x80) - encodedSize++; - else if (encodedSize <= 0xFF) - encodedSize += 2; - else - encodedSize += 3; - } - - byte[] encoded = writer.Encode(); - - Assert.Equal(tagHex, encoded.AsSpan(0, tagHex.Length / 2).ByteArrayToHex()); - Assert.Equal("0000", encoded.AsSpan(encoded.Length - 2).ByteArrayToHex()); - Assert.Equal(encodedSize, encoded.Length); - } - - protected void VerifyWrite_CERSegmented_String_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteString(writer, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_String_CustomTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); - string tagHex = Stringify(tag); - - WriteString(writer, tag, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_String_ConstructedTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteString(writer, tag, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_String_CustomPrimitiveTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); - Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); - string tagHex = Stringify(constr); - - WriteString(writer, prim, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteSpan(writer, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_CustomTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); - string tagHex = Stringify(tag); - - WriteSpan(writer, tag, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_ConstructedTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteSpan(writer, tag, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); - Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); - string tagHex = Stringify(constr); - - WriteSpan(writer, prim, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_String_NonEncodable_Helper(string input) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.Throws(() => WriteString(writer, input)); - } - } - - protected void VerifyWrite_Span_NonEncodable_Helper(string input) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.Throws(() => WriteSpan(writer, input.AsSpan())); - } - } - - protected void WriteAfterDispose_Span_Helper(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - string input = "1"; - - Assert.Throws( - () => WriteSpan(writer, input.AsSpan())); - - AssertExtensions.Throws( - "tag", - () => WriteSpan(writer, Asn1Tag.Boolean, input.AsSpan())); - - Assert.Throws( - () => WriteSpan(writer, new Asn1Tag(TagClass.Application, 0), input.AsSpan())); - } - } - - protected void WriteAfterDispose_String_Helper(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - string input = "1"; - - Assert.Throws( - () => WriteString(writer, input)); - - AssertExtensions.Throws( - "tag", - () => WriteString(writer, Asn1Tag.Boolean, input)); - - Assert.Throws( - () => WriteString(writer, new Asn1Tag(TagClass.Application, 0), input)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs deleted file mode 100644 index 20280d24514c0e..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs +++ /dev/null @@ -1,526 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.X509Certificates; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteEnumerated : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.SByteBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.SByteBacked.Pillow, true, "9E01EF")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.SByteBacked.Fluff, false, "0A0153")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.SByteBacked.Fluff, true, "9E0153")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.SByteBacked)(-127), true, "9E0181")] - public void VerifyWriteEnumerated_SByte( - PublicEncodingRules ruleSet, - ReadEnumerated.SByteBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 30), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ByteBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.ByteBacked.NotFluffy, true, "9A010B")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.ByteBacked.Fluff, false, "0A010C")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ByteBacked.Fluff, true, "9A010C")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ByteBacked)253, false, "0A0200FD")] - public void VerifyWriteEnumerated_Byte( - PublicEncodingRules ruleSet, - ReadEnumerated.ByteBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 26), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ShortBacked.Zero, true, "DF81540100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.ShortBacked.Pillow, true, "DF815402FC00")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.ShortBacked.Fluff, false, "0A020209")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ShortBacked.Fluff, true, "DF8154020209")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)25321, false, "0A0262E9")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)(-12345), false, "0A02CFC7")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)(-1), true, "DF815401FF")] - public void VerifyWriteEnumerated_Short( - PublicEncodingRules ruleSet, - ReadEnumerated.ShortBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 212), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, true, "4D0100")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.UShortBacked.Fluff, false, "0A03008000")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Fluff, true, "4D03008000")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.UShortBacked)11, false, "0A010B")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.UShortBacked)short.MaxValue, false, "0A027FFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.UShortBacked)ushort.MaxValue, true, "4D0300FFFF")] - public void VerifyWriteEnumerated_UShort( - PublicEncodingRules ruleSet, - ReadEnumerated.UShortBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, 13), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.IntBacked.Zero, true, "5F81FF7F0100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.IntBacked.Pillow, true, "5F81FF7F03FEFFFF")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.IntBacked.Fluff, false, "0A03010001")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.IntBacked.Fluff, true, "5F81FF7F03010001")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)25321, false, "0A0262E9")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.IntBacked)(-12345), false, "0A02CFC7")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.IntBacked)(-1), true, "5F81FF7F01FF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)int.MinValue, true, "5F81FF7F0480000000")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)int.MaxValue, false, "0A047FFFFFFF")] - public void VerifyWriteEnumerated_Int( - PublicEncodingRules ruleSet, - ReadEnumerated.IntBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, short.MaxValue), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, true, "9F610100")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.UIntBacked.Fluff, false, "0A050080000005")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Fluff, true, "9F61050080000005")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.UIntBacked)11, false, "0A010B")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.UIntBacked)short.MaxValue, false, "0A027FFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.UIntBacked)ushort.MaxValue, true, "9F610300FFFF")] - public void VerifyWriteEnumerated_UInt( - PublicEncodingRules ruleSet, - ReadEnumerated.UIntBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 97), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.LongBacked.Zero, true, "800100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.LongBacked.Pillow, true, "8005FF00000000")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.LongBacked.Fluff, false, "0A050200000441")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.LongBacked.Fluff, true, "80050200000441")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)25321, false, "0A0262E9")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.LongBacked)(-12345), false, "0A02CFC7")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.LongBacked)(-1), true, "8001FF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)int.MinValue, true, "800480000000")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.LongBacked)int.MaxValue, false, "0A047FFFFFFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.LongBacked)long.MinValue, false, "0A088000000000000000")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)long.MaxValue, true, "80087FFFFFFFFFFFFFFF")] - public void VerifyWriteEnumerated_Long( - PublicEncodingRules ruleSet, - ReadEnumerated.LongBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, true, "C10100")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.ULongBacked.Fluff, false, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Fluff, true, "C10900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ULongBacked)11, false, "0A010B")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.ULongBacked)short.MaxValue, false, "0A027FFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.ULongBacked)ushort.MaxValue, true, "C10300FFFF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ULongBacked)long.MaxValue, true, "C1087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.ULongBacked)ulong.MaxValue, false, "0A0900FFFFFFFFFFFFFFFF")] - public void VerifyWriteEnumerated_ULong( - PublicEncodingRules ruleSet, - ReadEnumerated.ULongBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 1), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyFlagsBased(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(OpenFlags.IncludeArchived)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 13), - OpenFlags.IncludeArchived)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue((object)OpenFlags.IncludeArchived)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 13), - (object)OpenFlags.IncludeArchived)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyNonEnum(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteEnumeratedValue(5)); - - Assert.Throws( - () => writer.WriteEnumeratedValue((object)"hi")); - - Assert.Throws( - () => writer.WriteEnumeratedValue((object)5)); - - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), 5)); - - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)"hi")); - - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)5)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyEndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, ReadEnumerated.IntBacked.Pillow)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, (object)ReadEnumerated.IntBacked.Pillow)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_NonNull(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue(null)); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 1), - null)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_Object(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.UIntBacked.Fluff); - - genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.SByteBacked.Fluff); - - genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.ULongBacked.Fluff); - - Verify(objWriter, genWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_Object_WithTag(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); - - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.UIntBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.UIntBacked.Fluff); - - tag = new Asn1Tag(TagClass.Private, 4); - - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.SByteBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.SByteBacked.Fluff); - - tag = new Asn1Tag(TagClass.Application, 75); - - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.ULongBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.ULongBacked.Fluff); - - Verify(objWriter, genWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteEnumeratedValue_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteEnumeratedValue( - new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true), - ReadEnumerated.ULongBacked.Fluff); - - writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), - (object)ReadEnumerated.SByteBacked.Fluff); - - Verify(writer, "0A0900FACEF00DCAFEBEEF" + "800153"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue(false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue((object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue((object)null)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff)); - - // Type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(ReadNamedBitList.LongFlags.Mid)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue((object)ReadEnumerated.SByteBacked.Fluff)); - - // Unboxed type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue((object)ReadNamedBitList.ULongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, ReadEnumerated.UIntBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, ReadNamedBitList.LongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)ReadEnumerated.SByteBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)ReadNamedBitList.ULongFlags.Mid)); - - Asn1Tag tag = new Asn1Tag(TagClass.Private, 6); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue(tag, false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue(tag, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue(tag, (object)null)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue(tag, ReadEnumerated.UIntBacked.Fluff)); - - // Type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(tag, ReadNamedBitList.LongFlags.Mid)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue(tag, (object)ReadEnumerated.SByteBacked.Fluff)); - - // Unboxed type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(tag, (object)ReadNamedBitList.ULongFlags.Mid)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs deleted file mode 100644 index 05c9b394867fa1..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs +++ /dev/null @@ -1,493 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteInteger : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "020100")] - [InlineData(PublicEncodingRules.CER, 0, "020100")] - [InlineData(PublicEncodingRules.DER, 0, "020100")] - [InlineData(PublicEncodingRules.BER, -1, "0201FF")] - [InlineData(PublicEncodingRules.CER, -1, "0201FF")] - [InlineData(PublicEncodingRules.DER, -1, "0201FF")] - [InlineData(PublicEncodingRules.BER, -2, "0201FE")] - [InlineData(PublicEncodingRules.DER, sbyte.MinValue, "020180")] - [InlineData(PublicEncodingRules.BER, sbyte.MinValue + 1, "020181")] - [InlineData(PublicEncodingRules.CER, sbyte.MinValue - 1, "0202FF7F")] - [InlineData(PublicEncodingRules.DER, sbyte.MinValue - 2, "0202FF7E")] - [InlineData(PublicEncodingRules.BER, -256, "0202FF00")] - [InlineData(PublicEncodingRules.CER, -257, "0202FEFF")] - [InlineData(PublicEncodingRules.DER, short.MinValue, "02028000")] - [InlineData(PublicEncodingRules.BER, short.MinValue + 1, "02028001")] - [InlineData(PublicEncodingRules.CER, short.MinValue + byte.MaxValue, "020280FF")] - [InlineData(PublicEncodingRules.DER, short.MinValue - 1, "0203FF7FFF")] - [InlineData(PublicEncodingRules.BER, short.MinValue - 2, "0203FF7FFE")] - [InlineData(PublicEncodingRules.CER, -65281, "0203FF00FF")] - [InlineData(PublicEncodingRules.DER, -8388608, "0203800000")] - [InlineData(PublicEncodingRules.BER, -8388607, "0203800001")] - [InlineData(PublicEncodingRules.CER, -8388609, "0204FF7FFFFF")] - [InlineData(PublicEncodingRules.DER, -16777216, "0204FF000000")] - [InlineData(PublicEncodingRules.BER, -16777217, "0204FEFFFFFF")] - [InlineData(PublicEncodingRules.CER, int.MinValue, "020480000000")] - [InlineData(PublicEncodingRules.DER, int.MinValue + 1, "020480000001")] - [InlineData(PublicEncodingRules.BER, (long)int.MinValue - 1, "0205FF7FFFFFFF")] - [InlineData(PublicEncodingRules.CER, (long)int.MinValue - 2, "0205FF7FFFFFFE")] - [InlineData(PublicEncodingRules.DER, -4294967296, "0205FF00000000")] - [InlineData(PublicEncodingRules.BER, -4294967295, "0205FF00000001")] - [InlineData(PublicEncodingRules.CER, -4294967294, "0205FF00000002")] - [InlineData(PublicEncodingRules.DER, -4294967297, "0205FEFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, -549755813888, "02058000000000")] - [InlineData(PublicEncodingRules.CER, -549755813887, "02058000000001")] - [InlineData(PublicEncodingRules.DER, -549755813889, "0206FF7FFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, -549755813890, "0206FF7FFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, -140737488355328, "0206800000000000")] - [InlineData(PublicEncodingRules.DER, -140737488355327, "0206800000000001")] - [InlineData(PublicEncodingRules.BER, -140737488355329, "0207FF7FFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, -281474976710656, "0207FF000000000000")] - [InlineData(PublicEncodingRules.DER, -281474976710655, "0207FF000000000001")] - [InlineData(PublicEncodingRules.BER, -281474976710657, "0207FEFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, -36028797018963968, "020780000000000000")] - [InlineData(PublicEncodingRules.DER, -36028797018963967, "020780000000000001")] - [InlineData(PublicEncodingRules.DER, -36028797018963969, "0208FF7FFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, -36028797018963970, "0208FF7FFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, -72057594037927936, "0208FF00000000000000")] - [InlineData(PublicEncodingRules.DER, -72057594037927935, "0208FF00000000000001")] - [InlineData(PublicEncodingRules.BER, -72057594037927937, "0208FEFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, long.MinValue + 1, "02088000000000000001")] - [InlineData(PublicEncodingRules.DER, long.MinValue, "02088000000000000000")] - [InlineData(PublicEncodingRules.BER, 1, "020101")] - [InlineData(PublicEncodingRules.CER, 127, "02017F")] - [InlineData(PublicEncodingRules.DER, 126, "02017E")] - [InlineData(PublicEncodingRules.BER, 128, "02020080")] - [InlineData(PublicEncodingRules.CER, 129, "02020081")] - [InlineData(PublicEncodingRules.DER, 254, "020200FE")] - [InlineData(PublicEncodingRules.BER, 255, "020200FF")] - [InlineData(PublicEncodingRules.CER, 256, "02020100")] - [InlineData(PublicEncodingRules.DER, 32767, "02027FFF")] - [InlineData(PublicEncodingRules.BER, 32766, "02027FFE")] - [InlineData(PublicEncodingRules.CER, 32768, "0203008000")] - [InlineData(PublicEncodingRules.DER, 32769, "0203008001")] - [InlineData(PublicEncodingRules.BER, 65535, "020300FFFF")] - [InlineData(PublicEncodingRules.CER, 65534, "020300FFFE")] - [InlineData(PublicEncodingRules.DER, 65536, "0203010000")] - [InlineData(PublicEncodingRules.BER, 65537, "0203010001")] - [InlineData(PublicEncodingRules.CER, 8388607, "02037FFFFF")] - [InlineData(PublicEncodingRules.DER, 8388606, "02037FFFFE")] - [InlineData(PublicEncodingRules.BER, 8388608, "020400800000")] - [InlineData(PublicEncodingRules.CER, 8388609, "020400800001")] - [InlineData(PublicEncodingRules.DER, 16777215, "020400FFFFFF")] - [InlineData(PublicEncodingRules.BER, 16777214, "020400FFFFFE")] - [InlineData(PublicEncodingRules.CER, 16777216, "020401000000")] - [InlineData(PublicEncodingRules.DER, 16777217, "020401000001")] - [InlineData(PublicEncodingRules.BER, 2147483647, "02047FFFFFFF")] - [InlineData(PublicEncodingRules.CER, 2147483646, "02047FFFFFFE")] - [InlineData(PublicEncodingRules.DER, 2147483648, "02050080000000")] - [InlineData(PublicEncodingRules.BER, 2147483649, "02050080000001")] - [InlineData(PublicEncodingRules.BER, 4294967295, "020500FFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 4294967294, "020500FFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 4294967296, "02050100000000")] - [InlineData(PublicEncodingRules.BER, 4294967297, "02050100000001")] - [InlineData(PublicEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 549755813888, "0206008000000000")] - [InlineData(PublicEncodingRules.CER, 549755813889, "0206008000000001")] - [InlineData(PublicEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 1099511627776, "0206010000000000")] - [InlineData(PublicEncodingRules.DER, 1099511627777, "0206010000000001")] - [InlineData(PublicEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 140737488355328, "020700800000000000")] - [InlineData(PublicEncodingRules.BER, 140737488355329, "020700800000000001")] - [InlineData(PublicEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 281474976710656, "020701000000000000")] - [InlineData(PublicEncodingRules.CER, 281474976710657, "020701000000000001")] - [InlineData(PublicEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 36028797018963968, "02080080000000000000")] - [InlineData(PublicEncodingRules.DER, 36028797018963969, "02080080000000000001")] - [InlineData(PublicEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 72057594037927936, "02080100000000000000")] - [InlineData(PublicEncodingRules.BER, 72057594037927937, "02080100000000000001")] - [InlineData(PublicEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_Long(PublicEncodingRules ruleSet, long value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "020100")] - [InlineData(PublicEncodingRules.CER, 0, "020100")] - [InlineData(PublicEncodingRules.DER, 0, "020100")] - [InlineData(PublicEncodingRules.BER, 1, "020101")] - [InlineData(PublicEncodingRules.CER, 127, "02017F")] - [InlineData(PublicEncodingRules.DER, 126, "02017E")] - [InlineData(PublicEncodingRules.BER, 128, "02020080")] - [InlineData(PublicEncodingRules.CER, 129, "02020081")] - [InlineData(PublicEncodingRules.DER, 254, "020200FE")] - [InlineData(PublicEncodingRules.BER, 255, "020200FF")] - [InlineData(PublicEncodingRules.CER, 256, "02020100")] - [InlineData(PublicEncodingRules.DER, 32767, "02027FFF")] - [InlineData(PublicEncodingRules.BER, 32766, "02027FFE")] - [InlineData(PublicEncodingRules.CER, 32768, "0203008000")] - [InlineData(PublicEncodingRules.DER, 32769, "0203008001")] - [InlineData(PublicEncodingRules.BER, 65535, "020300FFFF")] - [InlineData(PublicEncodingRules.CER, 65534, "020300FFFE")] - [InlineData(PublicEncodingRules.DER, 65536, "0203010000")] - [InlineData(PublicEncodingRules.BER, 65537, "0203010001")] - [InlineData(PublicEncodingRules.CER, 8388607, "02037FFFFF")] - [InlineData(PublicEncodingRules.DER, 8388606, "02037FFFFE")] - [InlineData(PublicEncodingRules.BER, 8388608, "020400800000")] - [InlineData(PublicEncodingRules.CER, 8388609, "020400800001")] - [InlineData(PublicEncodingRules.DER, 16777215, "020400FFFFFF")] - [InlineData(PublicEncodingRules.BER, 16777214, "020400FFFFFE")] - [InlineData(PublicEncodingRules.CER, 16777216, "020401000000")] - [InlineData(PublicEncodingRules.DER, 16777217, "020401000001")] - [InlineData(PublicEncodingRules.BER, 2147483647, "02047FFFFFFF")] - [InlineData(PublicEncodingRules.CER, 2147483646, "02047FFFFFFE")] - [InlineData(PublicEncodingRules.DER, 2147483648, "02050080000000")] - [InlineData(PublicEncodingRules.BER, 2147483649, "02050080000001")] - [InlineData(PublicEncodingRules.BER, 4294967295, "020500FFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 4294967294, "020500FFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 4294967296, "02050100000000")] - [InlineData(PublicEncodingRules.BER, 4294967297, "02050100000001")] - [InlineData(PublicEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 549755813888, "0206008000000000")] - [InlineData(PublicEncodingRules.CER, 549755813889, "0206008000000001")] - [InlineData(PublicEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 1099511627776, "0206010000000000")] - [InlineData(PublicEncodingRules.DER, 1099511627777, "0206010000000001")] - [InlineData(PublicEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 140737488355328, "020700800000000000")] - [InlineData(PublicEncodingRules.BER, 140737488355329, "020700800000000001")] - [InlineData(PublicEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 281474976710656, "020701000000000000")] - [InlineData(PublicEncodingRules.CER, 281474976710657, "020701000000000001")] - [InlineData(PublicEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 36028797018963968, "02080080000000000000")] - [InlineData(PublicEncodingRules.DER, 36028797018963969, "02080080000000000001")] - [InlineData(PublicEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 72057594037927936, "02080100000000000000")] - [InlineData(PublicEncodingRules.BER, 72057594037927937, "02080100000000000001")] - [InlineData(PublicEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 9223372036854775808, "0209008000000000000000")] - [InlineData(PublicEncodingRules.CER, 9223372036854775809, "0209008000000000000001")] - [InlineData(PublicEncodingRules.DER, ulong.MaxValue, "020900FFFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, ulong.MaxValue-1, "020900FFFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_ULong(PublicEncodingRules ruleSet, ulong value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0", "020100")] - [InlineData(PublicEncodingRules.CER, "127", "02017F")] - [InlineData(PublicEncodingRules.DER, "128", "02020080")] - [InlineData(PublicEncodingRules.BER, "32767", "02027FFF")] - [InlineData(PublicEncodingRules.CER, "32768", "0203008000")] - [InlineData(PublicEncodingRules.DER, "9223372036854775807", "02087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, "9223372036854775808", "0209008000000000000000")] - [InlineData(PublicEncodingRules.CER, "18446744073709551615", "020900FFFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, "18446744073709551616", "0209010000000000000000")] - [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "02100102030405060708090A0B0C0D0E0F00")] - [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "021100F0E0D0C0B0A090807060504030201000")] - [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "0210FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_BigInteger(PublicEncodingRules ruleSet, string decimalValue, string expectedHex) - { - BigInteger value = BigInteger.Parse(decimalValue); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "470100")] - [InlineData(PublicEncodingRules.CER, long.MinValue + 1, "47088000000000000001")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "47087FFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_Application7_Long(PublicEncodingRules ruleSet, long value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.Application, 7), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "890100")] - [InlineData(PublicEncodingRules.CER, 9223372036854775809, "8909008000000000000001")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "89087FFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_Context9_ULong(PublicEncodingRules ruleSet, ulong value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 9), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0", "D00100")] - [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "D0100102030405060708090A0B0C0D0E0F00")] - [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "D01100F0E0D0C0B0A090807060504030201000")] - [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "D010FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_Private16_BigInteger( - PublicEncodingRules ruleSet, - string decimalValue, - string expectedHex) - { - BigInteger value = BigInteger.Parse(decimalValue); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.Private, 16), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData("00")] - [InlineData("01")] - [InlineData("80")] - [InlineData("FF")] - [InlineData("0080")] - [InlineData("00FF")] - [InlineData("8000")] - [InlineData("00F0E0D0C0B0A090807060504030201000")] - [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_EncodedBytes(string valueHex) - { - string expectedHex = "02" + (valueHex.Length / 2).ToString("X2") + valueHex; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteInteger(valueHex.HexToByteArray()); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData("00")] - [InlineData("01")] - [InlineData("80")] - [InlineData("FF")] - [InlineData("0080")] - [InlineData("00FF")] - [InlineData("8000")] - [InlineData("00F0E0D0C0B0A090807060504030201000")] - [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_Context4_EncodedBytes(string valueHex) - { - string expectedHex = "84" + (valueHex.Length / 2).ToString("X2") + valueHex; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 4), valueHex.HexToByteArray()); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData("")] - [InlineData("0000")] - [InlineData("0000000000000000000001")] - [InlineData("0001")] - [InlineData("007F")] - [InlineData("FFFF")] - [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] - [InlineData("FF80")] - public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valuHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.ThrowsAny(() => writer.WriteInteger(valuHex.HexToByteArray())); - } - } - - [Theory] - [InlineData("")] - [InlineData("0000")] - [InlineData("0000000000000000000001")] - [InlineData("0001")] - [InlineData("007F")] - [InlineData("FFFF")] - [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] - [InlineData("FF80")] - public void VerifyWriteInteger_Application3_InvalidEncodedValue_Throws(string valuHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); - - Assert.ThrowsAny( - () => writer.WriteInteger(tag, valuHex.HexToByteArray())); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteInteger_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.EndOfContents, 0L)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.EndOfContents, 0UL)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.EndOfContents, BigInteger.Zero)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteInteger_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), 0L); - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), 0L); - writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), 0UL); - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), 0UL); - writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), BigInteger.Zero); - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), BigInteger.Zero); - - Verify(writer, "020100800100020100800100020100800100"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteInteger(1)); - - Assert.Throws( - () => writer.WriteInteger(1UL)); - - Assert.Throws( - () => writer.WriteInteger(BigInteger.One)); - - Assert.Throws( - () => writer.WriteInteger(BigInteger.One.ToByteArray())); - - Assert.Throws( - () => writer.WriteInteger(Array.Empty())); - - Assert.Throws( - () => writer.WriteInteger(new byte[] { 0, 0 })); - - Assert.Throws( - () => writer.WriteInteger(new byte[] { 0xFF, 0xFF })); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, 1)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, 1UL)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, BigInteger.One)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, BigInteger.One.ToByteArray())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, Array.Empty())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, new byte[] { 0, 0 })); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, new byte[] { 0xFF, 0xFF })); - - Asn1Tag tag = new Asn1Tag(TagClass.Application, 0); - - Assert.Throws( - () => writer.WriteInteger(tag, 1)); - - Assert.Throws( - () => writer.WriteInteger(tag, 1UL)); - - Assert.Throws( - () => writer.WriteInteger(tag, BigInteger.One)); - - Assert.Throws( - () => writer.WriteInteger(tag, BigInteger.One.ToByteArray())); - - Assert.Throws( - () => writer.WriteInteger(tag, Array.Empty())); - - Assert.Throws( - () => writer.WriteInteger(tag, new byte[] { 0, 0 })); - - Assert.Throws( - () => writer.WriteInteger(tag, new byte[] { 0xFF, 0xFF })); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs deleted file mode 100644 index f4de4f56372131..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs +++ /dev/null @@ -1,347 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteNamedBitList : Asn1WriterTests - { - [Theory] - [InlineData( - PublicEncodingRules.BER, - "030100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.CER, - "030100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.DER, - "030100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.BER, - "0309000000000000000003", - ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] - [InlineData( - PublicEncodingRules.CER, - "0309010000000080000002", - ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] - [InlineData( - PublicEncodingRules.DER, - "030204B0", - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] - public static void VerifyWriteNamedBitList( - PublicEncodingRules ruleSet, - string expectedHex, - object value) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNamedBitList(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData( - PublicEncodingRules.BER, - "C00100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.CER, - "410100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.DER, - "820100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.BER, - "C009000000000000000003", - ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] - [InlineData( - PublicEncodingRules.CER, - "4109010000000080000002", - ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] - [InlineData( - PublicEncodingRules.DER, - "820204B0", - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] - public static void VerifyWriteNamedBitList_WithTag( - PublicEncodingRules ruleSet, - string expectedHex, - object value) - { - int ruleSetVal = (int)ruleSet; - TagClass tagClass = (TagClass)(byte)(ruleSetVal << 6); - - if (tagClass == TagClass.Universal) - tagClass = TagClass.Private; - - Asn1Tag tag = new Asn1Tag(tagClass, ruleSetVal); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNamedBitList(tag, value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_Generic(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - var flagsValue = - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; - - genWriter.WriteNamedBitList(flagsValue); - objWriter.WriteNamedBitList((object)flagsValue); - - Verify(genWriter, objWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_Generic_WithTag(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); - - var flagsValue = - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; - - genWriter.WriteNamedBitList(tag, flagsValue); - objWriter.WriteNamedBitList(tag, (object)flagsValue); - - Verify(genWriter, objWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_NonNull(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(null)); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), null)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_EnumRequired(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteNamedBitList(3)); - - Assert.Throws( - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), 3)); - - Assert.Throws( - () => writer.WriteNamedBitList((object)3)); - - Assert.Throws( - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), (object)3)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_FlagsEnumRequired(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(AsnEncodingRules.BER)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList( - new Asn1Tag(TagClass.ContextSpecific, 1), - AsnEncodingRules.BER)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList((object)AsnEncodingRules.BER)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList( - new Asn1Tag(TagClass.ContextSpecific, 1), - (object)AsnEncodingRules.BER)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList( - Asn1Tag.EndOfContents, - StringSplitOptions.RemoveEmptyEntries)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList( - Asn1Tag.EndOfContents, - (object)StringSplitOptions.RemoveEmptyEntries)); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList(false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList((object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList((object)null)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList(ReadNamedBitList.LongFlags.Mid)); - - // Type is not [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(ReadEnumerated.UIntBacked.Fluff)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList((object)ReadNamedBitList.ULongFlags.Mid)); - - // Unboxed type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList((object)ReadEnumerated.SByteBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, ReadNamedBitList.LongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, ReadEnumerated.UIntBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)ReadNamedBitList.ULongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)ReadEnumerated.SByteBacked.Fluff)); - - Asn1Tag tag = new Asn1Tag(TagClass.Private, 6); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList(tag, false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList(tag, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(tag, (object)null)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList(tag, ReadNamedBitList.LongFlags.Mid)); - - // Type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(tag, ReadEnumerated.UIntBacked.Fluff)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList(tag, (object)ReadNamedBitList.ULongFlags.Mid)); - - // Unboxed type is not [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(tag, (object)ReadEnumerated.SByteBacked.Fluff)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs deleted file mode 100644 index f6d2cc0960922b..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteNull : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNull(); - - Verify(writer, "0500"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteNull(Asn1Tag.EndOfContents)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNull(new Asn1Tag(TagClass.ContextSpecific, 7, true)); - writer.WriteNull(new Asn1Tag(UniversalTagNumber.Null, true)); - - Verify(writer, "87000500"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteBoolean(true); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteNull()); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNull(Asn1Tag.Integer)); - - Assert.Throws( - () => writer.WriteNull(new Asn1Tag(TagClass.Private, 3))); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs deleted file mode 100644 index e488209ee9b5c0..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs +++ /dev/null @@ -1,533 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteObjectIdentifier : Asn1WriterTests - { - [Theory] - [MemberData(nameof(ValidOidData))] - public void VerifyWriteObjectIdentifier_String( - PublicEncodingRules ruleSet, - string oidValue, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidValue); - - Verify(writer, expectedHex); - } - } - - [Theory] - [MemberData(nameof(ValidOidData))] - public void VerifyWriteObjectIdentifier_Span( - PublicEncodingRules ruleSet, - string oidValue, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidValue.AsSpan()); - - Verify(writer, expectedHex); - } - } - - [Theory] - [MemberData(nameof(ValidOidData))] - public void VerifyWriteObjectIdentifier_Oid( - PublicEncodingRules ruleSet, - string oidValue, - string expectedHex) - { - Oid oidObj = new Oid(oidValue, "FriendlyName does not matter"); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidObj); - - Verify(writer, expectedHex); - } - } - - [Theory] - [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_String( - string description, - PublicEncodingRules ruleSet, - string nonOidValue) - { - _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (nonOidValue == null) - { - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(nonOidValue)); - } - else - { - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidValue)); - } - } - } - - [Theory] - [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_Span( - string description, - PublicEncodingRules ruleSet, - string nonOidValue) - { - _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidValue.AsSpan())); - } - } - - [Theory] - [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_Oid( - string description, - PublicEncodingRules ruleSet, - string nonOidValue) - { - _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Oid nonOidObj = new Oid(nonOidValue, "FriendlyName does not matter"); - - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidObj)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_String(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 3), "1.3.14.3.2.26"); - - Verify(writer, "83052B0E03021A"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_Span(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), "1.3.14.3.2.26".AsSpan()); - - Verify(writer, "42052B0E03021A"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_Oid(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier( - new Asn1Tag(TagClass.Private, 36), - Oid.FromFriendlyName("SHA1", OidGroup.HashAlgorithm)); - - Verify(writer, "DF24052B0E03021A"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteObjectIdentifier_NullString(bool defaultTag) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - AssertExtensions.Throws( - "oidValue", - () => - { - if (defaultTag) - { - writer.WriteObjectIdentifier((string)null); - } - else - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 6), (string)null); - } - }); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteObjectIdentifier_NullOid(bool defaultTag) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - AssertExtensions.Throws( - "oid", - () => - { - if (defaultTag) - { - writer.WriteObjectIdentifier((Oid)null); - } - else - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), (Oid)null); - } - }); - } - } - - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteObjectIdentifier_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, "1.1")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, "1.1".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, new Oid("1.1", "1.1"))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteObjectIdentifier_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - const string OidValue = "1.1"; - Asn1Tag constructedOid = new Asn1Tag(UniversalTagNumber.ObjectIdentifier, isConstructed: true); - Asn1Tag constructedContext0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); - - writer.WriteObjectIdentifier(constructedOid, OidValue); - writer.WriteObjectIdentifier(constructedContext0, OidValue); - writer.WriteObjectIdentifier(constructedOid, OidValue.AsSpan()); - writer.WriteObjectIdentifier(constructedContext0, OidValue.AsSpan()); - writer.WriteObjectIdentifier(constructedOid, new Oid(OidValue, OidValue)); - writer.WriteObjectIdentifier(constructedContext0, new Oid(OidValue, OidValue)); - - Verify(writer, "060129800129060129800129060129800129"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0q")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("123")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("4.0")); - - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier((string)null)); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0q".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("123".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("4.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("0.0", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("0", "short"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("0.0q", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("123", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("4.0", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid(null, "null"))); - - AssertExtensions.Throws( - "oid", - () => writer.WriteObjectIdentifier((Oid)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.Integer, false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0q")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "123")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "4.0")); - - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, (string)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0q".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "123".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "4.0".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("0.0", "valid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("0", "short"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("0.0q", "invalid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("123", "invalid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("4.0", "invalid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid(null, "null"))); - - AssertExtensions.Throws( - "oid", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, (Oid)null)); - - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 123); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0q")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "123")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "4.0")); - - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(tag, (string)null)); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0q".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "123".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "4.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("0.0", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("0", "short"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("0.0q", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("123", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("4.0", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid(null, "null"))); - - AssertExtensions.Throws( - "oid", - () => writer.WriteObjectIdentifier(tag, (Oid)null)); - } - } - - public static IEnumerable ValidOidData { get; } = - new object[][] - { - new object[] - { - PublicEncodingRules.BER, - "0.0", - "060100", - }, - new object[] - { - PublicEncodingRules.CER, - "1.0", - "060128", - }, - new object[] - { - PublicEncodingRules.DER, - "2.0", - "060150", - }, - new object[] - { - PublicEncodingRules.BER, - "1.3.14.3.2.26", - "06052B0E03021A", - }, - new object[] - { - PublicEncodingRules.CER, - "2.999.19427512891.25", - "06088837C8AFE1A43B19", - }, - new object[] - { - PublicEncodingRules.DER, - "1.2.840.113549.1.1.10", - "06092A864886F70D01010A", - }, - new object[] - { - // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and - // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 - // this is - // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } - PublicEncodingRules.DER, - "2.25.329800735698586629295641978511506172918.3", - "06156983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", - }, - }; - - public static IEnumerable InvalidOidData { get; } = - new object[][] - { - new object[] { "Null", PublicEncodingRules.BER, null }, - new object[] { "Empty string", PublicEncodingRules.BER, "" }, - new object[] { "No period", PublicEncodingRules.CER, "1" }, - new object[] { "No second RID", PublicEncodingRules.DER, "1." }, - new object[] { "Invalid first RID", PublicEncodingRules.BER, "3.0" }, - new object[] { "Invalid first RID - multichar", PublicEncodingRules.CER, "27.0" }, - new object[] { "Double zero - First RID", PublicEncodingRules.DER, "00.0" }, - new object[] { "Leading zero - First RID", PublicEncodingRules.BER, "01.0" }, - new object[] { "Double zero - second RID", PublicEncodingRules.CER, "0.00" }, - new object[] { "Leading zero - second RID", PublicEncodingRules.DER, "0.01" }, - new object[] { "Double-period", PublicEncodingRules.BER, "0..0" }, - new object[] { "Ends with period - second RID", PublicEncodingRules.BER, "0.0." }, - new object[] { "Ends with period - third RID", PublicEncodingRules.BER, "0.1.30." }, - new object[] { "Double zero - third RID", PublicEncodingRules.CER, "0.1.00" }, - new object[] { "Leading zero - third RID", PublicEncodingRules.DER, "0.1.023" }, - new object[] { "Invalid character first position", PublicEncodingRules.BER, "a.1.23" }, - new object[] { "Invalid character second position", PublicEncodingRules.CER, "0,1.23" }, - new object[] { "Invalid character second rid", PublicEncodingRules.DER, "0.1q.23" }, - new object[] { "Invalid character third rid", PublicEncodingRules.BER, "0.1.23q" }, - }; - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs deleted file mode 100644 index f0c7f9854df1bb..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs +++ /dev/null @@ -1,193 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteOctetString : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteEmpty(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(ReadOnlySpan.Empty); - - Verify(writer, "0400"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 1, "0401")] - [InlineData(PublicEncodingRules.CER, 2, "0402")] - [InlineData(PublicEncodingRules.DER, 3, "0403")] - [InlineData(PublicEncodingRules.BER, 126, "047E")] - [InlineData(PublicEncodingRules.CER, 127, "047F")] - [InlineData(PublicEncodingRules.DER, 128, "048180")] - [InlineData(PublicEncodingRules.BER, 1000, "048203E8")] - [InlineData(PublicEncodingRules.CER, 1000, "048203E8")] - [InlineData(PublicEncodingRules.DER, 1000, "048203E8")] - [InlineData(PublicEncodingRules.BER, 1001, "048203E9")] - [InlineData(PublicEncodingRules.DER, 1001, "048203E9")] - [InlineData(PublicEncodingRules.BER, 2001, "048207D1")] - [InlineData(PublicEncodingRules.DER, 2001, "048207D1")] - public void WritePrimitive(PublicEncodingRules ruleSet, int length, string hexStart) - { - string payloadHex = new string('0', 2 * length); - string expectedHex = hexStart + payloadHex; - byte[] data = new byte[length]; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(data); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(1001, "2480048203E8", "0401")] - [InlineData(1999, "2480048203E8", "048203E7")] - [InlineData(2000, "2480048203E8", "048203E8")] - public void WriteSegmentedCER(int length, string hexStart, string hexStart2) - { - string payload1Hex = new string('8', 2000); - string payload2Hex = new string('8', (length - 1000) * 2); - string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; - byte[] data = new byte[length]; - - for (int i = 0; i < data.Length; i++) - { - data[i] = 0x88; - } - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteOctetString(data); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, false)] - [InlineData(PublicEncodingRules.CER, 0, false)] - [InlineData(PublicEncodingRules.DER, 0, false)] - [InlineData(PublicEncodingRules.BER, 999, false)] - [InlineData(PublicEncodingRules.CER, 999, false)] - [InlineData(PublicEncodingRules.DER, 999, false)] - [InlineData(PublicEncodingRules.BER, 1000, false)] - [InlineData(PublicEncodingRules.CER, 1000, false)] - [InlineData(PublicEncodingRules.DER, 1000, false)] - [InlineData(PublicEncodingRules.BER, 1001, false)] - [InlineData(PublicEncodingRules.CER, 1001, true)] - [InlineData(PublicEncodingRules.DER, 1001, false)] - [InlineData(PublicEncodingRules.BER, 1998, false)] - [InlineData(PublicEncodingRules.CER, 1998, true)] - [InlineData(PublicEncodingRules.DER, 1998, false)] - [InlineData(PublicEncodingRules.BER, 1999, false)] - [InlineData(PublicEncodingRules.CER, 1999, true)] - [InlineData(PublicEncodingRules.DER, 1999, false)] - [InlineData(PublicEncodingRules.BER, 2000, false)] - [InlineData(PublicEncodingRules.CER, 2000, true)] - [InlineData(PublicEncodingRules.DER, 2000, false)] - [InlineData(PublicEncodingRules.BER, 2001, false)] - [InlineData(PublicEncodingRules.CER, 2001, true)] - [InlineData(PublicEncodingRules.DER, 2001, false)] - [InlineData(PublicEncodingRules.BER, 4096, false)] - [InlineData(PublicEncodingRules.CER, 4096, true)] - [InlineData(PublicEncodingRules.DER, 4096, false)] - public void VerifyWriteOctetString_PrimitiveOrConstructed( - PublicEncodingRules ruleSet, - int payloadLength, - bool expectConstructed) - { - byte[] data = new byte[payloadLength]; - - Asn1Tag[] tagsToTry = - { - new Asn1Tag(UniversalTagNumber.OctetString), - new Asn1Tag(UniversalTagNumber.OctetString, isConstructed: true), - new Asn1Tag(TagClass.Private, 87), - new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), - }; - - byte[] answerBuf = new byte[payloadLength + 100]; - - foreach (Asn1Tag toTry in tagsToTry) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(toTry, data); - - Assert.True(writer.TryEncode(answerBuf, out _)); - } - Assert.True(Asn1Tag.TryDecode(answerBuf, out Asn1Tag writtenTag, out _)); - - if (expectConstructed) - { - Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - else - { - Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - - Assert.Equal(toTry.TagClass, writtenTag.TagClass); - Assert.Equal(toTry.TagValue, writtenTag.TagValue); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteOctetString_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.EndOfContents, new byte[1])); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteOctetString(ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.Integer, ReadOnlySpan.Empty)); - - Assert.Throws( - () => writer.WriteOctetString( - new Asn1Tag(TagClass.Private, 3), - ReadOnlySpan.Empty)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs deleted file mode 100644 index 75e4281ef966ad..00000000000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs +++ /dev/null @@ -1,273 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteUtcTime : Asn1WriterTests - { - public static IEnumerable TestCases { get; } = new object[][] - { - new object[] - { - new DateTimeOffset(2017, 10, 16, 8, 24, 3, TimeSpan.FromHours(-7)), - "0D3137313031363135323430335A", - }, - new object[] - { - new DateTimeOffset(1817, 10, 16, 21, 24, 3, TimeSpan.FromHours(6)), - "0D3137313031363135323430335A", - }, - new object[] - { - new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero), - "0D3030303130313030303030305A", - }, - }; - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_BER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteUtcTime(input); - - Verify(writer, "17" + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_BER_CustomTag(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); - writer.WriteUtcTime(tag, input); - - Verify(writer, Stringify(tag) + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_CER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteUtcTime(input); - - Verify(writer, "17" + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_CER_CustomTag(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); - writer.WriteUtcTime(tag, input); - - Verify(writer, Stringify(tag) + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_DER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteUtcTime(input); - - Verify(writer, "17" + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_DER_CustomTag(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - writer.WriteUtcTime(tag, input); - - Verify(writer, Stringify(tag) + expectedHexPayload); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteUtcTime_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.EndOfContents, DateTimeOffset.Now)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteUtcTime_IgnoresConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); - - writer.WriteUtcTime(new Asn1Tag(UniversalTagNumber.UtcTime, true), value); - writer.WriteUtcTime(new Asn1Tag(TagClass.ContextSpecific, 3, true), value); - Verify(writer, "170D3137313131363137333530315A" + "830D3137313131363137333530315A"); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_RespectsYearMax_DER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Assert.Equal(0, writer.GetEncodedLength()); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(input, input.Year - 1)); - - Assert.Equal(0, writer.GetEncodedLength()); - - writer.WriteUtcTime(input, input.Year); - Assert.Equal(15, writer.GetEncodedLength()); - - writer.WriteUtcTime(input, input.Year + 99); - Assert.Equal(30, writer.GetEncodedLength()); - - writer.Reset(); - - _ = expectedHexPayload; - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(input, input.Year + 100)); - - Assert.Equal(0, writer.GetEncodedLength()); - } - } - - [Fact] - public void VerifyWriteUtcTime_RespectsYearMax_UniversalLimit() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 11); - - // 1950 afte ToUniversal - writer.WriteUtcTime( - tag, - new DateTimeOffset(1949, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), - 2049); - - Assert.Equal(15, writer.GetEncodedLength()); - - // 1949 after ToUniversal - AssertExtensions.Throws( - "value", - () => - writer.WriteUtcTime( - tag, - new DateTimeOffset(1950, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), - 2049)); - - Assert.Equal(15, writer.GetEncodedLength()); - - // 2050 after ToUniversal - AssertExtensions.Throws( - "value", - () => - writer.WriteUtcTime( - tag, - new DateTimeOffset(2049, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), - 2049)); - - Assert.Equal(15, writer.GetEncodedLength()); - - // 1950 afte ToUniversal - writer.WriteUtcTime( - tag, - new DateTimeOffset(2050, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), - 2049); - - Assert.Equal(30, writer.GetEncodedLength()); - - string hex = - Stringify(tag) + "0D3530303130313037313131395A" + - Stringify(tag) + "0D3439313233313139313131395A"; - - Verify(writer, hex); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteUtcTime(DateTimeOffset.Now)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(DateTimeOffset.Now, 1999)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(DateTimeOffset.Now, 8999)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.Integer, DateTimeOffset.Now)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.Integer, DateTimeOffset.Now, 1999)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.Integer, DateTimeOffset.Now, 8999)); - - Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); - - Assert.Throws( - () => writer.WriteUtcTime(tag, DateTimeOffset.Now)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(tag, DateTimeOffset.Now, 1999)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(tag, DateTimeOffset.Now, 8999)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj b/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj index 8ca393597862f1..0102c707f24342 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj @@ -3,49 +3,7 @@ true $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj index 4f7eff09e987c6..3e483c1facb1ae 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj @@ -110,6 +110,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/AsnHelpers.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/AsnHelpers.cs index 64329c05633972..ba13968cdd75b3 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/AsnHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/AsnHelpers.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs; @@ -56,7 +57,7 @@ internal static AlgorithmIdentifier ToPresentationObject(this AlgorithmIdentifie int keyLength; byte[] parameters = Array.Empty(); - switch (asn.Algorithm.Value) + switch (asn.Algorithm) { case Oids.Rc2Cbc: { @@ -97,27 +98,35 @@ internal static AlgorithmIdentifier ToPresentationObject(this AlgorithmIdentifie } int saltLen = 0; - AsnReader reader = new AsnReader(asn.Parameters.Value, AsnEncodingRules.BER); - // DER NULL is considered the same as not present. - // No call to ReadNull() is necessary because the serializer already verified that - // there's no data after the [AnyValue] value. - if (reader.PeekTag() != Asn1Tag.Null) + try { - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents)) - { - saltLen = contents.Length; - } - else - { - Span salt = stackalloc byte[KeyLengths.Rc4Max_128Bit / 8]; + AsnReader reader = new AsnReader(asn.Parameters.Value, AsnEncodingRules.BER); - if (!reader.TryCopyOctetStringBytes(salt, out saltLen)) + // DER NULL is considered the same as not present. + // No call to ReadNull() is necessary because the serializer already verified that + // there's no data after the [AnyValue] value. + if (reader.PeekTag() != Asn1Tag.Null) + { + if (reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents)) { - throw new CryptographicException(); + saltLen = contents.Length; + } + else + { + Span salt = stackalloc byte[KeyLengths.Rc4Max_128Bit / 8]; + + if (!reader.TryReadOctetString(salt, out saltLen)) + { + throw new CryptographicException(); + } } } } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } keyLength = KeyLengths.Rc4Max_128Bit - 8 * saltLen; break; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Asn.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Asn.cs index e39d35fdb20502..9bc4d24bdedda9 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Asn.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Asn.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Formats.Asn1; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs7; namespace Internal.Cryptography.Pal.AnyOS diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs index dea5107d27d866..63f2b1e61e61c2 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs7; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.Pkcs.Asn1; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decrypt.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decrypt.cs index 58ec6b700e7f26..79c5e0805ab856 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decrypt.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decrypt.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs; @@ -117,43 +118,14 @@ public ManagedDecryptorPal( // existing CMS that have the incorrect wrapping, we attempt to remove it. if (contentType == Oids.Pkcs7Data) { - byte[]? tmp = null; - - try + if (decrypted?.Length > 0 && decrypted[0] == 0x04) { - AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER); - - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents)) + try { - decrypted = contents.ToArray(); + decrypted = AsnDecoder.ReadOctetString(decrypted, AsnEncodingRules.BER, out _); } - else + catch (AsnContentException) { - tmp = CryptoPool.Rent(decrypted!.Length); - - if (reader.TryCopyOctetStringBytes(tmp, out int written)) - { - Span innerContents = new Span(tmp, 0, written); - decrypted = innerContents.ToArray(); - innerContents.Clear(); - } - else - { - Debug.Fail("Octet string grew during copy"); - // If this happens (which requires decrypted was overwritten, which - // shouldn't be possible), just leave decrypted alone. - } - } - } - catch (CryptographicException) - { - } - finally - { - if (tmp != null) - { - // Already cleared - CryptoPool.Return(tmp, clearSize: 0); } } } @@ -170,19 +142,18 @@ public ManagedDecryptorPal( private static byte[] GetAsnSequenceWithContentNoValidation(ReadOnlySpan content) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - // Content may be invalid ASN.1 data. - // We will encode it as octet string to bypass validation - writer.WriteOctetString(content); - byte[] encoded = writer.Encode(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); - // and replace octet string tag (0x04) with sequence tag (0x30 or constructed 0x10) - Debug.Assert(encoded[0] == 0x04); - encoded[0] = 0x30; + // Content may be invalid ASN.1 data. + // We will encode it as octet string to bypass validation + writer.WriteOctetString(content); + byte[] encoded = writer.Encode(); - return encoded; - } + // and replace octet string tag (0x04) with sequence tag (0x30 or constructed 0x10) + Debug.Assert(encoded[0] == 0x04); + encoded[0] = 0x30; + + return encoded; } private static byte[]? DecryptContent( diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Encrypt.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Encrypt.cs index 469138050b64ae..002a48c34dd76c 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Encrypt.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Encrypt.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -68,7 +69,7 @@ private byte[] Encrypt( ContentEncryptionAlgorithm = { - Algorithm = contentEncryptionAlgorithm.Oid, + Algorithm = contentEncryptionAlgorithm.Oid.Value!, Parameters = parameterBytes, }, @@ -141,11 +142,9 @@ private byte[] Encrypt( envelopedData.Version = 2; } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - envelopedData.Encode(writer); - return PkcsHelpers.EncodeContentInfo(writer.Encode(), Oids.Pkcs7Enveloped); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + envelopedData.Encode(writer); + return PkcsHelpers.EncodeContentInfo(writer.Encode(), Oids.Pkcs7Enveloped); } private byte[] EncryptContent( @@ -163,11 +162,9 @@ private byte[] EncryptContent( { Rc2CbcParameters rc2Params = new Rc2CbcParameters(alg.IV, alg.KeySize); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - rc2Params.Encode(writer); - parameterBytes = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + rc2Params.Encode(writer); + parameterBytes = writer.Encode(); } else { @@ -186,10 +183,24 @@ private byte[] EncryptContent( { return encryptor.OneShot(contentInfo.Content); } - else + + try + { + AsnDecoder.ReadEncodedValue( + contentInfo.Content, + AsnEncodingRules.BER, + out int contentOffset, + out int contentLength, + out _); + + ReadOnlySpan content = + contentInfo.Content.AsSpan(contentOffset, contentLength); + + return encryptor.OneShot(content.ToArray()); + } + catch (AsnContentException e) { - AsnReader reader = new AsnReader(contentInfo.Content, AsnEncodingRules.BER); - return encryptor.OneShot(reader.PeekContentBytes().ToArray()); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.KeyTrans.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.KeyTrans.cs index e7684f756b31e3..455f326221118e 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.KeyTrans.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.KeyTrans.cs @@ -36,7 +36,7 @@ internal ManagedKeyTransPal(KeyTransRecipientInfoAsn asn) internal byte[]? DecryptCek(X509Certificate2? cert, RSA? privateKey, out Exception? exception) { ReadOnlyMemory? parameters = _asn.KeyEncryptionAlgorithm.Parameters; - string? keyEncryptionAlgorithm = _asn.KeyEncryptionAlgorithm.Algorithm.Value; + string? keyEncryptionAlgorithm = _asn.KeyEncryptionAlgorithm.Algorithm; switch (keyEncryptionAlgorithm) { @@ -53,7 +53,7 @@ internal ManagedKeyTransPal(KeyTransRecipientInfoAsn asn) default: exception = new CryptographicException( SR.Cryptography_Cms_UnknownAlgorithm, - _asn.KeyEncryptionAlgorithm.Algorithm.Value); + _asn.KeyEncryptionAlgorithm.Algorithm); return null; } @@ -141,27 +141,27 @@ private KeyTransRecipientInfoAsn MakeKtri( if (padding == RSAEncryptionPadding.Pkcs1) { - ktri.KeyEncryptionAlgorithm.Algorithm = new Oid(Oids.Rsa, Oids.Rsa); + ktri.KeyEncryptionAlgorithm.Algorithm = Oids.Rsa; ktri.KeyEncryptionAlgorithm.Parameters = s_rsaPkcsParameters; } else if (padding == RSAEncryptionPadding.OaepSHA1) { - ktri.KeyEncryptionAlgorithm.Algorithm = new Oid(Oids.RsaOaep, Oids.RsaOaep); + ktri.KeyEncryptionAlgorithm.Algorithm = Oids.RsaOaep; ktri.KeyEncryptionAlgorithm.Parameters = s_rsaOaepSha1Parameters; } else if (padding == RSAEncryptionPadding.OaepSHA256) { - ktri.KeyEncryptionAlgorithm.Algorithm = new Oid(Oids.RsaOaep, Oids.RsaOaep); + ktri.KeyEncryptionAlgorithm.Algorithm = Oids.RsaOaep; ktri.KeyEncryptionAlgorithm.Parameters = s_rsaOaepSha256Parameters; } else if (padding == RSAEncryptionPadding.OaepSHA384) { - ktri.KeyEncryptionAlgorithm.Algorithm = new Oid(Oids.RsaOaep, Oids.RsaOaep); + ktri.KeyEncryptionAlgorithm.Algorithm = Oids.RsaOaep; ktri.KeyEncryptionAlgorithm.Parameters = s_rsaOaepSha384Parameters; } else if (padding == RSAEncryptionPadding.OaepSHA512) { - ktri.KeyEncryptionAlgorithm.Algorithm = new Oid(Oids.RsaOaep, Oids.RsaOaep); + ktri.KeyEncryptionAlgorithm.Algorithm = Oids.RsaOaep; ktri.KeyEncryptionAlgorithm.Parameters = s_rsaOaepSha512Parameters; } else diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.cs index 3386f884fb0453..4d475a43c6e2d4 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs; @@ -44,20 +45,27 @@ public override byte[] GetSubjectKeyIdentifier(X509Certificate2 certificate) false); } - // Certificates are DER encoded. - AsnReader reader = new AsnReader(extension.RawData, AsnEncodingRules.DER); + try + { + // Certificates are DER encoded. + AsnValueReader reader = new AsnValueReader(extension.RawData, AsnEncodingRules.DER); - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents)) + if (reader.TryReadPrimitiveOctetString(out ReadOnlySpan contents)) + { + reader.ThrowIfNotEmpty(); + return contents.ToArray(); + } + + // TryGetPrimitiveOctetStringBytes will have thrown if the next tag wasn't + // Universal (primitive) OCTET STRING, since we're in DER mode. + // So there's really no way we can get here. + Debug.Fail($"TryGetPrimitiveOctetStringBytes returned false in DER mode"); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + catch (AsnContentException e) { - reader.ThrowIfNotEmpty(); - return contents.ToArray(); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - // TryGetPrimitiveOctetStringBytes will have thrown if the next tag wasn't - // Universal (primitive) OCTET STRING, since we're in DER mode. - // So there's really no way we can get here. - Debug.Fail($"TryGetPrimitiveOctetStringBytes returned false in DER mode"); - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } [return: MaybeNull] @@ -114,23 +122,19 @@ private static SymmetricAlgorithm OpenAlgorithm(AlgorithmIdentifierAsn contentEn throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - AsnReader reader = new AsnReader(contentEncryptionAlgorithm.Parameters.Value, AsnEncodingRules.BER); - - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory primitiveBytes)) - { - alg.IV = primitiveBytes.ToArray(); - } - else + try { - byte[] iv = new byte[alg.BlockSize / 8]; + AsnReader reader = new AsnReader(contentEncryptionAlgorithm.Parameters.Value, AsnEncodingRules.BER); + alg.IV = reader.ReadOctetString(); - if (!reader.TryCopyOctetStringBytes(iv, out int bytesWritten) || - bytesWritten != iv.Length) + if (alg.IV.Length != alg.BlockSize / 8) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - - alg.IV = iv; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } } @@ -139,7 +143,7 @@ private static SymmetricAlgorithm OpenAlgorithm(AlgorithmIdentifierAsn contentEn private static SymmetricAlgorithm OpenAlgorithm(AlgorithmIdentifier algorithmIdentifier) { - SymmetricAlgorithm alg = OpenAlgorithm(algorithmIdentifier.Oid); + SymmetricAlgorithm alg = OpenAlgorithm(algorithmIdentifier.Oid.Value!); if (alg is RC2) { @@ -156,13 +160,13 @@ private static SymmetricAlgorithm OpenAlgorithm(AlgorithmIdentifier algorithmIde return alg; } - private static SymmetricAlgorithm OpenAlgorithm(Oid algorithmIdentifier) + private static SymmetricAlgorithm OpenAlgorithm(string algorithmIdentifier) { Debug.Assert(algorithmIdentifier != null); SymmetricAlgorithm alg; - switch (algorithmIdentifier.Value) + switch (algorithmIdentifier) { case Oids.Rc2Cbc: #pragma warning disable CA5351 @@ -192,7 +196,7 @@ private static SymmetricAlgorithm OpenAlgorithm(Oid algorithmIdentifier) alg.KeySize = 256; break; default: - throw new CryptographicException(SR.Cryptography_Cms_UnknownAlgorithm, algorithmIdentifier.Value); + throw new CryptographicException(SR.Cryptography_Cms_UnknownAlgorithm, algorithmIdentifier); } // These are the defaults, but they're restated here for clarity. diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decode.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decode.cs index cf4c423211ddb1..7397177c493fbc 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decode.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/DecryptorPalWindows.Decode.cs @@ -48,7 +48,7 @@ out CryptographicAttributeObjectCollection unprotectedAttributes { CRYPT_ALGORITHM_IDENTIFIER* pCryptAlgorithmIdentifier = (CRYPT_ALGORITHM_IDENTIFIER*)(sh.DangerousGetHandle()); contentEncryptionAlgorithm = (*pCryptAlgorithmIdentifier).ToAlgorithmIdentifier(); - contentEncryptionAlgorithmAsn.Algorithm = contentEncryptionAlgorithm.Oid; + contentEncryptionAlgorithmAsn.Algorithm = contentEncryptionAlgorithm.Oid.Value!; contentEncryptionAlgorithmAsn.Parameters = (*pCryptAlgorithmIdentifier).Parameters.ToByteArray(); } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.Encrypt.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.Encrypt.cs index 539dd52d9dcb72..e6e66a8308097d 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.Encrypt.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.Encrypt.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Text; using System.Diagnostics; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; @@ -15,7 +15,6 @@ using Microsoft.Win32.SafeHandles; using static Interop.Crypt32; -using System.Security.Cryptography.Asn1; namespace Internal.Cryptography.Pal.Windows { @@ -69,21 +68,37 @@ public sealed unsafe override byte[] Encrypt(CmsRecipientCollection recipients, private static void ReencodeIfUsingIndefiniteLengthEncodingOnOuterStructure(ref byte[] encodedContent) { - AsnReader reader = new AsnReader(encodedContent, AsnEncodingRules.BER); - reader.ReadTagAndLength(out int? contentsLength, out int _); - - if (contentsLength != null) + try { - // definite length, do nothing - return; - } + Asn1Tag.Decode(encodedContent, out int tagLength); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { + if (encodedContent.Length <= tagLength) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (encodedContent[tagLength] != 0x80) + { + // definite length, do nothing + return; + } + + AsnDecoder.ReadEncodedValue( + encodedContent, + AsnEncodingRules.BER, + out int contentOffset, + out int contentLength, + out _); + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); // Tag doesn't matter here as we won't write it into the document - writer.WriteOctetString(reader.PeekContentBytes().Span); + writer.WriteOctetString(encodedContent.AsSpan(contentOffset, contentLength)); encodedContent = writer.Encode(); } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } // diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsHelpers.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsHelpers.cs index 9e59207b5902aa..35ec0201859d73 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsHelpers.cs @@ -4,18 +4,17 @@ using System; using System.Buffers; -using System.Collections.Generic; -using System.Text; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.IO; -using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs7; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; +using System.Text; using X509IssuerSerial = System.Security.Cryptography.Xml.X509IssuerSerial; -using System.Diagnostics.CodeAnalysis; namespace Internal.Cryptography { @@ -123,32 +122,41 @@ public static AttributeAsn[] NormalizeAttributeSet( { byte[] normalizedValue; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSetOf(); + + foreach (AttributeAsn item in setItems) { - writer.PushSetOf(); - foreach (AttributeAsn item in setItems) - { - item.Encode(writer); - } - writer.PopSetOf(); - normalizedValue = writer.Encode(); - if (encodedValueProcessor != null) + item.Encode(writer); + } + + writer.PopSetOf(); + normalizedValue = writer.Encode(); + + if (encodedValueProcessor != null) + { + encodedValueProcessor(normalizedValue); + } + + try + { + AsnValueReader reader = new AsnValueReader(normalizedValue, AsnEncodingRules.DER); + AsnValueReader setReader = reader.ReadSetOf(); + AttributeAsn[] decodedSet = new AttributeAsn[setItems.Length]; + int i = 0; + while (setReader.HasData) { - encodedValueProcessor(normalizedValue); + AttributeAsn.Decode(ref setReader, normalizedValue, out AttributeAsn item); + decodedSet[i] = item; + i++; } - } - AsnValueReader reader = new AsnValueReader(normalizedValue, AsnEncodingRules.DER); - AsnValueReader setReader = reader.ReadSetOf(); - AttributeAsn[] decodedSet = new AttributeAsn[setItems.Length]; - int i = 0; - while (setReader.HasData) + return decodedSet; + } + catch (AsnContentException e) { - AttributeAsn.Decode(ref setReader, normalizedValue, out AttributeAsn item); - decodedSet[i] = item; - i++; + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - return decodedSet; } internal static byte[] EncodeContentInfo( @@ -162,11 +170,9 @@ internal static byte[] EncodeContentInfo( Content = content, }; - using (AsnWriter writer = new AsnWriter(ruleSet)) - { - contentInfo.Encode(writer); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(ruleSet); + contentInfo.Encode(writer); + return writer.Encode(); } public static CmsRecipientCollection DeepCopy(this CmsRecipientCollection recipients) @@ -445,128 +451,143 @@ internal static byte[] OneShot(this ICryptoTransform transform, byte[] data, int } } - public static ReadOnlyMemory DecodeOctetStringAsMemory(ReadOnlyMemory encodedOctetString) + public static void EnsureSingleBerValue(ReadOnlySpan source) { - AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER); - - if (reader.PeekEncodedValue().Length != encodedOctetString.Length) + if (!AsnDecoder.TryReadEncodedValue(source, AsnEncodingRules.BER, out _, out _, out _, out int consumed) || + consumed != source.Length) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } + } - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory primitiveContents)) - { - return primitiveContents; - } - - byte[] tooBig = new byte[encodedOctetString.Length]; - - if (reader.TryCopyOctetStringBytes(tooBig, out int bytesWritten)) + public static int FirstBerValueLength(ReadOnlySpan source) + { + if (!AsnDecoder.TryReadEncodedValue(source, AsnEncodingRules.BER, out _, out _, out _, out int consumed)) { - return tooBig.AsMemory(0, bytesWritten); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - Debug.Fail("TryCopyOctetStringBytes failed with an over-allocated array"); - throw new CryptographicException(); + return consumed; } - public static byte[] DecodeOctetString(ReadOnlyMemory encodedOctets) + public static ReadOnlyMemory DecodeOctetStringAsMemory(ReadOnlyMemory encodedOctetString) { - // Read using BER because the CMS specification says the encoding is BER. - AsnReader reader = new AsnReader(encodedOctets, AsnEncodingRules.BER); - - const int ArbitraryStackLimit = 256; - Span tmp = stackalloc byte[ArbitraryStackLimit]; - // Use stackalloc 0 so data can later hold a slice of tmp. - ReadOnlySpan data = stackalloc byte[0]; - byte[]? poolBytes = null; - try { - if (!reader.TryReadPrimitiveOctetStringBytes(out var contents)) + ReadOnlySpan input = encodedOctetString.Span; + + if (AsnDecoder.TryReadPrimitiveOctetString( + input, + AsnEncodingRules.BER, + out ReadOnlySpan primitive, + out int consumed)) { - if (reader.TryCopyOctetStringBytes(tmp, out int bytesWritten)) + if (consumed != input.Length) { - data = tmp.Slice(0, bytesWritten); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - else - { - poolBytes = CryptoPool.Rent(reader.PeekContentBytes().Length); - - if (!reader.TryCopyOctetStringBytes(poolBytes, out bytesWritten)) - { - Debug.Fail("TryCopyOctetStringBytes failed with a provably-large-enough buffer"); - throw new CryptographicException(); - } - data = new ReadOnlySpan(poolBytes, 0, bytesWritten); + if (input.Overlaps(primitive, out int offset)) + { + return encodedOctetString.Slice(offset, primitive.Length); } + + Debug.Fail("input.Overlaps(primitive) failed after TryReadPrimitiveOctetString succeeded"); } - else + + byte[] ret = AsnDecoder.ReadOctetString(input, AsnEncodingRules.BER, out consumed); + + if (consumed != input.Length) { - data = contents.Span; + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - reader.ThrowIfNotEmpty(); - - return data.ToArray(); + return ret; } - finally + catch (AsnContentException e) { - if (poolBytes != null) + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + public static byte[] DecodeOctetString(ReadOnlyMemory encodedOctets) + { + try + { + // Read using BER because the CMS specification says the encoding is BER. + byte[] ret = AsnDecoder.ReadOctetString(encodedOctets.Span, AsnEncodingRules.BER, out int consumed); + + if (consumed != encodedOctets.Length) { - CryptoPool.Return(poolBytes, data.Length); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } + + return ret; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } } public static byte[] EncodeOctetString(byte[] octets) { // Write using DER to support the most readers. - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteOctetString(octets); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteOctetString(octets); + return writer.Encode(); } public static byte[] EncodeUtcTime(DateTime utcTime) { const int maxLegalYear = 2049; // Write using DER to support the most readers. - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + try { - try + // Sending the DateTime through ToLocalTime here will cause the right normalization + // of DateTimeKind.Unknown. + // + // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows) + if (utcTime.Kind == DateTimeKind.Unspecified) { - // Sending the DateTime through ToLocalTime here will cause the right normalization - // of DateTimeKind.Unknown. - // - // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows) - if (utcTime.Kind == DateTimeKind.Unspecified) - { - writer.WriteUtcTime(utcTime.ToLocalTime(), maxLegalYear); - } - else - { - writer.WriteUtcTime(utcTime, maxLegalYear); - } - - return writer.Encode(); + writer.WriteUtcTime(utcTime.ToLocalTime(), maxLegalYear); } - catch (ArgumentException ex) + else { - throw new CryptographicException(ex.Message, ex); + writer.WriteUtcTime(utcTime, maxLegalYear); } + + return writer.Encode(); + } + catch (ArgumentException ex) + { + throw new CryptographicException(ex.Message, ex); } } public static DateTime DecodeUtcTime(byte[] encodedUtcTime) { // Read using BER because the CMS specification says the encoding is BER. - AsnReader reader = new AsnReader(encodedUtcTime, AsnEncodingRules.BER); - DateTimeOffset value = reader.ReadUtcTime(); - reader.ThrowIfNotEmpty(); - return value.UtcDateTime; + try + { + DateTimeOffset value = AsnDecoder.ReadUtcTime( + encodedUtcTime, + AsnEncodingRules.BER, + out int consumed); + + if (consumed != encodedUtcTime.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return value.UtcDateTime; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } public static string DecodeOid(ReadOnlySpan encodedOid) @@ -578,10 +599,24 @@ public static string DecodeOid(ReadOnlySpan encodedOid) } // Read using BER because the CMS specification says the encoding is BER. - AsnValueReader reader = new AsnValueReader(encodedOid, AsnEncodingRules.BER); - string value = reader.ReadObjectIdentifierAsString(); - reader.ThrowIfNotEmpty(); - return value; + try + { + string value = AsnDecoder.ReadObjectIdentifier( + encodedOid, + AsnEncodingRules.BER, + out int consumed); + + if (consumed != encodedOid.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return value; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } public static bool TryGetRsaOaepEncryptionPadding( @@ -602,9 +637,9 @@ public static bool TryGetRsaOaepEncryptionPadding( { OaepParamsAsn oaepParameters = OaepParamsAsn.Decode(parameters.Value, AsnEncodingRules.DER); - if (oaepParameters.MaskGenFunc.Algorithm.Value != Oids.Mgf1 || + if (oaepParameters.MaskGenFunc.Algorithm != Oids.Mgf1 || oaepParameters.MaskGenFunc.Parameters == null || - oaepParameters.PSourceFunc.Algorithm.Value != Oids.PSpecified + oaepParameters.PSourceFunc.Algorithm != Oids.PSpecified ) { exception = new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); @@ -613,7 +648,7 @@ public static bool TryGetRsaOaepEncryptionPadding( AlgorithmIdentifierAsn mgf1AlgorithmIdentifier = AlgorithmIdentifierAsn.Decode(oaepParameters.MaskGenFunc.Parameters.Value, AsnEncodingRules.DER); - if (mgf1AlgorithmIdentifier.Algorithm.Value != oaepParameters.HashFunc.Algorithm.Value) + if (mgf1AlgorithmIdentifier.Algorithm != oaepParameters.HashFunc.Algorithm) { exception = new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); return false; @@ -628,7 +663,7 @@ public static bool TryGetRsaOaepEncryptionPadding( return false; } - switch (oaepParameters.HashFunc.Algorithm.Value) + switch (oaepParameters.HashFunc.Algorithm) { case Oids.Sha1: rsaEncryptionPadding = RSAEncryptionPadding.OaepSHA1; @@ -645,7 +680,7 @@ public static bool TryGetRsaOaepEncryptionPadding( default: exception = new CryptographicException( SR.Cryptography_Cms_UnknownAlgorithm, - oaepParameters.HashFunc.Algorithm.Value); + oaepParameters.HashFunc.Algorithm); return false; } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj index d6fc49b30ecc8d..dea5805bbeedf0 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj @@ -344,6 +344,7 @@ + @@ -661,4 +662,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CadesIssuerSerial.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CadesIssuerSerial.xml.cs index fc9cf830af78d9..4a889d69cc9f9b 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CadesIssuerSerial.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CadesIssuerSerial.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -45,11 +44,18 @@ internal static CadesIssuerSerial Decode(ReadOnlyMemory encoded, AsnEncodi internal static CadesIssuerSerial Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CadesIssuerSerial decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out CadesIssuerSerial decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CadesIssuerSerial decoded) @@ -58,6 +64,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CadesIssuerSerial decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CadesIssuerSerial decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.xml.cs index 073aac1a878b56..99b1889f26541a 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/CertificateChoiceAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -51,7 +50,14 @@ internal void Encode(AsnWriter writer) } } - writer.WriteEncodedValue(Certificate.Value.Span); + try + { + writer.WriteEncodedValue(Certificate.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } wroteValue = true; } @@ -63,14 +69,33 @@ internal void Encode(AsnWriter writer) internal static CertificateChoiceAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out CertificateChoiceAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out CertificateChoiceAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificateChoiceAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificateChoiceAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.xml.cs index e80e5dd49ea083..c4bc5e6d95fa55 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EncapsulatedContentInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -25,12 +24,26 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(ContentType); + try + { + writer.WriteObjectIdentifier(ContentType); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } if (Content.HasValue) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(Content.Value.Span); + try + { + writer.WriteEncodedValue(Content.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); } @@ -44,11 +57,18 @@ internal static EncapsulatedContentInfoAsn Decode(ReadOnlyMemory encoded, internal static EncapsulatedContentInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EncapsulatedContentInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out EncapsulatedContentInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EncapsulatedContentInfoAsn decoded) @@ -57,6 +77,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncapsulatedContentInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EncapsulatedContentInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -65,7 +97,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.ContentType = sequenceReader.ReadObjectIdentifierAsString(); + decoded.ContentType = sequenceReader.ReadObjectIdentifier(); if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EnvelopedDataAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EnvelopedDataAsn.xml.cs index dfcaffa9ee05ad..acdbc65b5ef993 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EnvelopedDataAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EnvelopedDataAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -68,11 +67,18 @@ internal static EnvelopedDataAsn Decode(ReadOnlyMemory encoded, AsnEncodin internal static EnvelopedDataAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EnvelopedDataAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out EnvelopedDataAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EnvelopedDataAsn decoded) @@ -81,6 +87,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EnvelopedDataAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EnvelopedDataAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertId.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertId.xml.cs index d5e2095500c899..538ff8b24a5585 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertId.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertId.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -42,11 +41,18 @@ internal static EssCertId Decode(ReadOnlyMemory encoded, AsnEncodingRules internal static EssCertId Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EssCertId decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out EssCertId decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EssCertId decoded) @@ -55,6 +61,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EssCertId decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EssCertId decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -63,7 +81,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Hash = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs index df54bc64b5efe7..d94f24c563c879 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -44,15 +43,12 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for HashAlgorithm. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - HashAlgorithm.Encode(tmp); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + HashAlgorithm.Encode(tmp); - if (!encoded.SequenceEqual(DefaultHashAlgorithm)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultHashAlgorithm)) + { + tmp.CopyTo(writer); } } @@ -73,11 +69,18 @@ internal static EssCertIdV2 Decode(ReadOnlyMemory encoded, AsnEncodingRule internal static EssCertIdV2 Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out EssCertIdV2 decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out EssCertIdV2 decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out EssCertIdV2 decoded) @@ -86,6 +89,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EssCertIdV2 decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out EssCertIdV2 decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -106,7 +121,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read } - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Hash = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.xml.cs index 3077daa0d73823..446d9e016e4ce4 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/IssuerAndSerialNumberAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -34,7 +33,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) } } - writer.WriteEncodedValue(Issuer.Span); + try + { + writer.WriteEncodedValue(Issuer.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.WriteInteger(SerialNumber.Span); writer.PopSequence(tag); } @@ -46,11 +52,18 @@ internal static IssuerAndSerialNumberAsn Decode(ReadOnlyMemory encoded, As internal static IssuerAndSerialNumberAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out IssuerAndSerialNumberAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out IssuerAndSerialNumberAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out IssuerAndSerialNumberAsn decoded) @@ -59,6 +72,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out IssuerAndSerialNumberAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out IssuerAndSerialNumberAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientIdentifierAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientIdentifierAsn.xml.cs index cc5df62f2dc4ad..4fca26a3235541 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientIdentifierAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientIdentifierAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -65,14 +64,33 @@ internal void Encode(AsnWriter writer) internal static KeyAgreeRecipientIdentifierAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out KeyAgreeRecipientIdentifierAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out KeyAgreeRecipientIdentifierAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out KeyAgreeRecipientIdentifierAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out KeyAgreeRecipientIdentifierAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientInfoAsn.xml.cs index 012c59ddbc1b16..11691c9919568d 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyAgreeRecipientInfoAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -60,11 +59,18 @@ internal static KeyAgreeRecipientInfoAsn Decode(ReadOnlyMemory encoded, As internal static KeyAgreeRecipientInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out KeyAgreeRecipientInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out KeyAgreeRecipientInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out KeyAgreeRecipientInfoAsn decoded) @@ -73,6 +79,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out KeyAgreeRecipientInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out KeyAgreeRecipientInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -98,7 +116,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read { explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - if (explicitReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (explicitReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.Ukm = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyTransRecipientInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyTransRecipientInfoAsn.xml.cs index 94bffbfd283d7c..b6ced3dab490db 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyTransRecipientInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/KeyTransRecipientInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -41,11 +40,18 @@ internal static KeyTransRecipientInfoAsn Decode(ReadOnlyMemory encoded, As internal static KeyTransRecipientInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out KeyTransRecipientInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out KeyTransRecipientInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out KeyTransRecipientInfoAsn decoded) @@ -54,6 +60,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out KeyTransRecipientInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out KeyTransRecipientInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -70,7 +88,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Pkcs.Asn1.RecipientIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.Rid); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.KeyEncryptionAlgorithm); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.EncryptedKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml.cs index 1907c3b1cf309a..d31418b126c8bd 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -37,11 +36,18 @@ internal static MessageImprint Decode(ReadOnlyMemory encoded, AsnEncodingR internal static MessageImprint Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out MessageImprint decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out MessageImprint decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out MessageImprint decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out MessageImprint decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out MessageImprint decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -59,7 +77,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.HashAlgorithm); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.HashedMessage = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorIdentifierOrKeyAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorIdentifierOrKeyAsn.xml.cs index b9a72cc6aa9b96..3d42533e043214 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorIdentifierOrKeyAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorIdentifierOrKeyAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -55,7 +54,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), SubjectKeyIdentifier.Value.Span); + writer.WriteOctetString(SubjectKeyIdentifier.Value.Span, new Asn1Tag(TagClass.ContextSpecific, 0)); wroteValue = true; } @@ -76,14 +75,33 @@ internal void Encode(AsnWriter writer) internal static OriginatorIdentifierOrKeyAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out OriginatorIdentifierOrKeyAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out OriginatorIdentifierOrKeyAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out OriginatorIdentifierOrKeyAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out OriginatorIdentifierOrKeyAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -101,7 +119,7 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { - if (reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out tmpSpan)) + if (reader.TryReadPrimitiveOctetString(out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 0))) { decoded.SubjectKeyIdentifier = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorInfoAsn.xml.cs index 009aef45448e2d..643246b4f4f619 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorInfoAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -46,7 +45,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, 1)); for (int i = 0; i < RevocationInfoChoices.Length; i++) { - writer.WriteEncodedValue(RevocationInfoChoices[i].Span); + try + { + writer.WriteEncodedValue(RevocationInfoChoices[i].Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, 1)); @@ -62,11 +68,18 @@ internal static OriginatorInfoAsn Decode(ReadOnlyMemory encoded, AsnEncodi internal static OriginatorInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out OriginatorInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out OriginatorInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out OriginatorInfoAsn decoded) @@ -75,6 +88,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OriginatorInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OriginatorInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorPublicKeyAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorPublicKeyAsn.xml.cs index 0448009c0e54ca..6311bf2cbd9ef0 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorPublicKeyAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OriginatorPublicKeyAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -26,7 +25,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) writer.PushSequence(tag); Algorithm.Encode(writer); - writer.WriteBitString(PublicKey.Span); + writer.WriteBitString(PublicKey.Span, 0); writer.PopSequence(tag); } @@ -37,11 +36,18 @@ internal static OriginatorPublicKeyAsn Decode(ReadOnlyMemory encoded, AsnE internal static OriginatorPublicKeyAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out OriginatorPublicKeyAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out OriginatorPublicKeyAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out OriginatorPublicKeyAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OriginatorPublicKeyAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OriginatorPublicKeyAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -59,7 +77,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.Algorithm); - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan)) { decoded.PublicKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OtherKeyAttributeAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OtherKeyAttributeAsn.xml.cs index b33506cf05c38a..c02ffbb0eec467 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OtherKeyAttributeAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/OtherKeyAttributeAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -25,11 +24,25 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(KeyAttrId); + try + { + writer.WriteObjectIdentifier(KeyAttrId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } if (KeyAttr.HasValue) { - writer.WriteEncodedValue(KeyAttr.Value.Span); + try + { + writer.WriteEncodedValue(KeyAttr.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSequence(tag); @@ -42,11 +55,18 @@ internal static OtherKeyAttributeAsn Decode(ReadOnlyMemory encoded, AsnEnc internal static OtherKeyAttributeAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out OtherKeyAttributeAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out OtherKeyAttributeAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out OtherKeyAttributeAsn decoded) @@ -55,6 +75,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OtherKeyAttributeAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out OtherKeyAttributeAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -62,7 +94,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.KeyAttrId = sequenceReader.ReadObjectIdentifierAsString(); + decoded.KeyAttrId = sequenceReader.ReadObjectIdentifier(); if (sequenceReader.HasData) { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PkiStatusInfo.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PkiStatusInfo.xml.cs index ff2b70ebe4cf93..ccddd877c64132 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PkiStatusInfo.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PkiStatusInfo.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -39,7 +38,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) } } - writer.WriteEncodedValue(StatusString.Value.Span); + try + { + writer.WriteEncodedValue(StatusString.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } @@ -58,11 +64,18 @@ internal static PkiStatusInfo Decode(ReadOnlyMemory encoded, AsnEncodingRu internal static PkiStatusInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PkiStatusInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out PkiStatusInfo decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PkiStatusInfo decoded) @@ -71,6 +84,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PkiStatusInfo decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PkiStatusInfo decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyInformation.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyInformation.xml.cs index 2836c2ad36a788..a48ea2762f89cd 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyInformation.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyInformation.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -26,7 +25,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(PolicyIdentifier); + try + { + writer.WriteObjectIdentifier(PolicyIdentifier); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } if (PolicyQualifiers != null) { @@ -50,11 +56,18 @@ internal static PolicyInformation Decode(ReadOnlyMemory encoded, AsnEncodi internal static PolicyInformation Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PolicyInformation decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out PolicyInformation decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PolicyInformation decoded) @@ -63,12 +76,24 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyInformation decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyInformation decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); AsnValueReader collectionReader; - decoded.PolicyIdentifier = sequenceReader.ReadObjectIdentifierAsString(); + decoded.PolicyIdentifier = sequenceReader.ReadObjectIdentifier(); if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Sequence)) { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyQualifierInfo.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyQualifierInfo.xml.cs index a1cfc84c344199..4a2dfa05fc6563 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyQualifierInfo.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/PolicyQualifierInfo.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -25,8 +24,22 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(PolicyQualifierId); - writer.WriteEncodedValue(Qualifier.Span); + try + { + writer.WriteObjectIdentifier(PolicyQualifierId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + try + { + writer.WriteEncodedValue(Qualifier.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(tag); } @@ -37,11 +50,18 @@ internal static PolicyQualifierInfo Decode(ReadOnlyMemory encoded, AsnEnco internal static PolicyQualifierInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PolicyQualifierInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out PolicyQualifierInfo decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PolicyQualifierInfo decoded) @@ -50,6 +70,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyQualifierInfo decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyQualifierInfo decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -57,7 +89,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.PolicyQualifierId = sequenceReader.ReadObjectIdentifierAsString(); + decoded.PolicyQualifierId = sequenceReader.ReadObjectIdentifier(); tmpSpan = sequenceReader.ReadEncodedValue(); decoded.Qualifier = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientEncryptedKeyAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientEncryptedKeyAsn.xml.cs index a68a5685ef7c77..c7e229d8746c12 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientEncryptedKeyAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientEncryptedKeyAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -37,11 +36,18 @@ internal static RecipientEncryptedKeyAsn Decode(ReadOnlyMemory encoded, As internal static RecipientEncryptedKeyAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out RecipientEncryptedKeyAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out RecipientEncryptedKeyAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out RecipientEncryptedKeyAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RecipientEncryptedKeyAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RecipientEncryptedKeyAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -59,7 +77,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Pkcs.Asn1.KeyAgreeRecipientIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.Rid); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.EncryptedKey = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientIdentifierAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientIdentifierAsn.xml.cs index 252367118567ac..21c2d3173504cd 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientIdentifierAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientIdentifierAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -53,7 +52,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), SubjectKeyIdentifier.Value.Span); + writer.WriteOctetString(SubjectKeyIdentifier.Value.Span, new Asn1Tag(TagClass.ContextSpecific, 0)); wroteValue = true; } @@ -65,14 +64,33 @@ internal void Encode(AsnWriter writer) internal static RecipientIdentifierAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out RecipientIdentifierAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out RecipientIdentifierAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out RecipientIdentifierAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out RecipientIdentifierAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -90,7 +108,7 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { - if (reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out tmpSpan)) + if (reader.TryReadPrimitiveOctetString(out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 0))) { decoded.SubjectKeyIdentifier = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientInfoAsn.xml.cs index 4290612912cbbc..0f8b918db8138b 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientInfoAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -65,14 +64,33 @@ internal void Encode(AsnWriter writer) internal static RecipientInfoAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out RecipientInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out RecipientInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out RecipientInfoAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out RecipientInfoAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientKeyIdentifier.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientKeyIdentifier.xml.cs index b32bd308c72b74..27ed1e26b54cc9 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientKeyIdentifier.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/RecipientKeyIdentifier.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -30,7 +29,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (Date.HasValue) { - writer.WriteGeneralizedTime(Date.Value); + writer.WriteGeneralizedTime(Date.Value, false); } @@ -49,11 +48,18 @@ internal static RecipientKeyIdentifier Decode(ReadOnlyMemory encoded, AsnE internal static RecipientKeyIdentifier Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out RecipientKeyIdentifier decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out RecipientKeyIdentifier decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out RecipientKeyIdentifier decoded) @@ -62,6 +68,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RecipientKeyIdentifier decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out RecipientKeyIdentifier decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -70,7 +88,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read ReadOnlySpan tmpSpan; - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.SubjectKeyIdentifier = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161Accuracy.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161Accuracy.xml.cs index 10924f97bb1060..03b7e5cf71c346 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161Accuracy.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161Accuracy.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -35,13 +34,13 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (Millis.HasValue) { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0), Millis.Value); + writer.WriteInteger(Millis.Value, new Asn1Tag(TagClass.ContextSpecific, 0)); } if (Micros.HasValue) { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 1), Micros.Value); + writer.WriteInteger(Micros.Value, new Asn1Tag(TagClass.ContextSpecific, 1)); } writer.PopSequence(tag); @@ -54,11 +53,18 @@ internal static Rfc3161Accuracy Decode(ReadOnlyMemory encoded, AsnEncoding internal static Rfc3161Accuracy Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out Rfc3161Accuracy decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out Rfc3161Accuracy decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Rfc3161Accuracy decoded) @@ -67,6 +73,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161Accuracy decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161Accuracy decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -90,7 +108,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { - if (sequenceReader.TryReadInt32(new Asn1Tag(TagClass.ContextSpecific, 0), out int tmpMillis)) + if (sequenceReader.TryReadInt32(out int tmpMillis, new Asn1Tag(TagClass.ContextSpecific, 0))) { decoded.Millis = tmpMillis; } @@ -105,7 +123,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { - if (sequenceReader.TryReadInt32(new Asn1Tag(TagClass.ContextSpecific, 1), out int tmpMicros)) + if (sequenceReader.TryReadInt32(out int tmpMicros, new Asn1Tag(TagClass.ContextSpecific, 1))) { decoded.Micros = tmpMicros; } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs index 704fcc6995eb16..0097e7e9bdb0e2 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -18,7 +17,7 @@ internal partial struct Rfc3161TimeStampReq internal int Version; internal System.Security.Cryptography.Pkcs.Asn1.MessageImprint MessageImprint; - internal Oid? ReqPolicy; + internal string? ReqPolicy; internal ReadOnlyMemory? Nonce; internal bool CertReq; internal System.Security.Cryptography.Asn1.X509ExtensionAsn[]? Extensions; @@ -49,7 +48,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (ReqPolicy != null) { - writer.WriteObjectIdentifier(ReqPolicy); + try + { + writer.WriteObjectIdentifier(ReqPolicy); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } @@ -61,15 +67,12 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for CertReq. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - tmp.WriteBoolean(CertReq); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteBoolean(CertReq); - if (!encoded.SequenceEqual(DefaultCertReq)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultCertReq)) + { + tmp.CopyTo(writer); } } @@ -96,11 +99,18 @@ internal static Rfc3161TimeStampReq Decode(ReadOnlyMemory encoded, AsnEnco internal static Rfc3161TimeStampReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out Rfc3161TimeStampReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out Rfc3161TimeStampReq decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Rfc3161TimeStampReq decoded) @@ -109,6 +119,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161TimeStampReq decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161TimeStampReq decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampResp.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampResp.xml.cs index e7b85affbccdf2..d8239c76c94e00 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampResp.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TimeStampResp.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -29,7 +28,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (TimeStampToken.HasValue) { - writer.WriteEncodedValue(TimeStampToken.Value.Span); + try + { + writer.WriteEncodedValue(TimeStampToken.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSequence(tag); @@ -42,11 +48,18 @@ internal static Rfc3161TimeStampResp Decode(ReadOnlyMemory encoded, AsnEnc internal static Rfc3161TimeStampResp Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out Rfc3161TimeStampResp decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out Rfc3161TimeStampResp decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Rfc3161TimeStampResp decoded) @@ -55,6 +68,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161TimeStampResp decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161TimeStampResp decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs index 49a96ecbd8f890..73609bf83d3628 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -17,7 +16,7 @@ internal partial struct Rfc3161TstInfo private static ReadOnlySpan DefaultOrdering => new byte[] { 0x01, 0x01, 0x00 }; internal int Version; - internal Oid Policy; + internal string Policy; internal System.Security.Cryptography.Pkcs.Asn1.MessageImprint MessageImprint; internal ReadOnlyMemory SerialNumber; internal DateTimeOffset GenTime; @@ -49,10 +48,17 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) writer.PushSequence(tag); writer.WriteInteger(Version); - writer.WriteObjectIdentifier(Policy); + try + { + writer.WriteObjectIdentifier(Policy); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } MessageImprint.Encode(writer); writer.WriteInteger(SerialNumber.Span); - writer.WriteGeneralizedTime(GenTime); + writer.WriteGeneralizedTime(GenTime, false); if (Accuracy.HasValue) { @@ -62,15 +68,12 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for Ordering. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - tmp.WriteBoolean(Ordering); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteBoolean(Ordering); - if (!encoded.SequenceEqual(DefaultOrdering)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultOrdering)) + { + tmp.CopyTo(writer); } } @@ -111,11 +114,18 @@ internal static Rfc3161TstInfo Decode(ReadOnlyMemory encoded, AsnEncodingR internal static Rfc3161TstInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out Rfc3161TstInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out Rfc3161TstInfo decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out Rfc3161TstInfo decoded) @@ -124,6 +134,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161TstInfo decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out Rfc3161TstInfo decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SecretBagAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SecretBagAsn.xml.cs index ca4374fbb9aec2..db84eb1a6af5be 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SecretBagAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SecretBagAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -25,9 +24,23 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(SecretTypeId); + try + { + writer.WriteObjectIdentifier(SecretTypeId); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(SecretValue.Span); + try + { + writer.WriteEncodedValue(SecretValue.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); writer.PopSequence(tag); } @@ -39,11 +52,18 @@ internal static SecretBagAsn Decode(ReadOnlyMemory encoded, AsnEncodingRul internal static SecretBagAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SecretBagAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out SecretBagAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SecretBagAsn decoded) @@ -52,6 +72,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SecretBagAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SecretBagAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -60,7 +92,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.SecretTypeId = sequenceReader.ReadObjectIdentifierAsString(); + decoded.SecretTypeId = sequenceReader.ReadObjectIdentifier(); explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmpSpan = explicitReader.ReadEncodedValue(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedAttributesSet.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedAttributesSet.xml.cs index 7c81d8cc699f58..7ba35d2c838be5 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedAttributesSet.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedAttributesSet.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs { @@ -62,14 +61,33 @@ internal void Encode(AsnWriter writer) internal static SignedAttributesSet Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out SignedAttributesSet decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out SignedAttributesSet decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SignedAttributesSet decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out SignedAttributesSet decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.xml.cs index 1af67b092ff283..32b82b32468f7d 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignedDataAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -60,7 +59,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, 1)); for (int i = 0; i < Crls.Length; i++) { - writer.WriteEncodedValue(Crls[i].Span); + try + { + writer.WriteEncodedValue(Crls[i].Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, 1)); @@ -84,11 +90,18 @@ internal static SignedDataAsn Decode(ReadOnlyMemory encoded, AsnEncodingRu internal static SignedDataAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SignedDataAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out SignedDataAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SignedDataAsn decoded) @@ -97,6 +110,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SignedDataAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SignedDataAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.xml.cs index 5cb956e962acbe..23dd4e8520a3d0 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerIdentifierAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -53,7 +52,7 @@ internal void Encode(AsnWriter writer) if (wroteValue) throw new CryptographicException(); - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), SubjectKeyIdentifier.Value.Span); + writer.WriteOctetString(SubjectKeyIdentifier.Value.Span, new Asn1Tag(TagClass.ContextSpecific, 0)); wroteValue = true; } @@ -65,14 +64,33 @@ internal void Encode(AsnWriter writer) internal static SignerIdentifierAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out SignerIdentifierAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, encoded, out SignerIdentifierAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SignerIdentifierAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out SignerIdentifierAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -90,7 +108,7 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { - if (reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out tmpSpan)) + if (reader.TryReadPrimitiveOctetString(out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 0))) { decoded.SubjectKeyIdentifier = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.xml.cs index dc2267293d81cf..8b209b2a6aff7c 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SignerInfoAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -46,7 +45,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) } } - writer.WriteEncodedValue(SignedAttributes.Value.Span); + try + { + writer.WriteEncodedValue(SignedAttributes.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } SignatureAlgorithm.Encode(writer); @@ -74,11 +80,18 @@ internal static SignerInfoAsn Decode(ReadOnlyMemory encoded, AsnEncodingRu internal static SignerInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SignerInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out SignerInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SignerInfoAsn decoded) @@ -87,6 +100,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SignerInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SignerInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -112,7 +137,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.SignatureAlgorithm); - if (sequenceReader.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (sequenceReader.TryReadPrimitiveOctetString(out tmpSpan)) { decoded.SignatureValue = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateAsn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateAsn.xml.cs index 52a06d97d62b67..54a4bcd2b00dd7 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -57,11 +56,18 @@ internal static SigningCertificateAsn Decode(ReadOnlyMemory encoded, AsnEn internal static SigningCertificateAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SigningCertificateAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out SigningCertificateAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SigningCertificateAsn decoded) @@ -70,6 +76,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SigningCertificateAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SigningCertificateAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateV2Asn.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateV2Asn.xml.cs index 6556faf23f8f26..7201b40a878816 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateV2Asn.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/SigningCertificateV2Asn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.Pkcs.Asn1 { @@ -57,11 +56,18 @@ internal static SigningCertificateV2Asn Decode(ReadOnlyMemory encoded, Asn internal static SigningCertificateV2Asn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out SigningCertificateV2Asn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + Decode(ref reader, expectedTag, encoded, out SigningCertificateV2Asn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out SigningCertificateV2Asn decoded) @@ -70,6 +76,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SigningCertificateV2Asn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out SigningCertificateV2Asn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs index 247ed261f20605..03e37f97e53ae1 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.Pkcs.Asn1; using System.Security.Cryptography.X509Certificates; using Internal.Cryptography; @@ -215,12 +215,12 @@ protected override RSASignaturePadding GetSignaturePadding( PssParamsAsn pssParams = PssParamsAsn.Decode(signatureParameters.Value, AsnEncodingRules.DER); - if (pssParams.HashAlgorithm.Algorithm.Value != digestAlgorithmOid) + if (pssParams.HashAlgorithm.Algorithm != digestAlgorithmOid) { throw new CryptographicException( SR.Format( SR.Cryptography_Pkcs_PssParametersHashMismatch, - pssParams.HashAlgorithm.Algorithm.Value, + pssParams.HashAlgorithm.Algorithm, digestAlgorithmOid)); } @@ -238,11 +238,11 @@ protected override RSASignaturePadding GetSignaturePadding( digestAlgorithmName.Name)); } - if (pssParams.MaskGenAlgorithm.Algorithm.Value != Oids.Mgf1) + if (pssParams.MaskGenAlgorithm.Algorithm != Oids.Mgf1) { throw new CryptographicException( SR.Cryptography_Pkcs_PssParametersMgfNotSupported, - pssParams.MaskGenAlgorithm.Algorithm.Value); + pssParams.MaskGenAlgorithm.Algorithm); } if (pssParams.MaskGenAlgorithm.Parameters == null) @@ -254,12 +254,12 @@ protected override RSASignaturePadding GetSignaturePadding( pssParams.MaskGenAlgorithm.Parameters.Value, AsnEncodingRules.DER); - if (mgfParams.Algorithm.Value != digestAlgorithmOid) + if (mgfParams.Algorithm != digestAlgorithmOid) { throw new CryptographicException( SR.Format( SR.Cryptography_Pkcs_PssParametersMgfHashMismatch, - mgfParams.Algorithm.Value, + mgfParams.Algorithm, digestAlgorithmOid)); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs index 2ae38f799cc7c9..f32495dcc70bb4 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Numerics; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; namespace System.Security.Cryptography.Pkcs @@ -149,6 +149,10 @@ private static bool DsaDerToIeee( return !sequence.HasData; } + catch (AsnContentException) + { + return false; + } catch (CryptographicException) { return false; @@ -163,7 +167,7 @@ private static byte[] DsaIeeeToDer(ReadOnlySpan ieeeSignature) fieldSize * 2 == ieeeSignature.Length, $"ieeeSignature.Length ({ieeeSignature.Length}) must be even"); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); { writer.PushSequence(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs index 0070cc47921e0c..c0bf05e325cd80 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs.Asn1; using System.Security.Cryptography.X509Certificates; @@ -128,7 +129,7 @@ internal SignerInfoAsn Sign( byte[] dataHash = hasher.GetHashAndReset(); SignerInfoAsn newSignerInfo = default; - newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm; + newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm.Value!; // If the user specified attributes (not null, count > 0) we need attributes. // If the content type is null we're counter-signing, and need the message digest attr. @@ -137,31 +138,29 @@ internal SignerInfoAsn Sign( { List signedAttrs = BuildAttributes(SignedAttributes); - using (var writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteOctetString(dataHash); + + signedAttrs.Add( + new AttributeAsn + { + AttrType = Oids.MessageDigest, + AttrValues = new[] { new ReadOnlyMemory(writer.Encode()) }, + }); + + if (contentTypeOid != null) { - writer.WriteOctetString(dataHash); + writer.Reset(); + writer.WriteObjectIdentifierForCrypto(contentTypeOid); + signedAttrs.Add( new AttributeAsn { - AttrType = new Oid(Oids.MessageDigest, Oids.MessageDigest), + AttrType = Oids.ContentType, AttrValues = new[] { new ReadOnlyMemory(writer.Encode()) }, }); } - if (contentTypeOid != null) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteObjectIdentifier(contentTypeOid); - signedAttrs.Add( - new AttributeAsn - { - AttrType = new Oid(Oids.ContentType, Oids.ContentType), - AttrValues = new[] { new ReadOnlyMemory(writer.Encode()) }, - }); - } - } - // Use the serializer/deserializer to DER-normalize the attribute order. SignedAttributesSet signedAttrsSet = default; signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeAttributeSet( @@ -171,11 +170,9 @@ internal SignerInfoAsn Sign( // Since this contains user data in a context where BER is permitted, use BER. // There shouldn't be any observable difference here between BER and DER, though, // since the top level fields were written by NormalizeSet. - using (AsnWriter attrsWriter = new AsnWriter(AsnEncodingRules.BER)) - { - signedAttrsSet.Encode(attrsWriter); - newSignerInfo.SignedAttributes = attrsWriter.Encode(); - } + AsnWriter attrsWriter = new AsnWriter(AsnEncodingRules.BER); + signedAttrsSet.Encode(attrsWriter); + newSignerInfo.SignedAttributes = attrsWriter.Encode(); dataHash = hasher.GetHashAndReset(); } @@ -246,7 +243,7 @@ internal SignerInfoAsn Sign( } newSignerInfo.SignatureValue = signatureValue; - newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm!; + newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm!.Value!; X509Certificate2Collection certs = new X509Certificate2Collection(); certs.AddRange(Certificates); @@ -317,7 +314,7 @@ internal static List BuildAttributes(CryptographicAttributeObjectC { AttributeAsn newAttr = new AttributeAsn { - AttrType = attributeObject.Oid, + AttrType = attributeObject.Oid!.Value!, AttrValues = new ReadOnlyMemory[attributeObject.Values.Count], }; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Builder.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Builder.cs index 4290deb59336b6..3549154a917f7b 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Builder.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Builder.cs @@ -5,9 +5,8 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1.Pkcs7; -using System.Security.Cryptography.Pkcs.Asn1; using Internal.Cryptography; namespace System.Security.Cryptography.Pkcs @@ -170,7 +169,8 @@ public void SealWithMac( try { - using (AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER)) + AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER); + using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm)) { contentsWriter.PushSequence(); @@ -183,11 +183,15 @@ public void SealWithMac( } contentsWriter.PopSequence(); - ReadOnlySpan encodedSpan = contentsWriter.EncodeAsSpan(); + rentedAuthSafe = CryptoPool.Rent(contentsWriter.GetEncodedLength()); + + if (!contentsWriter.TryEncode(rentedAuthSafe, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } - rentedAuthSafe = CryptoPool.Rent(encodedSpan.Length); - encodedSpan.CopyTo(rentedAuthSafe); - authSafeSpan = rentedAuthSafe.AsSpan(0, encodedSpan.Length); + authSafeSpan = rentedAuthSafe.AsSpan(0, written); // Get an array of the proper size for the hash. byte[] macKey = hasher.GetHashAndReset(); @@ -209,7 +213,7 @@ public void SealWithMac( using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey)) { - mac.AppendData(encodedSpan); + mac.AppendData(authSafeSpan); if (!mac.TryGetHashAndReset(macSpan, out int bytesWritten) || bytesWritten != macSpan.Length) { @@ -226,7 +230,7 @@ public void SealWithMac( // authSafe ContentInfo, // macData MacData OPTIONAL // } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); { writer.PushSequence(); @@ -234,7 +238,7 @@ public void SealWithMac( writer.PushSequence(); { - writer.WriteObjectIdentifier(Oids.Pkcs7Data); + writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data); Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0); @@ -262,7 +266,7 @@ public void SealWithMac( { writer.PushSequence(); { - writer.WriteObjectIdentifier(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm)); + writer.WriteObjectIdentifierForCrypto(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm)); writer.PopSequence(); } @@ -308,8 +312,8 @@ public void SealWithoutIntegrity() if (IsSealed) throw new InvalidOperationException(SR.Cryptography_Pkcs12_PfxIsSealed); - using (AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER)) - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); { contentsWriter.PushSequence(); if (_contents != null) @@ -332,19 +336,15 @@ public void SealWithoutIntegrity() writer.WriteInteger(3); - writer.PushSequence(); + using (writer.PushSequence()) { - writer.WriteObjectIdentifier(Oids.Pkcs7Data); - - Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0); + writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data); - writer.PushSequence(contextSpecific0); + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0))) + using (writer.PushOctetString()) { - writer.WriteOctetString(contentsWriter.EncodeAsSpan()); - writer.PopSequence(contextSpecific0); + contentsWriter.CopyTo(writer); } - - writer.PopSequence(); } writer.PopSequence(); diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs index 7496e00ceac73a..144c3378587078 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1.Pkcs12; using System.Security.Cryptography.X509Certificates; using Internal.Cryptography; @@ -96,9 +96,17 @@ private static byte[] EncodeBagValue(Oid certificateType, ReadOnlyMemory e private static byte[] EncodeBagValue(string certificateType, ReadOnlyMemory encodedCertificate) { // Read to ensure that there is precisely one legally encoded value. - AsnReader reader = new AsnReader(encodedCertificate, AsnEncodingRules.BER); - reader.ReadEncodedValue(); - reader.ThrowIfNotEmpty(); + if (!AsnDecoder.TryReadEncodedValue( + encodedCertificate.Span, + AsnEncodingRules.BER, + out _, + out _, + out _, + out int consumed) || + consumed != encodedCertificate.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } // No need to copy encodedCertificate here, because it will be copied into the // return value. @@ -108,11 +116,9 @@ private static byte[] EncodeBagValue(string certificateType, ReadOnlyMemory bagValue) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Info.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Info.cs index d10123453bfd44..57bcc859d71a40 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Info.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Info.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1.Pkcs12; using System.Security.Cryptography.Asn1.Pkcs7; using System.Security.Cryptography.Pkcs.Asn1; @@ -49,11 +49,11 @@ public static Pkcs12Info Decode( out int bytesConsumed, bool skipCopy = false) { - AsnReader reader = new AsnReader(encodedBytes, AsnEncodingRules.BER); // Trim it to the first value - encodedBytes = reader.PeekEncodedValue(); + int firstValueLength = PkcsHelpers.FirstBerValueLength(encodedBytes.Span); + ReadOnlyMemory firstValue = encodedBytes.Slice(0, firstValueLength); - ReadOnlyMemory maybeCopy = skipCopy ? encodedBytes : encodedBytes.ToArray(); + ReadOnlyMemory maybeCopy = skipCopy ? firstValue : firstValue.ToArray(); PfxAsn pfx = PfxAsn.Decode(maybeCopy, AsnEncodingRules.BER); // https://tools.ietf.org/html/rfc7292#section-4 only defines version 3. @@ -101,43 +101,50 @@ public static Pkcs12Info Decode( } List authSafeData = new List(); - AsnValueReader authSafeReader = new AsnValueReader(authSafeBytes.Span, AsnEncodingRules.BER); - AsnValueReader sequenceReader = authSafeReader.ReadSequence(); - - authSafeReader.ThrowIfNotEmpty(); - while (sequenceReader.HasData) + try { - ContentInfoAsn.Decode(ref sequenceReader, authSafeBytes, out ContentInfoAsn contentInfo); - authSafeData.Add(contentInfo); - } + AsnValueReader authSafeReader = new AsnValueReader(authSafeBytes.Span, AsnEncodingRules.BER); + AsnValueReader sequenceReader = authSafeReader.ReadSequence(); - ReadOnlyCollection authSafe; + authSafeReader.ThrowIfNotEmpty(); + while (sequenceReader.HasData) + { + ContentInfoAsn.Decode(ref sequenceReader, authSafeBytes, out ContentInfoAsn contentInfo); + authSafeData.Add(contentInfo); + } - if (authSafeData.Count == 0) - { - authSafe = new ReadOnlyCollection(Array.Empty()); - } - else - { - Pkcs12SafeContents[] contentsArray = new Pkcs12SafeContents[authSafeData.Count]; + ReadOnlyCollection authSafe; - for (int i = 0; i < contentsArray.Length; i++) + if (authSafeData.Count == 0) { - contentsArray[i] = new Pkcs12SafeContents(authSafeData[i]); + authSafe = new ReadOnlyCollection(Array.Empty()); } + else + { + Pkcs12SafeContents[] contentsArray = new Pkcs12SafeContents[authSafeData.Count]; - authSafe = new ReadOnlyCollection(contentsArray); - } + for (int i = 0; i < contentsArray.Length; i++) + { + contentsArray[i] = new Pkcs12SafeContents(authSafeData[i]); + } + + authSafe = new ReadOnlyCollection(contentsArray); + } - bytesConsumed = encodedBytes.Length; + bytesConsumed = firstValueLength; - return new Pkcs12Info + return new Pkcs12Info + { + AuthenticatedSafe = authSafe, + IntegrityMode = mode, + _decoded = pfx, + _authSafeContents = authSafeBytes, + }; + } + catch (AsnContentException e) { - AuthenticatedSafe = authSafe, - IntegrityMode = mode, - _decoded = pfx, - _authSafeContents = authSafeBytes, - }; + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeBag.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeBag.cs index 054c9c944429a7..c67f7c07760911 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeBag.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeBag.cs @@ -4,7 +4,9 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; +using Internal.Cryptography; namespace System.Security.Cryptography.Pkcs { @@ -41,9 +43,7 @@ protected Pkcs12SafeBag(string bagIdValue, ReadOnlyMemory encodedBagValue, throw new ArgumentNullException(nameof(bagIdValue)); // Read to ensure that there is precisely one legally encoded value. - AsnReader reader = new AsnReader(encodedBagValue, AsnEncodingRules.BER); - reader.ReadEncodedValue(); - reader.ThrowIfNotEmpty(); + PkcsHelpers.EnsureSingleBerValue(encodedBagValue.Span); _bagIdValue = bagIdValue; EncodedBagValue = skipCopy ? encodedBagValue : encodedBagValue.ToArray(); @@ -51,10 +51,8 @@ protected Pkcs12SafeBag(string bagIdValue, ReadOnlyMemory encodedBagValue, public byte[] Encode() { - using (AsnWriter writer = EncodeToNewWriter()) - { - return writer.Encode(); - } + AsnWriter writer = EncodeToNewWriter(); + return writer.Encode(); } public Oid GetBagId() @@ -69,31 +67,19 @@ public Oid GetBagId() public bool TryEncode(Span destination, out int bytesWritten) { - using (AsnWriter writer = EncodeToNewWriter()) - { - ReadOnlySpan encoded = writer.EncodeAsSpan(); - - if (destination.Length < encoded.Length) - { - bytesWritten = 0; - return false; - } - - encoded.CopyTo(destination); - bytesWritten = encoded.Length; - return true; - } + AsnWriter writer = EncodeToNewWriter(); + return writer.TryEncode(destination, out bytesWritten); } internal void EncodeTo(AsnWriter writer) { writer.PushSequence(); - writer.WriteObjectIdentifier(_bagIdValue); + writer.WriteObjectIdentifierForCrypto(_bagIdValue); Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0); writer.PushSequence(contextSpecific0); - writer.WriteEncodedValue(EncodedBagValue.Span); + writer.WriteEncodedValueForCrypto(EncodedBagValue.Span); writer.PopSequence(contextSpecific0); if (_attributes?.Count > 0) @@ -116,17 +102,8 @@ internal void EncodeTo(AsnWriter writer) private AsnWriter EncodeToNewWriter() { AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); - - try - { - EncodeTo(writer); - return writer; - } - catch - { - writer.Dispose(); - throw; - } + EncodeTo(writer); + return writer; } internal sealed class UnknownBag : Pkcs12SafeBag diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContents.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContents.cs index 49a4a244061fab..32a4871c7d4bfd 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContents.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContents.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Linq; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs12; using System.Security.Cryptography.Asn1.Pkcs7; using System.Security.Cryptography.X509Certificates; @@ -172,9 +172,7 @@ public Pkcs12SecretBag AddSecret(Oid secretType, ReadOnlyMemory secretValu throw new ArgumentNullException(nameof(secretType)); // Read to ensure that there is precisely one legally encoded value. - AsnReader reader = new AsnReader(secretValue, AsnEncodingRules.BER); - reader.ReadEncodedValue(); - reader.ThrowIfNotEmpty(); + PkcsHelpers.EnsureSingleBerValue(secretValue.Span); Pkcs12SecretBag bag = new Pkcs12SecretBag(secretType, secretValue); AddSafeBag(bag); @@ -282,19 +280,27 @@ public IEnumerable GetBags() private static List ReadBags(ReadOnlyMemory serialized) { List serializedBags = new List(); - AsnValueReader reader = new AsnValueReader(serialized.Span, AsnEncodingRules.BER); - AsnValueReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); - while (sequenceReader.HasData) + try { - SafeBagAsn.Decode(ref sequenceReader, serialized, out SafeBagAsn serializedBag); - serializedBags.Add(serializedBag); - } + AsnValueReader reader = new AsnValueReader(serialized.Span, AsnEncodingRules.BER); + AsnValueReader sequenceReader = reader.ReadSequence(); + + reader.ThrowIfNotEmpty(); + while (sequenceReader.HasData) + { + SafeBagAsn.Decode(ref sequenceReader, serialized, out SafeBagAsn serializedBag); + serializedBags.Add(serializedBag); + } - if (serializedBags.Count == 0) + if (serializedBags.Count == 0) + { + return new List(0); + } + } + catch (AsnContentException e) { - return new List(0); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } List bags = new List(serializedBags.Count); @@ -328,6 +334,9 @@ private static List ReadBags(ReadOnlyMemory serialized) break; } } + catch (AsnContentException) + { + } catch (CryptographicException) { } @@ -352,81 +361,72 @@ internal byte[] Encrypt( Debug.Assert(pbeParameters != null); Debug.Assert(pbeParameters.IterationCount >= 1); - AsnWriter? writer = null; + AsnWriter contentsWriter = Encode(); - using (AsnWriter contentsWriter = Encode()) - { - ReadOnlySpan contentsSpan = contentsWriter.EncodeAsSpan(); + PasswordBasedEncryption.InitiateEncryption( + pbeParameters, + out SymmetricAlgorithm cipher, + out string hmacOid, + out string encryptionAlgorithmOid, + out bool isPkcs12); - PasswordBasedEncryption.InitiateEncryption( + int cipherBlockBytes = cipher.BlockSize / 8; + byte[] encryptedRent = CryptoPool.Rent(contentsWriter.GetEncodedLength() + cipherBlockBytes); + Span encryptedSpan = Span.Empty; + Span iv = stackalloc byte[cipherBlockBytes]; + Span salt = stackalloc byte[16]; + RandomNumberGenerator.Fill(salt); + + try + { + int written = PasswordBasedEncryption.Encrypt( + password, + passwordBytes, + cipher, + isPkcs12, + contentsWriter, pbeParameters, - out SymmetricAlgorithm cipher, - out string hmacOid, - out string encryptionAlgorithmOid, - out bool isPkcs12); - - int cipherBlockBytes = cipher.BlockSize / 8; - byte[] encryptedRent = CryptoPool.Rent(contentsSpan.Length + cipherBlockBytes); - Span encryptedSpan = Span.Empty; - Span iv = stackalloc byte[cipherBlockBytes]; - Span salt = stackalloc byte[16]; - RandomNumberGenerator.Fill(salt); + salt, + encryptedRent, + iv); - try - { - int written = PasswordBasedEncryption.Encrypt( - password, - passwordBytes, - cipher, - isPkcs12, - contentsSpan, - pbeParameters, - salt, - encryptedRent, - iv); + encryptedSpan = encryptedRent.AsSpan(0, written); - encryptedSpan = encryptedRent.AsSpan(0, written); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); - writer = new AsnWriter(AsnEncodingRules.DER); + // EncryptedData + writer.PushSequence(); - // EncryptedData - writer.PushSequence(); + // version + // Since we're not writing unprotected attributes, version=0 + writer.WriteInteger(0); - // version - // Since we're not writing unprotected attributes, version=0 - writer.WriteInteger(0); + // encryptedContentInfo + { + writer.PushSequence(); + writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data); - // encryptedContentInfo - { - writer.PushSequence(); - writer.WriteObjectIdentifier(Oids.Pkcs7Data); - - PasswordBasedEncryption.WritePbeAlgorithmIdentifier( - writer, - isPkcs12, - encryptionAlgorithmOid, - salt, - pbeParameters.IterationCount, - hmacOid, - iv); - - writer.WriteOctetString( - new Asn1Tag(TagClass.ContextSpecific, 0), - encryptedSpan); - - writer.PopSequence(); - } + PasswordBasedEncryption.WritePbeAlgorithmIdentifier( + writer, + isPkcs12, + encryptionAlgorithmOid, + salt, + pbeParameters.IterationCount, + hmacOid, + iv); + writer.WriteOctetString(encryptedSpan, new Asn1Tag(TagClass.ContextSpecific, 0)); writer.PopSequence(); - - return writer.Encode(); - } - finally - { - CryptographicOperations.ZeroMemory(encryptedSpan); - CryptoPool.Return(encryptedRent, clearSize: 0); - writer?.Dispose(); } + + writer.PopSequence(); + + return writer.Encode(); + } + finally + { + CryptographicOperations.ZeroMemory(encryptedSpan); + CryptoPool.Return(encryptedRent, clearSize: 0); } } @@ -438,7 +438,7 @@ internal AsnWriter Encode() ConfidentialityMode == Pkcs12ConfidentialityMode.PublicKey) { writer = new AsnWriter(AsnEncodingRules.BER); - writer.WriteEncodedValue(_encrypted.Span); + writer.WriteEncodedValueForCrypto(_encrypted.Span); return writer; } @@ -446,67 +446,60 @@ internal AsnWriter Encode() writer = new AsnWriter(AsnEncodingRules.BER); - try - { - writer.PushSequence(); + writer.PushSequence(); - if (_bags != null) + if (_bags != null) + { + foreach (Pkcs12SafeBag safeBag in _bags) { - foreach (Pkcs12SafeBag safeBag in _bags) - { - safeBag.EncodeTo(writer); - } + safeBag.EncodeTo(writer); } - - writer.PopSequence(); - return writer; - } - catch - { - writer.Dispose(); - throw; } + + writer.PopSequence(); + return writer; } internal ContentInfoAsn EncodeToContentInfo() { - using (AsnWriter contentsWriter = Encode()) + AsnWriter contentsWriter = Encode(); + + if (ConfidentialityMode == Pkcs12ConfidentialityMode.None) { - if (ConfidentialityMode == Pkcs12ConfidentialityMode.None) - { - using (AsnWriter valueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - valueWriter.WriteOctetString(contentsWriter.EncodeAsSpan()); + AsnWriter valueWriter = new AsnWriter(AsnEncodingRules.DER); - return new ContentInfoAsn - { - ContentType = Oids.Pkcs7Data, - Content = valueWriter.Encode(), - }; - } + using (valueWriter.PushOctetString()) + { + contentsWriter.CopyTo(valueWriter); } - if (ConfidentialityMode == Pkcs12ConfidentialityMode.Password) + return new ContentInfoAsn { - return new ContentInfoAsn - { - ContentType = Oids.Pkcs7Encrypted, - Content = contentsWriter.Encode(), - }; - } + ContentType = Oids.Pkcs7Data, + Content = valueWriter.Encode(), + }; + } - if (ConfidentialityMode == Pkcs12ConfidentialityMode.PublicKey) + if (ConfidentialityMode == Pkcs12ConfidentialityMode.Password) + { + return new ContentInfoAsn { - return new ContentInfoAsn - { - ContentType = Oids.Pkcs7Enveloped, - Content = contentsWriter.Encode(), - }; - } + ContentType = Oids.Pkcs7Encrypted, + Content = contentsWriter.Encode(), + }; + } - Debug.Fail($"No handler for {ConfidentialityMode}"); - throw new CryptographicException(); + if (ConfidentialityMode == Pkcs12ConfidentialityMode.PublicKey) + { + return new ContentInfoAsn + { + ContentType = Oids.Pkcs7Enveloped, + Content = contentsWriter.Encode(), + }; } + + Debug.Fail($"No handler for {ConfidentialityMode}"); + throw new CryptographicException(); } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContentsBag.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContentsBag.cs index cda6344eb5c82a..1536bf4dada3b7 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContentsBag.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContentsBag.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; namespace System.Security.Cryptography.Pkcs { @@ -21,10 +21,8 @@ internal static Pkcs12SafeContentsBag Create(Pkcs12SafeContents copyFrom) Debug.Assert(copyFrom != null); Debug.Assert(copyFrom.ConfidentialityMode == Pkcs12ConfidentialityMode.None); - using (AsnWriter writer = copyFrom.Encode()) - { - return Decode(writer.Encode()); - } + AsnWriter writer = copyFrom.Encode(); + return Decode(writer.Encode()); } internal static Pkcs12SafeContentsBag Decode(ReadOnlyMemory encodedValue) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SecretBag.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SecretBag.cs index e275fca76ecd05..eb9d8746ada5f1 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SecretBag.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SecretBag.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using System.Security.Cryptography.Pkcs.Asn1; namespace System.Security.Cryptography.Pkcs @@ -54,11 +54,9 @@ private static byte[] EncodeBagValue(Oid secretTypeOid, in ReadOnlyMemory SecretValue = secretValue, }; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - secretBagAsn.Encode(writer); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + secretBagAsn.Encode(writer); + return writer.Encode(); } internal static Pkcs12SecretBag DecodeValue(ReadOnlyMemory bagValue) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs index 97bc53be0ab920..a6c20a457104f3 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; using Internal.Cryptography; @@ -28,9 +29,7 @@ public Pkcs8PrivateKeyInfo( if (algorithmParameters?.Length > 0) { // Read to ensure that there is precisely one legally encoded value. - AsnReader reader = new AsnReader(algorithmParameters.Value, AsnEncodingRules.BER); - reader.ReadEncodedValue(); - reader.ThrowIfNotEmpty(); + PkcsHelpers.EnsureSingleBerValue(algorithmParameters.Value.Span); } AlgorithmId = algorithmId; @@ -67,28 +66,33 @@ public static Pkcs8PrivateKeyInfo Decode( out int bytesRead, bool skipCopy = false) { - AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); - // By using the default/empty ReadOnlyMemory value, the Decode method will have to - // make copies of the data when assigning ReadOnlyMemory fields. - ReadOnlyMemory rebind = skipCopy ? source : default; - - int localRead = reader.PeekEncodedValue().Length; - PrivateKeyInfoAsn.Decode(ref reader, rebind, out PrivateKeyInfoAsn privateKeyInfo); - bytesRead = localRead; - - return new Pkcs8PrivateKeyInfo( - privateKeyInfo.PrivateKeyAlgorithm.Algorithm, - privateKeyInfo.PrivateKeyAlgorithm.Parameters, - privateKeyInfo.PrivateKey, - SignerInfo.MakeAttributeCollection(privateKeyInfo.Attributes)); + try + { + AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); + // By using the default/empty ReadOnlyMemory value, the Decode method will have to + // make copies of the data when assigning ReadOnlyMemory fields. + ReadOnlyMemory rebind = skipCopy ? source : default; + + int localRead = reader.PeekEncodedValue().Length; + PrivateKeyInfoAsn.Decode(ref reader, rebind, out PrivateKeyInfoAsn privateKeyInfo); + bytesRead = localRead; + + return new Pkcs8PrivateKeyInfo( + new Oid(privateKeyInfo.PrivateKeyAlgorithm.Algorithm, null), + privateKeyInfo.PrivateKeyAlgorithm.Parameters, + privateKeyInfo.PrivateKey, + SignerInfo.MakeAttributeCollection(privateKeyInfo.Attributes)); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } public byte[] Encode() { - using (AsnWriter writer = WritePkcs8()) - { - return writer.Encode(); - } + AsnWriter writer = WritePkcs8(); + return writer.Encode(); } public byte[] Encrypt(ReadOnlySpan password, PbeParameters pbeParameters) @@ -101,8 +105,8 @@ public byte[] Encrypt(ReadOnlySpan password, PbeParameters pbeParameters) password, ReadOnlySpan.Empty); - using (AsnWriter pkcs8 = WritePkcs8()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(password, pkcs8, pbeParameters)) + AsnWriter pkcs8 = WritePkcs8(); + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(password, pkcs8, pbeParameters); { return writer.Encode(); } @@ -118,19 +122,15 @@ public byte[] Encrypt(ReadOnlySpan passwordBytes, PbeParameters pbeParamet ReadOnlySpan.Empty, passwordBytes); - using (AsnWriter pkcs8 = WritePkcs8()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(passwordBytes, pkcs8, pbeParameters)) - { - return writer.Encode(); - } + AsnWriter pkcs8 = WritePkcs8(); + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(passwordBytes, pkcs8, pbeParameters); + return writer.Encode(); } public bool TryEncode(Span destination, out int bytesWritten) { - using (AsnWriter writer = WritePkcs8()) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter writer = WritePkcs8(); + return writer.TryEncode(destination, out bytesWritten); } public bool TryEncrypt( @@ -147,11 +147,9 @@ public bool TryEncrypt( password, ReadOnlySpan.Empty); - using (AsnWriter pkcs8 = WritePkcs8()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(password, pkcs8, pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter pkcs8 = WritePkcs8(); + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(password, pkcs8, pbeParameters); + return writer.TryEncode(destination, out bytesWritten); } public bool TryEncrypt( @@ -168,11 +166,9 @@ public bool TryEncrypt( ReadOnlySpan.Empty, passwordBytes); - using (AsnWriter pkcs8 = WritePkcs8()) - using (AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(passwordBytes, pkcs8, pbeParameters)) - { - return writer.TryEncode(destination, out bytesWritten); - } + AsnWriter pkcs8 = WritePkcs8(); + AsnWriter writer = KeyFormatHelper.WriteEncryptedPkcs8(passwordBytes, pkcs8, pbeParameters); + return writer.TryEncode(destination, out bytesWritten); } public static Pkcs8PrivateKeyInfo DecryptAndDecode( @@ -245,7 +241,7 @@ private AsnWriter WritePkcs8() { PrivateKeyAlgorithm = { - Algorithm = AlgorithmId, + Algorithm = AlgorithmId.Value!, }, PrivateKey = PrivateKeyBytes, }; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9LocalKeyId.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9LocalKeyId.cs index 92584ddaf1560d..fc3907d2a1ea35 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9LocalKeyId.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9LocalKeyId.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using Internal.Cryptography; namespace System.Security.Cryptography.Pkcs @@ -26,11 +26,9 @@ public Pkcs9LocalKeyId(byte[] keyId) public Pkcs9LocalKeyId(ReadOnlySpan keyId) : this() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteOctetString(keyId); - RawData = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteOctetString(keyId); + RawData = writer.Encode(); } public ReadOnlyMemory KeyId => diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs index 73619763016e09..05c3875e6e2675 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using Internal.Cryptography; namespace System.Security.Cryptography.Pkcs @@ -23,11 +21,9 @@ public Pkcs9MessageDigest() : internal Pkcs9MessageDigest(ReadOnlySpan signatureDigest) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteOctetString(signatureDigest); - RawData = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteOctetString(signatureDigest); + RawData = writer.Encode(); } // diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampRequest.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampRequest.cs index b126b1aa099ab6..fca4e9166eb07e 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampRequest.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampRequest.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Linq; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs.Asn1; @@ -16,6 +17,8 @@ public sealed class Rfc3161TimestampRequest { private byte[] _encodedBytes = null!; // Initided using object initializer private Rfc3161TimeStampReq _parsedData; + private Oid? _hashAlgorithmId; + private Oid? _requestedPolicyId; private Rfc3161TimestampRequest() { @@ -23,8 +26,8 @@ private Rfc3161TimestampRequest() public int Version => _parsedData.Version; public ReadOnlyMemory GetMessageHash() => _parsedData.MessageImprint.HashedMessage; - public Oid HashAlgorithmId => _parsedData.MessageImprint.HashAlgorithm.Algorithm; - public Oid? RequestedPolicyId => _parsedData.ReqPolicy; + public Oid HashAlgorithmId => (_hashAlgorithmId ??= new Oid(_parsedData.MessageImprint.HashAlgorithm.Algorithm, null)); + public Oid? RequestedPolicyId => _parsedData.ReqPolicy == null ? null : (_requestedPolicyId ??= new Oid(_parsedData.ReqPolicy, null)); public bool RequestSignerCertificate => _parsedData.CertReq; public ReadOnlyMemory? GetNonce() => _parsedData.Nonce; public bool HasExtensions => _parsedData.Extensions?.Length > 0; @@ -95,6 +98,16 @@ private bool ProcessResponse( status = Rfc3161RequestResponseStatus.DoesNotParse; return false; } + catch (AsnContentException) when (!shouldThrow) + { + bytesConsumed = 0; + status = Rfc3161RequestResponseStatus.DoesNotParse; + return false; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } // bytesRead will be set past this point @@ -292,13 +305,13 @@ public static Rfc3161TimestampRequest CreateFromHash( { HashAlgorithm = { - Algorithm = hashAlgorithmId, + Algorithm = hashAlgorithmId.Value!, Parameters = AlgorithmIdentifierAsn.ExplicitDerNull, }, HashedMessage = hash, }, - ReqPolicy = requestedPolicyId, + ReqPolicy = requestedPolicyId?.Value, CertReq = requestSignerCertificates, Nonce = nonce, }; @@ -312,21 +325,19 @@ public static Rfc3161TimestampRequest CreateFromHash( // The RFC implies DER (see TryParse), and DER is the most widely understood given that // CER isn't specified. const AsnEncodingRules ruleSet = AsnEncodingRules.DER; - using (AsnWriter writer = new AsnWriter(ruleSet)) - { - req.Encode(writer); + AsnWriter writer = new AsnWriter(ruleSet); + req.Encode(writer); - byte[] encodedBytes = writer.Encode(); + byte[] encodedBytes = writer.Encode(); - // Make sure everything normalizes - req = Rfc3161TimeStampReq.Decode(encodedBytes, ruleSet); + // Make sure everything normalizes + req = Rfc3161TimeStampReq.Decode(encodedBytes, ruleSet); - return new Rfc3161TimestampRequest - { - _encodedBytes = writer.Encode(), - _parsedData = req, - }; - } + return new Rfc3161TimestampRequest + { + _encodedBytes = writer.Encode(), + _parsedData = req, + }; } public static bool TryDecode( @@ -358,6 +369,9 @@ public static bool TryDecode( bytesConsumed = firstElement.Length; return true; } + catch (AsnContentException) + { + } catch (CryptographicException) { } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampToken.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampToken.cs index 958308bd6d3494..43cc1957f779d8 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampToken.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampToken.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Linq; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs7; @@ -412,6 +413,9 @@ public static bool TryDecode(ReadOnlyMemory source, [NotNullWhen(true)] ou return true; } } + catch (AsnContentException) + { + } catch (CryptographicException) { } @@ -522,7 +526,7 @@ private static bool CertMatchesIds(X509Certificate2 signerCert, in EssCertId? ce else { Debug.Fail( - $"TryGetCertHash did not fit in {thumbprint.Length} for hash {certId2.Value.HashAlgorithm.Algorithm.Value}"); + $"TryGetCertHash did not fit in {thumbprint.Length} for hash {certId2.Value.HashAlgorithm.Algorithm}"); thumbprint = signerCert.GetCertHash(alg); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampTokenInfo.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampTokenInfo.cs index a9ff1feb53166b..54c1383f5aa0b1 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampTokenInfo.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Rfc3161TimestampTokenInfo.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Linq; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs.Asn1; @@ -16,6 +17,8 @@ public sealed class Rfc3161TimestampTokenInfo { private readonly byte[] _encodedBytes; private readonly Rfc3161TstInfo _parsedData; + private Oid? _policyOid; + private Oid? _hashAlgorithmId; private ReadOnlyMemory? _tsaNameBytes; public Rfc3161TimestampTokenInfo( @@ -56,8 +59,8 @@ private Rfc3161TimestampTokenInfo(byte[] copiedBytes, Rfc3161TstInfo tstInfo) } public int Version => _parsedData.Version; - public Oid PolicyId => _parsedData.Policy; - public Oid HashAlgorithmId => _parsedData.MessageImprint.HashAlgorithm.Algorithm; + public Oid PolicyId => (_policyOid ??= new Oid(_parsedData.Policy, null)); + public Oid HashAlgorithmId => (_hashAlgorithmId ??= new Oid(_parsedData.MessageImprint.HashAlgorithm.Algorithm, null)); public ReadOnlyMemory GetMessageHash() => _parsedData.MessageImprint.HashedMessage; public ReadOnlyMemory GetSerialNumber() => _parsedData.SerialNumber; public DateTimeOffset Timestamp => _parsedData.GenTime; @@ -77,12 +80,10 @@ private Rfc3161TimestampTokenInfo(byte[] copiedBytes, Rfc3161TstInfo tstInfo) return null; } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - tsaName.Value.Encode(writer); - _tsaNameBytes = writer.Encode(); - Debug.Assert(_tsaNameBytes.HasValue); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + tsaName.Value.Encode(writer); + _tsaNameBytes = writer.Encode(); + Debug.Assert(_tsaNameBytes.HasValue); } return _tsaNameBytes.Value; @@ -198,6 +199,13 @@ private static bool TryDecode( bytesConsumed = firstElement.Length; return true; } + catch (AsnContentException) + { + tstInfo = default; + bytesConsumed = 0; + copiedBytes = null; + return false; + } catch (CryptographicException) { tstInfo = default; @@ -228,12 +236,12 @@ private static byte[] Encode( { // The only legal value as of 2017. Version = 1, - Policy = policyId, + Policy = policyId.Value!, MessageImprint = { HashAlgorithm = { - Algorithm = hashAlgorithmId, + Algorithm = hashAlgorithmId.Value!, Parameters = AlgorithmIdentifierAsn.ExplicitDerNull, }, @@ -261,11 +269,9 @@ private static byte[] Encode( Select(ex => new X509ExtensionAsn(ex)).ToArray(); } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - tstInfo.Encode(writer); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + tstInfo.Encode(writer); + return writer.Encode(); } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs index d45d274e7639bd..f9befdd073742d 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Linq; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs7; @@ -119,11 +120,9 @@ public byte[] Encode() try { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - _signedData.Encode(writer); - return PkcsHelpers.EncodeContentInfo(writer.Encode(), Oids.Pkcs7Signed); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + _signedData.Encode(writer); + return PkcsHelpers.EncodeContentInfo(writer.Encode(), Oids.Pkcs7Signed); } catch (CryptographicException) when (!Detached) { @@ -140,19 +139,15 @@ public byte[] Encode() copy.EncapContentInfo.Content = null; Debug.Assert(_signedData.EncapContentInfo.Content != null); - using (AsnWriter detachedWriter = new AsnWriter(AsnEncodingRules.DER)) - { - copy.Encode(detachedWriter); - copy = SignedDataAsn.Decode(detachedWriter.Encode(), AsnEncodingRules.BER); - } + AsnWriter detachedWriter = new AsnWriter(AsnEncodingRules.DER); + copy.Encode(detachedWriter); + copy = SignedDataAsn.Decode(detachedWriter.Encode(), AsnEncodingRules.BER); copy.EncapContentInfo.Content = _signedData.EncapContentInfo.Content; - using (AsnWriter attachedWriter = new AsnWriter(AsnEncodingRules.BER)) - { - copy.Encode(attachedWriter); - return PkcsHelpers.EncodeContentInfo(attachedWriter.Encode(), Oids.Pkcs7Signed); - } + AsnWriter attachedWriter = new AsnWriter(AsnEncodingRules.BER); + copy.Encode(attachedWriter); + return PkcsHelpers.EncodeContentInfo(attachedWriter.Encode(), Oids.Pkcs7Signed); } } @@ -243,14 +238,14 @@ internal static ReadOnlyMemory GetContent( { AsnReader reader = new AsnReader(wrappedContent, AsnEncodingRules.BER); - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory inner)) + if (reader.TryReadPrimitiveOctetString(out ReadOnlyMemory inner)) { return inner; } rented = CryptoPool.Rent(wrappedContent.Length); - if (!reader.TryCopyOctetStringBytes(rented, out bytesWritten)) + if (!reader.TryReadOctetString(rented, out bytesWritten)) { Debug.Fail($"TryCopyOctetStringBytes failed with an array larger than the encoded value"); throw new CryptographicException(); @@ -261,6 +256,10 @@ internal static ReadOnlyMemory GetContent( catch (Exception) when (contentType != Oids.Pkcs7Data) { } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } finally { if (rented != null) @@ -343,12 +342,10 @@ public void ComputeSignature(CmsSigner signer, bool silent) // the copy of _heldContent or _contentType here if we're attached. if (!Detached) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteOctetString(content.Span); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteOctetString(content.Span); - _signedData.EncapContentInfo.Content = writer.Encode(); - } + _signedData.EncapContentInfo.Content = writer.Encode(); } _hasData = true; @@ -408,17 +405,30 @@ internal ReadOnlySpan GetHashableContentSpan() { Debug.Assert(_heldContent.HasValue); ReadOnlyMemory content = _heldContent.Value; + ReadOnlySpan contentSpan = content.Span; if (!_hasPkcs7Content) { - return content.Span; + return contentSpan; } // In PKCS#7 compat, only return the contents within the outermost tag. // See https://tools.ietf.org/html/rfc5652#section-5.2.1 - AsnReader reader = new AsnReader(content, AsnEncodingRules.BER); - // This span is safe to return because it's still bound under _heldContent. - return reader.PeekContentBytes().Span; + try + { + AsnDecoder.ReadEncodedValue( + contentSpan, + AsnEncodingRules.BER, + out int contentOffset, + out int contentLength, + out _); + + return contentSpan.Slice(contentOffset, contentLength); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal void Reencode() diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs index 9247ae50b14bf8..39ad8897440996 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignerInfo.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Linq; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs.Asn1; @@ -19,10 +20,10 @@ public sealed class SignerInfo public int Version { get; } public SubjectIdentifier SignerIdentifier { get; } - private readonly Oid _digestAlgorithm; + private readonly string _digestAlgorithm; private readonly AttributeAsn[]? _signedAttributes; private readonly ReadOnlyMemory? _signedAttributesMemory; - private readonly Oid _signatureAlgorithm; + private readonly string _signatureAlgorithm; private readonly ReadOnlyMemory? _signatureAlgorithmParameters; private readonly ReadOnlyMemory _signature; private readonly AttributeAsn[]? _unsignedAttributes; @@ -116,9 +117,9 @@ public SignerInfoCollection CounterSignerInfos } } - public Oid DigestAlgorithm => new Oid(_digestAlgorithm); + public Oid DigestAlgorithm => new Oid(_digestAlgorithm, null); - public Oid SignatureAlgorithm => new Oid(_signatureAlgorithm); + public Oid SignatureAlgorithm => new Oid(_signatureAlgorithm, null); private delegate void WithSelfInfoDelegate(ref SignerInfoAsn mySigned); @@ -165,7 +166,7 @@ private void WithSelfInfo(WithSelfInfoDelegate action) { ref AttributeAsn attributeAsn = ref unsignedAttrs[i]; - if (attributeAsn.AttrType.Value == Oids.CounterSigner) + if (attributeAsn.AttrType == Oids.CounterSigner) { for (int j = 0; j < attributeAsn.AttrValues.Length; j++) { @@ -179,11 +180,9 @@ private void WithSelfInfo(WithSelfInfoDelegate action) // counterSigner represent the current state of `this` action(ref counterSigner); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - counterSigner.Encode(writer); - counterSignerBytes = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + counterSigner.Encode(writer); + counterSignerBytes = writer.Encode(); // Re-normalize the document _document.Reencode(); @@ -274,7 +273,7 @@ private SignerInfoCollection GetCounterSigners(AttributeAsn[] unsignedAttrs) foreach (AttributeAsn attributeAsn in unsignedAttrs) { - if (attributeAsn.AttrType.Value == Oids.CounterSigner) + if (attributeAsn.AttrType == Oids.CounterSigner) { foreach (ReadOnlyMemory attrValue in attributeAsn.AttrValues) { @@ -321,16 +320,14 @@ public void ComputeCounterSignature(CmsSigner signer) AttributeAsn newUnsignedAttr; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - newSignerInfo.Encode(writer); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + newSignerInfo.Encode(writer); - newUnsignedAttr = new AttributeAsn - { - AttrType = new Oid(Oids.CounterSigner, Oids.CounterSigner), - AttrValues = new[] { new ReadOnlyMemory(writer.Encode()) }, - }; - } + newUnsignedAttr = new AttributeAsn + { + AttrType = Oids.CounterSigner, + AttrValues = new[] { new ReadOnlyMemory(writer.Encode()) }, + }; ref SignedDataAsn signedData = ref _document.GetRawData(); ref SignerInfoAsn mySigner = ref signedData.SignerInfos[myIdx]; @@ -395,7 +392,7 @@ public void RemoveCounterSignature(int index) { AttributeAsn attributeAsn = unsignedAttrs[i]; - if (attributeAsn.AttrType.Value == Oids.CounterSigner) + if (attributeAsn.AttrType == Oids.CounterSigner) { if (index < csIndex + attributeAsn.AttrValues.Length) { @@ -485,7 +482,7 @@ public void CheckSignature(X509Certificate2Collection extraStore, bool verifySig public void CheckHash() { - if (_signatureAlgorithm.Value != Oids.NoSignature) + if (_signatureAlgorithm != Oids.NoSignature) { throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); } @@ -615,7 +612,7 @@ private bool CheckHash(bool compatMode) { byte[] contentDigest = hasher.GetHashAndReset(); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); { // Some CMS implementations exist which do not sort the attributes prior to // generating the signature. While they are not, technically, validly signed, @@ -638,7 +635,7 @@ private bool CheckHash(bool compatMode) // .NET Framework doesn't seem to validate the content type attribute, // so we won't, either. - if (attr.AttrType.Value == Oids.MessageDigest) + if (attr.AttrType == Oids.MessageDigest) { CryptographicAttributeObject obj = MakeAttribute(attr); @@ -662,27 +659,15 @@ private bool CheckHash(bool compatMode) { writer.PopSequence(); -#if NETCOREAPP || NETSTANDARD2_1 - Span setOfTag = stackalloc byte[1]; - setOfTag[0] = 0x31; - - hasher.AppendData(setOfTag); - hasher.AppendData(writer.EncodeAsSpan().Slice(1)); -#else byte[] encoded = writer.Encode(); encoded[0] = 0x31; hasher.AppendData(encoded); -#endif } else { writer.PopSetOf(); -#if NETCOREAPP || NETSTANDARD2_1 - hasher.AppendData(writer.EncodeAsSpan()); -#else hasher.AppendData(writer.Encode()); -#endif } } } @@ -839,7 +824,7 @@ private static int FindAttributeIndexByOid(AttributeAsn[] attributes, Oid oid, i { for (int i = startIndex; i < attributes.Length; i++) { - if (attributes[i].AttrType.Value == oid.Value) + if (attributes[i].AttrType == oid.Value) { return i; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Helpers.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Helpers.cs index 8ff27c2ee65866..7cb59190cd73ef 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Helpers.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Helpers.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Globalization; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -166,78 +167,97 @@ internal static void DisposeAll(this IEnumerable disposables) public static void ValidateDer(ReadOnlyMemory encodedValue) { - Asn1Tag tag; - AsnReader reader = new AsnReader(encodedValue, AsnEncodingRules.DER); - - while (reader.HasData) + try { - tag = reader.PeekTag(); + Asn1Tag tag; + AsnReader reader = new AsnReader(encodedValue, AsnEncodingRules.DER); - // If the tag is in the UNIVERSAL class - // - // DER limits the constructed encoding to SEQUENCE and SET, as well as anything which gets - // a defined encoding as being an IMPLICIT SEQUENCE. - if (tag.TagClass == TagClass.Universal) + while (reader.HasData) { - switch ((UniversalTagNumber)tag.TagValue) + tag = reader.PeekTag(); + + // If the tag is in the UNIVERSAL class + // + // DER limits the constructed encoding to SEQUENCE and SET, as well as anything which gets + // a defined encoding as being an IMPLICIT SEQUENCE. + if (tag.TagClass == TagClass.Universal) { - case UniversalTagNumber.External: - case UniversalTagNumber.Embedded: - case UniversalTagNumber.Sequence: - case UniversalTagNumber.Set: - case UniversalTagNumber.UnrestrictedCharacterString: - if (!tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - break; - - default: - if (tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - break; + switch ((UniversalTagNumber)tag.TagValue) + { + case UniversalTagNumber.External: + case UniversalTagNumber.Embedded: + case UniversalTagNumber.Sequence: + case UniversalTagNumber.Set: + case UniversalTagNumber.UnrestrictedCharacterString: + if (!tag.IsConstructed) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + break; + default: + if (tag.IsConstructed) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + break; + } } - } - if (tag.IsConstructed) - { - ValidateDer(reader.PeekContentBytes()); - } + if (tag.IsConstructed) + { + ValidateDer(reader.PeekContentBytes()); + } - // Skip past the current value. - reader.ReadEncodedValue(); + // Skip past the current value. + reader.ReadEncodedValue(); + } + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } } public static ReadOnlyMemory DecodeOctetStringAsMemory(ReadOnlyMemory encodedOctetString) { - AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER); - - if (reader.PeekEncodedValue().Length != encodedOctetString.Length) + try { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } + ReadOnlySpan input = encodedOctetString.Span; - // Almost everything in X.509 is DER-encoded, which means Octet String values are - // encoded as a primitive (non-segmented) - // - // Even in BER Octet Strings are usually encoded as a primitive. - if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory primitiveContents)) - { - return primitiveContents; - } + if (AsnDecoder.TryReadPrimitiveOctetString( + input, + AsnEncodingRules.BER, + out ReadOnlySpan primitive, + out int consumed)) + { + if (consumed != input.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (input.Overlaps(primitive, out int offset)) + { + return encodedOctetString.Slice(offset, primitive.Length); + } + + Debug.Fail("input.Overlaps(primitive) failed after TryReadPrimitiveOctetString succeeded"); + } - byte[] tooBig = new byte[encodedOctetString.Length]; + byte[] ret = AsnDecoder.ReadOctetString(input, AsnEncodingRules.BER, out consumed); - if (reader.TryCopyOctetStringBytes(tooBig, out int bytesWritten)) + if (consumed != input.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return ret; + } + catch (AsnContentException e) { - return tooBig.AsMemory(0, bytesWritten); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - Debug.Fail("TryCopyOctetStringBytes failed with an over-allocated array"); - throw new CryptographicException(); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index 08faefd0c4f7d1..bc11ac0d721f28 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -6,9 +6,9 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Apple; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; using System.Text; using Microsoft.Win32.SafeHandles; @@ -404,14 +404,13 @@ internal static unsafe byte[] ExportPkcs8(SafeSecKeyRefHandle key, ReadOnlySpan< // // Since Apple only reliably exports keys with encrypted PKCS#8 there's not a // "so export it plaintext and only encrypt it once" option. - using (AsnWriter writer = KeyFormatHelper.ReencryptPkcs8( + AsnWriter writer = KeyFormatHelper.ReencryptPkcs8( password, manager.Memory, password, - UnixExportProvider.s_windowsPbe)) - { - return writer.Encode(); - } + UnixExportProvider.s_windowsPbe); + + return writer.Encode(); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ApplePkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ApplePkcs12Reader.cs index 9c8190959ade0a..8f9c872923eefe 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ApplePkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ApplePkcs12Reader.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Apple; using System.Security.Cryptography.Asn1; @@ -47,7 +48,7 @@ protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(pkcs8, AsnEncodingRules.BER); AsymmetricAlgorithm key; - switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm.Value) + switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm) { case Oids.Rsa: key = new RSAImplementation.RSASecurityTransforms(); @@ -62,7 +63,7 @@ protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) default: throw new CryptographicException( SR.Cryptography_UnknownAlgorithmIdentifier, - privateKeyInfo.PrivateKeyAlgorithm.Algorithm.Value); + privateKeyInfo.PrivateKeyAlgorithm.Algorithm); } key.ImportPkcs8PrivateKey(pkcs8.Span, out int bytesRead); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs index f9a22a425039e7..dded057cc62e89 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificateData.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; @@ -35,7 +36,7 @@ internal struct AlgorithmIdentifier { public AlgorithmIdentifier(AlgorithmIdentifierAsn algorithmIdentifier) { - AlgorithmId = algorithmIdentifier.Algorithm.Value; + AlgorithmId = algorithmIdentifier.Algorithm; Parameters = algorithmIdentifier.Parameters?.ToArray() ?? Array.Empty(); } @@ -82,11 +83,9 @@ internal CertificateData(byte[] rawData) Issuer = new X500DistinguishedName(certificate.TbsCertificate.Issuer.ToArray()); Subject = new X500DistinguishedName(certificate.TbsCertificate.Subject.ToArray()); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - certificate.TbsCertificate.SubjectPublicKeyInfo.Encode(writer); - SubjectPublicKeyInfo = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + certificate.TbsCertificate.SubjectPublicKeyInfo.Encode(writer); + SubjectPublicKeyInfo = writer.Encode(); Extensions = new List(); if (certificate.TbsCertificate.Extensions != null) @@ -275,65 +274,85 @@ public string GetNameInfo(X509NameType nameType, bool forIssuer) otherOid == null || otherOid == Oids.UserPrincipalName, $"otherOid ({otherOid}) is not supported"); - AsnValueReader reader = new AsnValueReader(extensionBytes, AsnEncodingRules.DER); - AsnValueReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); - - while (sequenceReader.HasData) + try { - GeneralNameAsn.Decode(ref sequenceReader, extensionBytes, out GeneralNameAsn generalName); + AsnValueReader reader = new AsnValueReader(extensionBytes, AsnEncodingRules.DER); + AsnValueReader sequenceReader = reader.ReadSequence(); + reader.ThrowIfNotEmpty(); - switch (matchType) + while (sequenceReader.HasData) { - case GeneralNameType.OtherName: - // If the OtherName OID didn't match, move to the next entry. - if (generalName.OtherName.HasValue && generalName.OtherName.Value.TypeId == otherOid) - { - // Currently only UPN is supported, which is a UTF8 string per - // https://msdn.microsoft.com/en-us/library/ff842518.aspx - AsnReader nameReader = new AsnReader(generalName.OtherName.Value.Value, AsnEncodingRules.DER); - string udnName = nameReader.ReadCharacterString(UniversalTagNumber.UTF8String); - nameReader.ThrowIfNotEmpty(); - return udnName; - } - break; + GeneralNameAsn.Decode(ref sequenceReader, extensionBytes, out GeneralNameAsn generalName); - case GeneralNameType.Rfc822Name: - if (generalName.Rfc822Name != null) - { - return generalName.Rfc822Name; - } - break; + switch (matchType) + { + case GeneralNameType.OtherName: + // If the OtherName OID didn't match, move to the next entry. + if (generalName.OtherName.HasValue && generalName.OtherName.Value.TypeId == otherOid) + { + // Currently only UPN is supported, which is a UTF8 string per + // https://msdn.microsoft.com/en-us/library/ff842518.aspx + AsnValueReader nameReader = new AsnValueReader( + generalName.OtherName.Value.Value.Span, + AsnEncodingRules.DER); + + string udnName = nameReader.ReadCharacterString(UniversalTagNumber.UTF8String); + nameReader.ThrowIfNotEmpty(); + return udnName; + } - case GeneralNameType.DnsName: - if (generalName.DnsName != null) - { - return generalName.DnsName; - } - break; + break; + case GeneralNameType.Rfc822Name: + if (generalName.Rfc822Name != null) + { + return generalName.Rfc822Name; + } - case GeneralNameType.UniformResourceIdentifier: - if (generalName.Uri != null) - { - return generalName.Uri; - } - break; + break; + case GeneralNameType.DnsName: + if (generalName.DnsName != null) + { + return generalName.DnsName; + } + + break; + case GeneralNameType.UniformResourceIdentifier: + if (generalName.Uri != null) + { + return generalName.Uri; + } + + break; + } } } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } return null; } private static IEnumerable> ReadReverseRdns(X500DistinguishedName name) { - AsnReader x500NameReader = new AsnReader(name.RawData, AsnEncodingRules.DER); - AsnReader sequenceReader = x500NameReader.ReadSequence(); - var rdnReaders = new Stack(); - x500NameReader.ThrowIfNotEmpty(); + Stack rdnReaders; + + try + { + AsnReader x500NameReader = new AsnReader(name.RawData, AsnEncodingRules.DER); + AsnReader sequenceReader = x500NameReader.ReadSequence(); + x500NameReader.ThrowIfNotEmpty(); + rdnReaders = new Stack(); - while (sequenceReader.HasData) + while (sequenceReader.HasData) + { + rdnReaders.Push(sequenceReader.ReadSetOf()); + } + } + catch (AsnContentException e) { - rdnReaders.Push(sequenceReader.ReadSetOf()); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } while (rdnReaders.Count > 0) @@ -341,10 +360,21 @@ private static IEnumerable> ReadReverseRdns(X500Dis AsnReader rdnReader = rdnReaders.Pop(); while (rdnReader.HasData) { - AsnReader tavReader = rdnReader.ReadSequence(); - string oid = tavReader.ReadObjectIdentifierAsString(); - string value = tavReader.ReadAnyAsnString(); - tavReader.ThrowIfNotEmpty(); + string oid; + string value; + + try + { + AsnReader tavReader = rdnReader.ReadSequence(); + oid = tavReader.ReadObjectIdentifier(); + value = tavReader.ReadAnyAsnString(); + tavReader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + yield return new KeyValuePair(oid, value); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs index e1b86cdaf30fd9..fe45ba5efb829e 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs @@ -4,7 +4,7 @@ using System; using System.Diagnostics; -using System.Numerics; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Apple; using System.Security.Cryptography.Asn1; @@ -102,24 +102,34 @@ private static AsymmetricAlgorithm DecodeDsaPublicKey(byte[] encodedKeyValue, by { SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn { - Algorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(Oids.Dsa, null), Parameters = encodedParameters }, + Algorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Dsa, Parameters = encodedParameters }, SubjectPublicKey = encodedKeyValue, }; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + spki.Encode(writer); + + byte[] rented = CryptoPool.Rent(writer.GetEncodedLength()); + + if (!writer.TryEncode(rented, out int written)) { - spki.Encode(writer); - DSA dsa = DSA.Create(); - try - { - dsa.ImportSubjectPublicKeyInfo(writer.EncodeAsSpan(), out _); - return dsa; - } - catch (Exception) - { - dsa.Dispose(); - throw; - } + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + DSA dsa = DSA.Create(); + IDisposable? toDispose = dsa; + + try + { + dsa.ImportSubjectPublicKeyInfo(rented.AsSpan(0, written), out _); + toDispose = null; + return dsa; + } + finally + { + toDispose?.Dispose(); + CryptoPool.Return(rented, written); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CertificatePolicy.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CertificatePolicy.cs index 3387ccd5c75a56..8f685cdfb2aa7a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CertificatePolicy.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CertificatePolicy.cs @@ -4,9 +4,8 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; +using System.Formats.Asn1; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates.Asn1; @@ -292,11 +291,18 @@ private static bool CheckExplicitAnyPolicy(ISet? declaredPolicies) private static int ReadInhibitAnyPolicyExtension(byte[] rawData) { - AsnReader reader = new AsnReader(rawData, AsnEncodingRules.DER); - int inhibitAnyPolicy; - reader.TryReadInt32(out inhibitAnyPolicy); - reader.ThrowIfNotEmpty(); - return inhibitAnyPolicy; + try + { + AsnReader reader = new AsnReader(rawData, AsnEncodingRules.DER); + int inhibitAnyPolicy; + reader.TryReadInt32(out inhibitAnyPolicy); + reader.ThrowIfNotEmpty(); + return inhibitAnyPolicy; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } private static void ReadCertPolicyConstraintsExtension(byte[] rawData, CertificatePolicy policy) @@ -313,14 +319,21 @@ private static ISet ReadExtendedKeyUsageExtension(byte[] rawData) { HashSet oids = new HashSet(); - AsnReader reader = new AsnReader(rawData, AsnEncodingRules.DER); - AsnReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); + try + { + AsnReader reader = new AsnReader(rawData, AsnEncodingRules.DER); + AsnReader sequenceReader = reader.ReadSequence(); + reader.ThrowIfNotEmpty(); - //OidCollection usages - while (sequenceReader.HasData) + //OidCollection usages + while (sequenceReader.HasData) + { + oids.Add(sequenceReader.ReadObjectIdentifier()); + } + } + catch (AsnContentException e) { - oids.Add(sequenceReader.ReadObjectIdentifierAsString()); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } return oids; @@ -328,40 +341,54 @@ private static ISet ReadExtendedKeyUsageExtension(byte[] rawData) internal static ISet ReadCertPolicyExtension(byte[] rawData) { - AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER); - AsnValueReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); - - HashSet policies = new HashSet(); - while (sequenceReader.HasData) + try { - PolicyInformationAsn.Decode(ref sequenceReader, rawData, out PolicyInformationAsn policyInformation); - policies.Add(policyInformation.PolicyIdentifier); + AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER); + AsnValueReader sequenceReader = reader.ReadSequence(); + reader.ThrowIfNotEmpty(); - // There is an optional policy qualifier here, but it is for information - // purposes, there is no logic that would be changed. + HashSet policies = new HashSet(); + while (sequenceReader.HasData) + { + PolicyInformationAsn.Decode(ref sequenceReader, rawData, out PolicyInformationAsn policyInformation); + policies.Add(policyInformation.PolicyIdentifier); - // Since reader (the outer one) has already skipped past the rest of the - // sequence we don't particularly need to drain out here. - } + // There is an optional policy qualifier here, but it is for information + // purposes, there is no logic that would be changed. - return policies; + // Since reader (the outer one) has already skipped past the rest of the + // sequence we don't particularly need to drain out here. + } + + return policies; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } private static List ReadCertPolicyMappingsExtension(byte[] rawData) { - AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER); - AsnValueReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); + try + { + AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER); + AsnValueReader sequenceReader = reader.ReadSequence(); + reader.ThrowIfNotEmpty(); - List mappings = new List(); - while (sequenceReader.HasData) + List mappings = new List(); + while (sequenceReader.HasData) + { + CertificatePolicyMappingAsn.Decode(ref sequenceReader, rawData, out CertificatePolicyMappingAsn mapping); + mappings.Add(mapping); + } + + return mappings; + } + catch (AsnContentException e) { - CertificatePolicyMappingAsn.Decode(ref sequenceReader, rawData, out CertificatePolicyMappingAsn mapping); - mappings.Add(mapping); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - return mappings; } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CrlCache.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CrlCache.cs index 22c0d5c65beecd..aeb2a2b4254351 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CrlCache.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CrlCache.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -273,6 +274,10 @@ private static string GetCachedCrlPath(string localFileName, bool mkDir=false) { // Treat any ASN errors as if the extension was missing. } + catch (AsnContentException) + { + // Treat any ASN errors as if the extension was missing. + } finally { // The data came from a certificate, so it's public. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs index fed160f2637be2..d7ccc9cb5a3c23 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Numerics; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates.Asn1; @@ -134,10 +134,19 @@ public void FindByTemplateName(string templateName) if (ext != null) { - // Try a V1 template structure, just a string: - AsnReader reader = new AsnReader(ext.RawData, AsnEncodingRules.DER); - string decodedName = reader.ReadAnyAsnString(); - reader.ThrowIfNotEmpty(); + string decodedName; + + try + { + // Try a V1 template structure, just a string: + AsnReader reader = new AsnReader(ext.RawData, AsnEncodingRules.DER); + decodedName = reader.ReadAnyAsnString(); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } // If this doesn't match, maybe a V2 template will if (StringComparer.OrdinalIgnoreCase.Equals(templateName, decodedName)) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs index e88618e8ce1502..3a39724bc079cc 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; @@ -23,18 +24,25 @@ public virtual byte[] EncodeX509KeyUsageExtension(X509KeyUsageFlags keyUsages) (KeyUsageFlagsAsn)(ReverseBitOrder((byte)(((ushort)keyUsages >> 8))) << 8); // The expected output of this method isn't the SEQUENCE value, but just the payload bytes. - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteNamedBitList(keyUsagesAsn); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteNamedBitList(keyUsagesAsn); + return writer.Encode(); } public virtual void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags keyUsages) { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER); - KeyUsageFlagsAsn keyUsagesAsn = reader.ReadNamedBitListValue(); - reader.ThrowIfNotEmpty(); + KeyUsageFlagsAsn keyUsagesAsn; + + try + { + AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER); + keyUsagesAsn = reader.ReadNamedBitListValue(); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } // DER encodings of BIT_STRING values number the bits as // 01234567 89 (big endian), plus a number saying how many bits of the last byte were padding. @@ -75,11 +83,9 @@ public virtual byte[] EncodeX509BasicConstraints2Extension( if (hasPathLengthConstraint) constraints.PathLengthConstraint = pathLengthConstraint; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - constraints.Encode(writer); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + constraints.Encode(writer); + return writer.Encode(); } public virtual bool SupportsLegacyBasicConstraintsExtension => false; @@ -121,16 +127,17 @@ public virtual byte[] EncodeX509EnhancedKeyUsageExtension(OidCollection usages) // // KeyPurposeId ::= OBJECT IDENTIFIER - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + using (writer.PushSequence()) { - writer.PushSequence(); foreach (Oid usage in usages) { - writer.WriteObjectIdentifier(usage); + writer.WriteObjectIdentifierForCrypto(usage.Value!); } - writer.PopSequence(); - return writer.Encode(); } + + return writer.Encode(); } public virtual void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages) @@ -141,13 +148,21 @@ public virtual void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidC // // KeyPurposeId ::= OBJECT IDENTIFIER - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER); - AsnReader sequenceReader = reader.ReadSequence(); - reader.ThrowIfNotEmpty(); - usages = new OidCollection(); - while (sequenceReader.HasData) + try + { + AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER); + AsnReader sequenceReader = reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + usages = new OidCollection(); + + while (sequenceReader.HasData) + { + usages.Add(new Oid(sequenceReader.ReadObjectIdentifier(), null)); + } + } + catch (AsnContentException e) { - usages.Add(sequenceReader.ReadObjectIdentifier()); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } } @@ -164,11 +179,9 @@ public virtual byte[] EncodeX509SubjectKeyIdentifierExtension(byte[] subjectKeyI // // KeyIdentifier ::= OCTET STRING - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteOctetString(subjectKeyIdentifier); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteOctetString(subjectKeyIdentifier); + return writer.Encode(); } public virtual void DecodeX509SubjectKeyIdentifierExtension(byte[] encoded, out byte[] subjectKeyIdentifier) @@ -178,13 +191,26 @@ public virtual void DecodeX509SubjectKeyIdentifierExtension(byte[] encoded, out internal static byte[] DecodeX509SubjectKeyIdentifierExtension(byte[] encoded) { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER); - ReadOnlyMemory contents; - if (!reader.TryReadPrimitiveOctetStringBytes(out contents)) + ReadOnlySpan contents; + + try { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + bool gotContents = AsnDecoder.TryReadPrimitiveOctetString( + encoded, + AsnEncodingRules.BER, + out contents, + out int consumed); + + if (!gotContents || consumed != encoded.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - reader.ThrowIfNotEmpty(); + return contents.ToArray(); } @@ -195,13 +221,14 @@ public virtual byte[] ComputeCapiSha1OfPublicKey(PublicKey key) // of the DER structural bytes. SubjectPublicKeyInfoAsn spki = default; - spki.Algorithm = new AlgorithmIdentifierAsn { Algorithm = key.Oid, Parameters = key.EncodedParameters.RawData }; + spki.Algorithm = new AlgorithmIdentifierAsn { Algorithm = key.Oid!.Value!, Parameters = key.EncodedParameters.RawData }; spki.SubjectPublicKey = key.EncodedKeyValue.RawData; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + spki.Encode(writer); + using (SHA1 hash = SHA1.Create()) { - spki.Encode(writer); return hash.ComputeHash(writer.Encode()); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslPkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslPkcs12Reader.cs index 842472e98dd11e..d55d3449ca5d37 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslPkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslPkcs12Reader.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -37,7 +38,7 @@ protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(pkcs8, AsnEncodingRules.BER); AsymmetricAlgorithm key; - switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm.Value) + switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm) { case Oids.Rsa: key = new RSAOpenSsl(); @@ -52,7 +53,7 @@ protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) default: throw new CryptographicException( SR.Cryptography_UnknownAlgorithmIdentifier, - privateKeyInfo.PrivateKeyAlgorithm.Algorithm.Value); + privateKeyInfo.PrivateKeyAlgorithm.Algorithm); } key.ImportPkcs8PrivateKey(pkcs8.Span, out int bytesRead); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs index 644e31849d9bff..0aa954dbd8c641 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -1134,6 +1135,10 @@ private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.Crypto.X { // Treat any ASN errors as if the extension was missing. } + catch (AsnContentException) + { + // Treat any ASN errors as if the extension was missing. + } return null; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs index 49320d45ee64c9..b409468e71abcb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -271,24 +272,23 @@ private static DSA BuildDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParam { SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn { - Algorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(Oids.Dsa, null), Parameters = encodedParameters }, + Algorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Dsa, Parameters = encodedParameters }, SubjectPublicKey = encodedKeyValue, }; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + spki.Encode(writer); + + DSA dsa = new DSAOpenSsl(); + try { - spki.Encode(writer); - DSA dsa = new DSAOpenSsl(); - try - { - dsa.ImportSubjectPublicKeyInfo(writer.EncodeAsSpan(), out _); - return dsa; - } - catch (Exception) - { - dsa.Dispose(); - throw; - } + dsa.ImportSubjectPublicKeyInfo(writer.Encode(), out _); + return dsa; + } + catch (Exception) + { + dsa.Dispose(); + throw; } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixExportProvider.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixExportProvider.cs index 89c5e0ce7eaf0a..0d5639b8cec199 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixExportProvider.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixExportProvider.cs @@ -6,6 +6,7 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -155,7 +156,6 @@ private byte[] ExportPfx(SafePasswordHandle password) finally { password.DangerousRelease(); - tmpWriter.Dispose(); certAttrs.AsSpan(0, certCount).Clear(); certBags.AsSpan(0, certCount).Clear(); keyBags.AsSpan(0, certCount).Clear(); @@ -199,7 +199,7 @@ private void BuildBags( AttributeAsn attribute = new AttributeAsn { - AttrType = new Oid(Oids.LocalKeyId, null), + AttrType = Oids.LocalKeyId, AttrValues = new ReadOnlyMemory[] { attrBytes, @@ -289,18 +289,25 @@ private static unsafe ArraySegment EncodeAuthSafe( private static ArraySegment EncodeKeys(AsnWriter tmpWriter, SafeBagAsn[] keyBags, int keyCount) { Debug.Assert(tmpWriter.GetEncodedLength() == 0); - tmpWriter.PushSequence(); - for (int i = 0; i < keyCount; i++) + using (tmpWriter.PushSequence()) { - keyBags[i].Encode(tmpWriter); + for (int i = 0; i < keyCount; i++) + { + keyBags[i].Encode(tmpWriter); + } } - tmpWriter.PopSequence(); - ReadOnlySpan encodedKeys = tmpWriter.EncodeAsSpan(); - int length = encodedKeys.Length; + int length = tmpWriter.GetEncodedLength(); byte[] keyBuf = CryptoPool.Rent(length); - encodedKeys.CopyTo(keyBuf); + + if (!tmpWriter.TryEncode(keyBuf, out length)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + // Explicitly clear the internal buffer before it goes out of scope. tmpWriter.Reset(); return new ArraySegment(keyBuf, 0, length); @@ -344,7 +351,7 @@ private static ArraySegment EncodeCerts( // } tmpWriter.PushSequence(); - tmpWriter.WriteObjectIdentifier(Oids.Pkcs12CertBag); + tmpWriter.WriteObjectIdentifierForCrypto(Oids.Pkcs12CertBag); tmpWriter.PushSequence(s_contextSpecific0); certBags[i].Encode(tmpWriter); @@ -361,12 +368,11 @@ private static ArraySegment EncodeCerts( } tmpWriter.PopSequence(); - ReadOnlySpan contentsSpan = tmpWriter.EncodeAsSpan(); // The padding applied will add at most a block to the output, // so ask for contentsSpan.Length + the number of bytes in a cipher block. int cipherBlockBytes = cipher.BlockSize >> 3; - int requestedSize = checked(contentsSpan.Length + cipherBlockBytes); + int requestedSize = checked(tmpWriter.GetEncodedLength() + cipherBlockBytes); byte[] certContents = CryptoPool.Rent(requestedSize); int encryptedLength = PasswordBasedEncryption.Encrypt( @@ -374,7 +380,7 @@ private static ArraySegment EncodeCerts( ReadOnlySpan.Empty, cipher, isPkcs12, - contentsSpan, + tmpWriter, s_windowsPbe, salt, certContents, @@ -442,7 +448,7 @@ private static ArraySegment EncodeAuthSafe( hmacOid, certContentsIv); - tmpWriter.WriteOctetString(s_contextSpecific0, encodedCertContents.Span); + tmpWriter.WriteOctetString(encodedCertContents.Span, s_contextSpecific0); tmpWriter.PopSequence(); } @@ -456,12 +462,18 @@ private static ArraySegment EncodeAuthSafe( tmpWriter.PopSequence(); - ReadOnlySpan authSafeSpan = tmpWriter.EncodeAsSpan(); - byte[] authSafe = CryptoPool.Rent(authSafeSpan.Length); - authSafeSpan.CopyTo(authSafe); + int authSafeLength = tmpWriter.GetEncodedLength(); + byte[] authSafe = CryptoPool.Rent(authSafeLength); + + if (!tmpWriter.TryEncode(authSafe, out authSafeLength)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + tmpWriter.Reset(); - return new ArraySegment(authSafe, 0, authSafeSpan.Length); + return new ArraySegment(authSafe, 0, authSafeLength); } private static unsafe byte[] MacAndEncode( diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs index d1d453246f776a..d32d7118606501 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.Asn1; @@ -31,24 +32,31 @@ internal abstract class UnixPkcs12Reader : IDisposable protected void ParsePkcs12(byte[] data) { - // RFC7292 specifies BER instead of DER - AsnValueReader reader = new AsnValueReader(data, AsnEncodingRules.BER); - ReadOnlySpan encodedData = reader.PeekEncodedValue(); - - // Windows compatibility: Ignore trailing data. - if (encodedData.Length != data.Length) + try { - reader = new AsnValueReader(encodedData, AsnEncodingRules.BER); - } + // RFC7292 specifies BER instead of DER + AsnValueReader reader = new AsnValueReader(data, AsnEncodingRules.BER); + ReadOnlySpan encodedData = reader.PeekEncodedValue(); + + // Windows compatibility: Ignore trailing data. + if (encodedData.Length != data.Length) + { + reader = new AsnValueReader(encodedData, AsnEncodingRules.BER); + } - PfxAsn.Decode(ref reader, data, out PfxAsn pfxAsn); + PfxAsn.Decode(ref reader, data, out PfxAsn pfxAsn); - if (pfxAsn.AuthSafe.ContentType != Oids.Pkcs7Data) + if (pfxAsn.AuthSafe.ContentType != Oids.Pkcs7Data) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + _pfxAsn = pfxAsn; + } + catch (AsnContentException e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - _pfxAsn = pfxAsn; } internal CertAndKey GetSingleCert() @@ -337,20 +345,27 @@ private static ContentInfoAsn[] DecodeSafeContents(ReadOnlyMemory authSafe // and one plain (contains encrypted keys) ContentInfoAsn[] rented = ArrayPool.Shared.Rent(10); - AsnValueReader outer = new AsnValueReader(authSafeContents.Span, AsnEncodingRules.BER); - AsnValueReader reader = outer.ReadSequence(); - outer.ThrowIfNotEmpty(); - int i = 0; + try + { + AsnValueReader outer = new AsnValueReader(authSafeContents.Span, AsnEncodingRules.BER); + AsnValueReader reader = outer.ReadSequence(); + outer.ThrowIfNotEmpty(); + int i = 0; + + while (reader.HasData) + { + GrowIfNeeded(ref rented, i); + ContentInfoAsn.Decode(ref reader, authSafeContents, out rented[i]); + i++; + } - while (reader.HasData) + rented.AsSpan(i).Clear(); + return rented; + } + catch (AsnContentException e) { - GrowIfNeeded(ref rented, i); - ContentInfoAsn.Decode(ref reader, authSafeContents, out rented[i]); - i++; + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - rented.AsSpan(i).Clear(); - return rented; } private void DecryptAndProcessSafeContents( @@ -469,7 +484,7 @@ private void BuildCertsWithKeys( foreach (AttributeAsn attr in certBagAttrs[certBagIdx] ?? Array.Empty()) { - if (attr.AttrType.Value == Oids.LocalKeyId && attr.AttrValues.Length > 0) + if (attr.AttrType == Oids.LocalKeyId && attr.AttrValues.Length > 0) { matchingKeyIdx = FindMatchingKey( keyBags, @@ -533,7 +548,7 @@ private static bool PublicKeyMatches( { case Oids.Rsa: case Oids.RsaPss: - switch (publicKeyInfo.Algorithm.Algorithm.Value) + switch (publicKeyInfo.Algorithm.Algorithm) { case Oids.Rsa: case Oids.RsaPss: @@ -547,7 +562,7 @@ private static bool PublicKeyMatches( AlgorithmIdentifierAsn.RepresentsNull(keyParams); case Oids.EcPublicKey: case Oids.EcDiffieHellman: - switch (publicKeyInfo.Algorithm.Algorithm.Value) + switch (publicKeyInfo.Algorithm.Algorithm) { case Oids.EcPublicKey: case Oids.EcDiffieHellman: @@ -561,7 +576,7 @@ private static bool PublicKeyMatches( publicKeyInfo.Algorithm.Parameters.Value.Span.SequenceEqual(keyParams); } - if (algorithm != publicKeyInfo.Algorithm.Algorithm.Value) + if (algorithm != publicKeyInfo.Algorithm.Algorithm) { return false; } @@ -583,7 +598,7 @@ private static int FindMatchingKey( { foreach (AttributeAsn attr in keyBags[i].BagAttributes ?? Array.Empty()) { - if (attr.AttrType.Value == Oids.LocalKeyId && attr.AttrValues.Length > 0) + if (attr.AttrType == Oids.LocalKeyId && attr.AttrValues.Length > 0) { ReadOnlyMemory curKeyId = Helpers.DecodeOctetStringAsMemory(attr.AttrValues[0]); @@ -668,33 +683,40 @@ private static void ProcessSafeContents( contentData = Helpers.DecodeOctetStringAsMemory(contentData); } - AsnValueReader outer = new AsnValueReader(contentData.Span, AsnEncodingRules.BER); - AsnValueReader reader = outer.ReadSequence(); - outer.ThrowIfNotEmpty(); - - while (reader.HasData) + try { - SafeBagAsn.Decode(ref reader, contentData, out SafeBagAsn bag); + AsnValueReader outer = new AsnValueReader(contentData.Span, AsnEncodingRules.BER); + AsnValueReader reader = outer.ReadSequence(); + outer.ThrowIfNotEmpty(); - if (bag.BagId == Oids.Pkcs12CertBag) + while (reader.HasData) { - CertBagAsn certBag = CertBagAsn.Decode(bag.BagValue, AsnEncodingRules.BER); + SafeBagAsn.Decode(ref reader, contentData, out SafeBagAsn bag); - if (certBag.CertId == Oids.Pkcs12X509CertBagType) + if (bag.BagId == Oids.Pkcs12CertBag) { - GrowIfNeeded(ref certBags, certBagIdx); - GrowIfNeeded(ref certBagAttrs, certBagIdx); - certBags[certBagIdx] = certBag; - certBagAttrs[certBagIdx] = bag.BagAttributes; - certBagIdx++; + CertBagAsn certBag = CertBagAsn.Decode(bag.BagValue, AsnEncodingRules.BER); + + if (certBag.CertId == Oids.Pkcs12X509CertBagType) + { + GrowIfNeeded(ref certBags, certBagIdx); + GrowIfNeeded(ref certBagAttrs, certBagIdx); + certBags[certBagIdx] = certBag; + certBagAttrs[certBagIdx] = bag.BagAttributes; + certBagIdx++; + } + } + else if (bag.BagId == Oids.Pkcs12KeyBag || bag.BagId == Oids.Pkcs12ShroudedKeyBag) + { + GrowIfNeeded(ref keyBags, keyBagIdx); + keyBags[keyBagIdx] = bag; + keyBagIdx++; } } - else if (bag.BagId == Oids.Pkcs12KeyBag || bag.BagId == Oids.Pkcs12ShroudedKeyBag) - { - GrowIfNeeded(ref keyBags, keyBagIdx); - keyBags[keyBagIdx] = bag; - keyBagIdx++; - } + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs index 877762e189ba36..83da553de35702 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; using System.Text; namespace Internal.Cryptography.Pal @@ -21,123 +21,129 @@ private static string X500DistinguishedNameDecode( string multiValueSeparator, bool addTrailingDelimiter) { - AsnReader x500NameReader = new AsnReader(encodedName, AsnEncodingRules.DER); - AsnReader x500NameSequenceReader = x500NameReader.ReadSequence(); - var rdnReaders = new List(); - - x500NameReader.ThrowIfNotEmpty(); - - while (x500NameSequenceReader.HasData) - { - // To match Windows' behavior, permit multi-value RDN SETs to not - // be DER sorted. - rdnReaders.Add(x500NameSequenceReader.ReadSetOf(skipSortOrderValidation: true)); - } - - // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual - // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting - // too much space in the average case. - // - // So, let's look at an example of what our output might be. - // - // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs): - // businessCategory=Private Organization - // 1.3.6.1.4.1.311.60.2.1.3=US - // 1.3.6.1.4.1.311.60.2.1.2=Delaware - // serialNumber=5157550 - // street=548 4th Street - // postalCode=94107 - // C=US - // ST=California - // L=San Francisco - // O=GitHub, Inc. - // CN=github.com - // - // Which comes out to 228 characters using OpenSSL's default pretty-print - // (openssl x509 -in github.cer -text -noout) - // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values - // and round that up to the next programmer number, and you get that 512 should avoid reallocations - // in all but the most dire of cases. - StringBuilder decodedName = new StringBuilder(512); - int entryCount = rdnReaders.Count; - bool printSpacing = false; - - for (int i = 0; i < entryCount; i++) + try { - int loc = reverse ? entryCount - i - 1 : i; + AsnReader x500NameReader = new AsnReader(encodedName, AsnEncodingRules.DER); + AsnReader x500NameSequenceReader = x500NameReader.ReadSequence(); + var rdnReaders = new List(); - // RelativeDistinguishedName ::= - // SET SIZE (1..MAX) OF AttributeTypeAndValue - // - // AttributeTypeAndValue::= SEQUENCE { - // type AttributeType, - // value AttributeValue } - // - // AttributeType::= OBJECT IDENTIFIER - // - // AttributeValue ::= ANY-- DEFINED BY AttributeType + x500NameReader.ThrowIfNotEmpty(); - if (printSpacing) + while (x500NameSequenceReader.HasData) { - decodedName.Append(dnSeparator); - } - else - { - printSpacing = true; + // To match Windows' behavior, permit multi-value RDN SETs to not + // be DER sorted. + rdnReaders.Add(x500NameSequenceReader.ReadSetOf(skipSortOrderValidation: true)); } - AsnReader rdnReader = rdnReaders[loc]; - bool hadValue = false; - - while (rdnReader.HasData) + // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual + // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting + // too much space in the average case. + // + // So, let's look at an example of what our output might be. + // + // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs): + // businessCategory=Private Organization + // 1.3.6.1.4.1.311.60.2.1.3=US + // 1.3.6.1.4.1.311.60.2.1.2=Delaware + // serialNumber=5157550 + // street=548 4th Street + // postalCode=94107 + // C=US + // ST=California + // L=San Francisco + // O=GitHub, Inc. + // CN=github.com + // + // Which comes out to 228 characters using OpenSSL's default pretty-print + // (openssl x509 -in github.cer -text -noout) + // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values + // and round that up to the next programmer number, and you get that 512 should avoid reallocations + // in all but the most dire of cases. + StringBuilder decodedName = new StringBuilder(512); + int entryCount = rdnReaders.Count; + bool printSpacing = false; + + for (int i = 0; i < entryCount; i++) { - AsnReader tavReader = rdnReader.ReadSequence(); - string oid = tavReader.ReadObjectIdentifierAsString(); - string attributeValue = tavReader.ReadAnyAsnString(); - - tavReader.ThrowIfNotEmpty(); - - if (hadValue) + int loc = reverse ? entryCount - i - 1 : i; + + // RelativeDistinguishedName ::= + // SET SIZE (1..MAX) OF AttributeTypeAndValue + // + // AttributeTypeAndValue::= SEQUENCE { + // type AttributeType, + // value AttributeValue } + // + // AttributeType::= OBJECT IDENTIFIER + // + // AttributeValue ::= ANY-- DEFINED BY AttributeType + + if (printSpacing) { - decodedName.Append(multiValueSeparator); + decodedName.Append(dnSeparator); } else { - hadValue = true; - } - - if (printOid) - { - AppendOid(decodedName, oid); + printSpacing = true; } + AsnReader rdnReader = rdnReaders[loc]; + bool hadValue = false; - bool quote = quoteIfNeeded && NeedsQuoting(attributeValue); - - if (quote) + while (rdnReader.HasData) { - decodedName.Append('"'); - - // If the RDN itself had a quote within it, that quote needs to be escaped - // with another quote. - attributeValue = attributeValue.Replace("\"", "\"\""); + AsnReader tavReader = rdnReader.ReadSequence(); + string oid = tavReader.ReadObjectIdentifier(); + string attributeValue = tavReader.ReadAnyAsnString(); + + tavReader.ThrowIfNotEmpty(); + + if (hadValue) + { + decodedName.Append(multiValueSeparator); + } + else + { + hadValue = true; + } + + if (printOid) + { + AppendOid(decodedName, oid); + } + + bool quote = quoteIfNeeded && NeedsQuoting(attributeValue); + + if (quote) + { + decodedName.Append('"'); + + // If the RDN itself had a quote within it, that quote needs to be escaped + // with another quote. + attributeValue = attributeValue.Replace("\"", "\"\""); + } + + decodedName.Append(attributeValue); + + if (quote) + { + decodedName.Append('"'); + } } + } - decodedName.Append(attributeValue); - - if (quote) - { - decodedName.Append('"'); - } + if (addTrailingDelimiter && decodedName.Length > 0) + { + decodedName.Append(dnSeparator); } - } - if (addTrailingDelimiter && decodedName.Length > 0) + return decodedName.ToString(); + } + catch (AsnContentException e) { - decodedName.Append(dnSeparator); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } - - return decodedName.ToString(); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs index ae62596e2d6f98..306e047900d488 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs @@ -5,13 +5,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates; using System.Text; -using Microsoft.Win32.SafeHandles; - namespace Internal.Cryptography.Pal { internal static partial class X500NameEncoder @@ -124,16 +122,17 @@ internal static byte[] X500DistinguishedNameEncode( encodedSets.Reverse(); } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + using (writer.PushSequence()) { - writer.PushSequence(); foreach (byte[] encodedSet in encodedSets) { writer.WriteEncodedValue(encodedSet); } - writer.PopSequence(); - return writer.Encode(); } + + return writer.Encode(); } private static bool NeedsQuoting(string rdnValue) @@ -518,16 +517,16 @@ private static byte[] ParseRdn(Oid tagOid, ReadOnlySpan chars, bool hadEsc chars = ExtractValue(chars); } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSetOf(); - writer.PushSequence(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + using (writer.PushSetOf()) + using (writer.PushSequence()) + { try { - writer.WriteObjectIdentifier(tagOid); + writer.WriteObjectIdentifier(tagOid.Value!); } - catch (CryptographicException e) + catch (ArgumentException e) { throw new CryptographicException(SR.Cryptography_Invalid_X500Name, e); } @@ -544,34 +543,20 @@ private static byte[] ParseRdn(Oid tagOid, ReadOnlySpan chars, bool hadEsc throw new CryptographicException(SR.Cryptography_Invalid_IA5String); } } - else if (IsValidPrintableString(chars)) - { - writer.WriteCharacterString(UniversalTagNumber.PrintableString, chars); - } else { - writer.WriteCharacterString(UniversalTagNumber.UTF8String, chars); + try + { + writer.WriteCharacterString(UniversalTagNumber.PrintableString, chars); + } + catch (EncoderFallbackException) + { + writer.WriteCharacterString(UniversalTagNumber.UTF8String, chars); + } } - - writer.PopSequence(); - writer.PopSetOf(); - return writer.Encode(); } - } - private static bool IsValidPrintableString(ReadOnlySpan value) - { - try - { - Encoding encoding = AsnCharacterStringEncodings.GetEncoding(UniversalTagNumber.PrintableString); - // Throws on invalid characters. - encoding.GetByteCount(value); - return true; - } - catch (EncoderFallbackException) - { - return false; - } + return writer.Encode(); } private static char[] ExtractValue(ReadOnlySpan chars) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index d1de01a95e8c81..6507510a915f16 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -653,6 +653,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/AccessDescriptionAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/AccessDescriptionAsn.xml.cs index f6ddd18e5bdd5e..59458ec5b01066 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/AccessDescriptionAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/AccessDescriptionAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -25,7 +24,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(AccessMethod); + try + { + writer.WriteObjectIdentifier(AccessMethod); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } AccessLocation.Encode(writer); writer.PopSequence(tag); } @@ -37,11 +43,18 @@ internal static AccessDescriptionAsn Decode(ReadOnlyMemory encoded, AsnEnc internal static AccessDescriptionAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out AccessDescriptionAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out AccessDescriptionAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out AccessDescriptionAsn decoded) @@ -50,11 +63,23 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out AccessDescriptionAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out AccessDescriptionAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); - decoded.AccessMethod = sequenceReader.ReadObjectIdentifierAsString(); + decoded.AccessMethod = sequenceReader.ReadObjectIdentifier(); System.Security.Cryptography.Asn1.GeneralNameAsn.Decode(ref sequenceReader, rebind, out decoded.AccessLocation); sequenceReader.ThrowIfNotEmpty(); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/BasicConstraintsAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/BasicConstraintsAsn.xml.cs index 400b063f8c7398..0eefd38fe9644b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/BasicConstraintsAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/BasicConstraintsAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -42,15 +41,12 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for CA. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - tmp.WriteBoolean(CA); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteBoolean(CA); - if (!encoded.SequenceEqual(DefaultCA)) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals(DefaultCA)) + { + tmp.CopyTo(writer); } } @@ -70,11 +66,18 @@ internal static BasicConstraintsAsn Decode(ReadOnlyMemory encoded, AsnEnco internal static BasicConstraintsAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out BasicConstraintsAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out BasicConstraintsAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out BasicConstraintsAsn decoded) @@ -83,6 +86,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out BasicConstraintsAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out BasicConstraintsAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateAsn.xml.cs index a045b54a83c2a3..42d2690931e1ec 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -28,7 +27,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) TbsCertificate.Encode(writer); SignatureAlgorithm.Encode(writer); - writer.WriteBitString(SignatureValue.Span); + writer.WriteBitString(SignatureValue.Span, 0); writer.PopSequence(tag); } @@ -39,11 +38,18 @@ internal static CertificateAsn Decode(ReadOnlyMemory encoded, AsnEncodingR internal static CertificateAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CertificateAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CertificateAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificateAsn decoded) @@ -52,6 +58,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificateAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificateAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -62,7 +80,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.X509Certificates.Asn1.TbsCertificateAsn.Decode(ref sequenceReader, rebind, out decoded.TbsCertificate); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.SignatureAlgorithm); - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan)) { decoded.SignatureValue = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificatePolicyMappingAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificatePolicyMappingAsn.xml.cs index e21a9cd6f878e8..2cc8c662310c71 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificatePolicyMappingAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificatePolicyMappingAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -25,8 +24,22 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(IssuerDomainPolicy); - writer.WriteObjectIdentifier(SubjectDomainPolicy); + try + { + writer.WriteObjectIdentifier(IssuerDomainPolicy); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + try + { + writer.WriteObjectIdentifier(SubjectDomainPolicy); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.PopSequence(tag); } @@ -37,11 +50,18 @@ internal static CertificatePolicyMappingAsn Decode(ReadOnlyMemory encoded, internal static CertificatePolicyMappingAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CertificatePolicyMappingAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CertificatePolicyMappingAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificatePolicyMappingAsn decoded) @@ -50,12 +70,24 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificatePolicyMappingAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificatePolicyMappingAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); - decoded.IssuerDomainPolicy = sequenceReader.ReadObjectIdentifierAsString(); - decoded.SubjectDomainPolicy = sequenceReader.ReadObjectIdentifierAsString(); + decoded.IssuerDomainPolicy = sequenceReader.ReadObjectIdentifier(); + decoded.SubjectDomainPolicy = sequenceReader.ReadObjectIdentifier(); sequenceReader.ThrowIfNotEmpty(); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateTemplateAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateTemplateAsn.xml.cs index 7325f62893c7d5..165b2a94662e05 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateTemplateAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificateTemplateAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -26,7 +25,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(TemplateID); + try + { + writer.WriteObjectIdentifier(TemplateID); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } writer.WriteInteger(TemplateMajorVersion); if (TemplateMinorVersion.HasValue) @@ -44,11 +50,18 @@ internal static CertificateTemplateAsn Decode(ReadOnlyMemory encoded, AsnE internal static CertificateTemplateAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CertificateTemplateAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CertificateTemplateAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificateTemplateAsn decoded) @@ -57,11 +70,23 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificateTemplateAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificateTemplateAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); - decoded.TemplateID = sequenceReader.ReadObjectIdentifierAsString(); + decoded.TemplateID = sequenceReader.ReadObjectIdentifier(); if (!sequenceReader.TryReadInt32(out decoded.TemplateMajorVersion)) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestAsn.xml.cs index a318835d7ba7d9..5de3c36bc22e47 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -28,7 +27,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) CertificationRequestInfo.Encode(writer); SignatureAlgorithm.Encode(writer); - writer.WriteBitString(SignatureValue.Span); + writer.WriteBitString(SignatureValue.Span, 0); writer.PopSequence(tag); } @@ -39,11 +38,18 @@ internal static CertificationRequestAsn Decode(ReadOnlyMemory encoded, Asn internal static CertificationRequestAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CertificationRequestAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CertificationRequestAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificationRequestAsn decoded) @@ -52,6 +58,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificationRequestAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificationRequestAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -62,7 +80,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read System.Security.Cryptography.X509Certificates.Asn1.CertificationRequestInfoAsn.Decode(ref sequenceReader, rebind, out decoded.CertificationRequestInfo); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref sequenceReader, rebind, out decoded.SignatureAlgorithm); - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan)) { decoded.SignatureValue = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs index 338bed0b0f13a2..a947100aba29c1 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -38,7 +37,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) } } - writer.WriteEncodedValue(Subject.Span); + try + { + writer.WriteEncodedValue(Subject.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } SubjectPublicKeyInfo.Encode(writer); writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, 0)); @@ -58,11 +64,18 @@ internal static CertificationRequestInfoAsn Decode(ReadOnlyMemory encoded, internal static CertificationRequestInfoAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out CertificationRequestInfoAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out CertificationRequestInfoAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out CertificationRequestInfoAsn decoded) @@ -71,6 +84,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificationRequestInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out CertificationRequestInfoAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointAsn.xml.cs index 23af6308a9ca64..64cd14144256e6 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -38,7 +37,7 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (Reasons.HasValue) { - writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), Reasons.Value); + writer.WriteNamedBitList(Reasons.Value, new Asn1Tag(TagClass.ContextSpecific, 1)); } @@ -64,11 +63,18 @@ internal static DistributionPointAsn Decode(ReadOnlyMemory encoded, AsnEnc internal static DistributionPointAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out DistributionPointAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out DistributionPointAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out DistributionPointAsn decoded) @@ -77,6 +83,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out DistributionPointAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out DistributionPointAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointNameAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointNameAsn.xml.cs index 03164cdb84d759..6e075c32519d4c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointNameAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/DistributionPointNameAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -70,7 +69,14 @@ internal void Encode(AsnWriter writer) } } - writer.WriteEncodedValue(NameRelativeToCRLIssuer.Value.Span); + try + { + writer.WriteEncodedValue(NameRelativeToCRLIssuer.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } wroteValue = true; } @@ -82,14 +88,33 @@ internal void Encode(AsnWriter writer) internal static DistributionPointNameAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out DistributionPointNameAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out DistributionPointNameAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out DistributionPointNameAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out DistributionPointNameAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyConstraintsAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyConstraintsAsn.xml.cs index e9e3763d9e7afa..abe53a5ae15c52 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyConstraintsAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyConstraintsAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -28,13 +27,13 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) if (RequireExplicitPolicyDepth.HasValue) { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0), RequireExplicitPolicyDepth.Value); + writer.WriteInteger(RequireExplicitPolicyDepth.Value, new Asn1Tag(TagClass.ContextSpecific, 0)); } if (InhibitMappingDepth.HasValue) { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 1), InhibitMappingDepth.Value); + writer.WriteInteger(InhibitMappingDepth.Value, new Asn1Tag(TagClass.ContextSpecific, 1)); } writer.PopSequence(tag); @@ -47,11 +46,18 @@ internal static PolicyConstraintsAsn Decode(ReadOnlyMemory encoded, AsnEnc internal static PolicyConstraintsAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PolicyConstraintsAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PolicyConstraintsAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PolicyConstraintsAsn decoded) @@ -60,6 +66,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyConstraintsAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyConstraintsAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -68,7 +86,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { - if (sequenceReader.TryReadInt32(new Asn1Tag(TagClass.ContextSpecific, 0), out int tmpRequireExplicitPolicyDepth)) + if (sequenceReader.TryReadInt32(out int tmpRequireExplicitPolicyDepth, new Asn1Tag(TagClass.ContextSpecific, 0))) { decoded.RequireExplicitPolicyDepth = tmpRequireExplicitPolicyDepth; } @@ -83,7 +101,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { - if (sequenceReader.TryReadInt32(new Asn1Tag(TagClass.ContextSpecific, 1), out int tmpInhibitMappingDepth)) + if (sequenceReader.TryReadInt32(out int tmpInhibitMappingDepth, new Asn1Tag(TagClass.ContextSpecific, 1))) { decoded.InhibitMappingDepth = tmpInhibitMappingDepth; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyInformationAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyInformationAsn.xml.cs index 557f920f3ded1d..84fae67ba6899a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyInformationAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/PolicyInformationAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -25,11 +24,25 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); - writer.WriteObjectIdentifier(PolicyIdentifier); + try + { + writer.WriteObjectIdentifier(PolicyIdentifier); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } if (PolicyQualifiers.HasValue) { - writer.WriteEncodedValue(PolicyQualifiers.Value.Span); + try + { + writer.WriteEncodedValue(PolicyQualifiers.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } writer.PopSequence(tag); @@ -42,11 +55,18 @@ internal static PolicyInformationAsn Decode(ReadOnlyMemory encoded, AsnEnc internal static PolicyInformationAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out PolicyInformationAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out PolicyInformationAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out PolicyInformationAsn decoded) @@ -55,6 +75,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyInformationAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out PolicyInformationAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -62,7 +94,7 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read int offset; ReadOnlySpan tmpSpan; - decoded.PolicyIdentifier = sequenceReader.ReadObjectIdentifierAsString(); + decoded.PolicyIdentifier = sequenceReader.ReadObjectIdentifier(); if (sequenceReader.HasData) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs index c9ea25a04a1b3e..e5b2abe9b4a283 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs @@ -5,9 +5,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; using System.Collections.Generic; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -56,17 +55,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) // DEFAULT value handler for Version. { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - tmp.WriteInteger(Version); - ReadOnlySpan encoded = tmp.EncodeAsSpan(); + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + tmp.WriteInteger(Version); - if (!encoded.SequenceEqual(DefaultVersion)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEncodedValue(encoded); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - } + if (!tmp.EncodedValueEquals(DefaultVersion)) + { + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); + tmp.CopyTo(writer); + writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); } } @@ -81,7 +77,14 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) } } - writer.WriteEncodedValue(Issuer.Span); + try + { + writer.WriteEncodedValue(Issuer.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } Validity.Encode(writer); // Validator for tag constraint for Subject { @@ -92,18 +95,25 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) } } - writer.WriteEncodedValue(Subject.Span); + try + { + writer.WriteEncodedValue(Subject.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } SubjectPublicKeyInfo.Encode(writer); if (IssuerUniqueId.HasValue) { - writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 1), IssuerUniqueId.Value.Span); + writer.WriteBitString(IssuerUniqueId.Value.Span, 0, new Asn1Tag(TagClass.ContextSpecific, 1)); } if (SubjectUniqueId.HasValue) { - writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 2), SubjectUniqueId.Value.Span); + writer.WriteBitString(SubjectUniqueId.Value.Span, 0, new Asn1Tag(TagClass.ContextSpecific, 2)); } @@ -131,11 +141,18 @@ internal static TbsCertificateAsn Decode(ReadOnlyMemory encoded, AsnEncodi internal static TbsCertificateAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out TbsCertificateAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out TbsCertificateAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out TbsCertificateAsn decoded) @@ -144,6 +161,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out TbsCertificateAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out TbsCertificateAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); @@ -200,13 +229,13 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { - if (sequenceReader.TryReadPrimitiveBitStringValue(new Asn1Tag(TagClass.ContextSpecific, 1), out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 1))) { decoded.IssuerUniqueId = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } else { - decoded.IssuerUniqueId = sequenceReader.ReadBitString(new Asn1Tag(TagClass.ContextSpecific, 1), out _); + decoded.IssuerUniqueId = sequenceReader.ReadBitString(out _, new Asn1Tag(TagClass.ContextSpecific, 1)); } } @@ -215,13 +244,13 @@ internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, Read if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) { - if (sequenceReader.TryReadPrimitiveBitStringValue(new Asn1Tag(TagClass.ContextSpecific, 2), out _, out tmpSpan)) + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan, new Asn1Tag(TagClass.ContextSpecific, 2))) { decoded.SubjectUniqueId = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } else { - decoded.SubjectUniqueId = sequenceReader.ReadBitString(new Asn1Tag(TagClass.ContextSpecific, 2), out _); + decoded.SubjectUniqueId = sequenceReader.ReadBitString(out _, new Asn1Tag(TagClass.ContextSpecific, 2)); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TimeAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TimeAsn.xml.cs index 4b2f3ace204d27..7063eaacb8c962 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TimeAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/TimeAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -65,14 +64,33 @@ internal void Encode(AsnWriter writer) internal static TimeAsn Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, encoded, out TimeAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, encoded, out TimeAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out TimeAsn decoded) + { + try + { + DecodeCore(ref reader, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, ReadOnlyMemory rebind, out TimeAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); @@ -83,7 +101,13 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } else if (tag.HasSameClassAndValue(Asn1Tag.GeneralizedTime)) { - decoded.GeneralTime = reader.ReadGeneralizedTime(disallowFractions: true); + decoded.GeneralTime = reader.ReadGeneralizedTime(); + + if (decoded.GeneralTime!.Value.Ticks % TimeSpan.TicksPerSecond != 0) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + } else { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/ValidityAsn.xml.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/ValidityAsn.xml.cs index 1a1af27eee54be..5cc3d55568e673 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/ValidityAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Asn1/ValidityAsn.xml.cs @@ -4,9 +4,8 @@ #pragma warning disable SA1028 // ignore whitespace warnings for generated code using System; +using System.Formats.Asn1; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; namespace System.Security.Cryptography.X509Certificates.Asn1 { @@ -37,11 +36,18 @@ internal static ValidityAsn Decode(ReadOnlyMemory encoded, AsnEncodingRule internal static ValidityAsn Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) { - AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); + try + { + AsnValueReader reader = new AsnValueReader(encoded.Span, ruleSet); - Decode(ref reader, expectedTag, encoded, out ValidityAsn decoded); - reader.ThrowIfNotEmpty(); - return decoded; + DecodeCore(ref reader, expectedTag, encoded, out ValidityAsn decoded); + reader.ThrowIfNotEmpty(); + return decoded; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } } internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebind, out ValidityAsn decoded) @@ -50,6 +56,18 @@ internal static void Decode(ref AsnValueReader reader, ReadOnlyMemory rebi } internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out ValidityAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, rebind, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory rebind, out ValidityAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs index 168cd105214335..27fbe6a6f1cb34 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates.Asn1; using Internal.Cryptography; @@ -549,7 +550,7 @@ public X509Certificate2 Create( { Algorithm = new AlgorithmIdentifierAsn { - Algorithm = PublicKey.Oid, + Algorithm = PublicKey.Oid!.Value!, Parameters = PublicKey.EncodedParameters.RawData, }, SubjectPublicKey = PublicKey.EncodedKeyValue.RawData, @@ -586,22 +587,21 @@ public X509Certificate2 Create( tbsCertificate.Extensions = extensionAsns.ToArray(); } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - using (AsnWriter signedWriter = new AsnWriter(AsnEncodingRules.DER)) - { - tbsCertificate.Encode(writer); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + tbsCertificate.Encode(writer); - byte[] encodedTbsCertificate = writer.Encode(); - CertificateAsn certificate = new CertificateAsn - { - TbsCertificate = tbsCertificate, - SignatureAlgorithm = signatureAlgorithmAsn, - SignatureValue = generator.SignData(encodedTbsCertificate, HashAlgorithm), - }; + byte[] encodedTbsCertificate = writer.Encode(); + writer.Reset(); - certificate.Encode(signedWriter); - return new X509Certificate2(signedWriter.Encode()); - } + CertificateAsn certificate = new CertificateAsn + { + TbsCertificate = tbsCertificate, + SignatureAlgorithm = signatureAlgorithmAsn, + SignatureValue = generator.SignData(encodedTbsCertificate, HashAlgorithm), + }; + + certificate.Encode(writer); + return new X509Certificate2(writer.Encode()); } private ReadOnlyMemory NormalizeSerialNumber(byte[] serialNumber) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/ECDsaX509SignatureGenerator.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/ECDsaX509SignatureGenerator.cs index b5f7ee0347248d..a212624fc02d2c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/ECDsaX509SignatureGenerator.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/ECDsaX509SignatureGenerator.cs @@ -3,8 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Numerics; -using System.Security.Cryptography.Asn1; +using System.Formats.Asn1; using Internal.Cryptography; namespace System.Security.Cryptography.X509Certificates @@ -44,13 +43,11 @@ public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlg SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name)); } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); - writer.WriteObjectIdentifier(oid); - writer.PopSequence(); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSequence(); + writer.WriteObjectIdentifier(oid); + writer.PopSequence(); + return writer.Encode(); } public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm) @@ -86,11 +83,9 @@ protected override PublicKey BuildPublicKey() }; } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteObjectIdentifier(curveOid!); - curveOidEncoded = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteObjectIdentifier(curveOid!); + curveOidEncoded = writer.Encode(); Debug.Assert(ecParameters.Q.X!.Length == ecParameters.Q.Y!.Length); byte[] uncompressedPoint = new byte[1 + ecParameters.Q.X.Length + ecParameters.Q.Y.Length]; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs10CertificationRequestInfo.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs10CertificationRequestInfo.cs index 9088a675541f2f..b8c9af4c86620f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs10CertificationRequestInfo.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs10CertificationRequestInfo.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.X509Certificates.Asn1; using Internal.Cryptography; @@ -54,7 +55,7 @@ internal byte[] ToPkcs10Request(X509SignatureGenerator signatureGenerator, HashA } SubjectPublicKeyInfoAsn spki = default; - spki.Algorithm = new AlgorithmIdentifierAsn { Algorithm = PublicKey.Oid, Parameters = PublicKey.EncodedParameters.RawData }; + spki.Algorithm = new AlgorithmIdentifierAsn { Algorithm = PublicKey.Oid!.Value!, Parameters = PublicKey.EncodedParameters.RawData }; spki.SubjectPublicKey = PublicKey.EncodedKeyValue.RawData; var attributes = new AttributeAsn[Attributes.Count]; @@ -71,22 +72,20 @@ internal byte[] ToPkcs10Request(X509SignatureGenerator signatureGenerator, HashA Attributes = attributes }; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - using (AsnWriter signedWriter = new AsnWriter(AsnEncodingRules.DER)) - { - requestInfo.Encode(writer); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + requestInfo.Encode(writer); + byte[] encodedRequestInfo = writer.Encode(); + writer.Reset(); - byte[] encodedRequestInfo = writer.Encode(); - CertificationRequestAsn certificationRequest = new CertificationRequestAsn - { - CertificationRequestInfo = requestInfo, - SignatureAlgorithm = signatureAlgorithmAsn, - SignatureValue = signatureGenerator.SignData(encodedRequestInfo, hashAlgorithm), - }; + CertificationRequestAsn certificationRequest = new CertificationRequestAsn + { + CertificationRequestInfo = requestInfo, + SignatureAlgorithm = signatureAlgorithmAsn, + SignatureValue = signatureGenerator.SignData(encodedRequestInfo, hashAlgorithm), + }; - certificationRequest.Encode(signedWriter); - return signedWriter.Encode(); - } + certificationRequest.Encode(writer); + return writer.Encode(); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs9ExtensionRequest.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs9ExtensionRequest.cs index 08ca432a4d597d..e96b9ff0467ebb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs9ExtensionRequest.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/Pkcs9ExtensionRequest.cs @@ -3,9 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.X509Certificates.Asn1; -using Internal.Cryptography; namespace System.Security.Cryptography.X509Certificates { @@ -21,18 +20,17 @@ private static byte[] EncodeAttribute(IEnumerable extensions) if (extensions == null) throw new ArgumentNullException(nameof(extensions)); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + using (writer.PushSequence()) + { foreach (X509Extension e in extensions) { new X509ExtensionAsn(e).Encode(writer); } - - writer.PopSequence(); - return writer.Encode(); } + + return writer.Encode(); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPkcs1X509SignatureGenerator.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPkcs1X509SignatureGenerator.cs index 0fa48fabef184f..cbe6e86e853e81 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPkcs1X509SignatureGenerator.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPkcs1X509SignatureGenerator.cs @@ -3,8 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security.Cryptography.Asn1; -using Internal.Cryptography; +using System.Formats.Asn1; namespace System.Security.Cryptography.X509Certificates { @@ -69,14 +68,12 @@ public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlg SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name)); } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); - writer.WriteObjectIdentifier(oid); - writer.WriteNull(); - writer.PopSequence(); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSequence(); + writer.WriteObjectIdentifier(oid); + writer.WriteNull(); + writer.PopSequence(); + return writer.Encode(); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPssX509SignatureGenerator.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPssX509SignatureGenerator.cs index 7a0188532b7a03..f3b7b2fb8b1c22 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPssX509SignatureGenerator.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/RSAPssX509SignatureGenerator.cs @@ -3,9 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Formats.Asn1; using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.X509Certificates.Asn1; -using Internal.Cryptography; namespace System.Security.Cryptography.X509Certificates { @@ -74,34 +73,33 @@ public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlg PssParamsAsn parameters = new PssParamsAsn { - HashAlgorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(digestOid) }, - MaskGenAlgorithm = new AlgorithmIdentifierAsn { Algorithm = new Oid(Oids.Mgf1) }, + HashAlgorithm = new AlgorithmIdentifierAsn { Algorithm = digestOid }, + MaskGenAlgorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Mgf1 }, SaltLength = cbSalt, TrailerField = 1, }; - using (AsnWriter mgfParamWriter = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + using (writer.PushSequence()) { - mgfParamWriter.PushSequence(); - mgfParamWriter.WriteObjectIdentifier(digestOid); - mgfParamWriter.PopSequence(); - parameters.MaskGenAlgorithm.Parameters = mgfParamWriter.Encode(); + writer.WriteObjectIdentifierForCrypto(digestOid); } - using (AsnWriter parametersWriter = new AsnWriter(AsnEncodingRules.DER)) - using (AsnWriter identifierWriter = new AsnWriter(AsnEncodingRules.DER)) - { - parameters.Encode(parametersWriter); + parameters.MaskGenAlgorithm.Parameters = writer.Encode(); + writer.Reset(); - AlgorithmIdentifierAsn identifier = new AlgorithmIdentifierAsn - { - Algorithm = new Oid(Oids.RsaPss), - Parameters = parametersWriter.Encode(), - }; + parameters.Encode(writer); - identifier.Encode(identifierWriter); - return identifierWriter.Encode(); - } + AlgorithmIdentifierAsn identifier = new AlgorithmIdentifierAsn + { + Algorithm = Oids.RsaPss, + Parameters = writer.Encode(), + }; + + writer.Reset(); + identifier.Encode(writer); + return writer.Encode(); } public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/SubjectAlternativeNameBuilder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/SubjectAlternativeNameBuilder.cs index 8796d3003e618c..73c2a33cd1b758 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/SubjectAlternativeNameBuilder.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/SubjectAlternativeNameBuilder.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Formats.Asn1; using System.Globalization; using System.Net; using System.Security.Cryptography.Asn1; using System.Text; -using Internal.Cryptography; namespace System.Security.Cryptography.X509Certificates { @@ -55,12 +55,9 @@ public void AddUserPrincipalName(string upn) if (string.IsNullOrEmpty(upn)) throw new ArgumentOutOfRangeException(nameof(upn), SR.Arg_EmptyOrNullString); - byte[] otherNameValue; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteCharacterString(UniversalTagNumber.UTF8String, upn); - otherNameValue = writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteCharacterString(UniversalTagNumber.UTF8String, upn); + byte[] otherNameValue = writer.Encode(); OtherNameAsn otherName = new OtherNameAsn { @@ -73,20 +70,20 @@ public void AddUserPrincipalName(string upn) public X509Extension Build(bool critical = false) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + using (writer.PushSequence()) { - writer.PushSequence(); foreach (byte[] encodedName in _encodedNames) { writer.WriteEncodedValue(encodedName); } - writer.PopSequence(); - - return new X509Extension( - Oids.SubjectAltName, - writer.Encode(), - critical); } + + return new X509Extension( + Oids.SubjectAltName, + writer.Encode(), + critical); } private void AddGeneralName(GeneralNameAsn generalName) @@ -94,11 +91,9 @@ private void AddGeneralName(GeneralNameAsn generalName) try { // Verify that the general name can be serialized and store it. - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - generalName.Encode(writer); - _encodedNames.Add(writer.Encode()); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + generalName.Encode(writer); + _encodedNames.Add(writer.Encode()); } catch (EncoderFallbackException) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/CertificateAuthority.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/CertificateAuthority.cs index 5766ce18a5fbe0..1acc0bbe76dbbb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/CertificateAuthority.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/CertificateAuthority.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Formats.Asn1; using System.Linq; -using System.Security.Cryptography.Asn1; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests @@ -277,154 +277,135 @@ internal byte[] GetCrl() DateTimeOffset newExpiry = now.AddSeconds(2); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); - { - writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); - writer.WriteNull(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); - writer.PopSequence(); - } + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + } - byte[] signatureAlgId = writer.Encode(); - writer.Reset(); + byte[] signatureAlgId = writer.Encode(); + writer.Reset(); - // TBSCertList - writer.PushSequence(); - { - // version v2(1) - writer.WriteInteger(1); + // TBSCertList + using (writer.PushSequence()) + { + // version v2(1) + writer.WriteInteger(1); - // signature (AlgorithmIdentifier) - writer.WriteEncodedValue(signatureAlgId); + // signature (AlgorithmIdentifier) + writer.WriteEncodedValue(signatureAlgId); - // issuer - if (CorruptRevocationIssuerName) - { - writer.WriteEncodedValue(s_nonParticipatingName.RawData); - } - else - { - writer.WriteEncodedValue(_cert.SubjectName.RawData); - } + // issuer + if (CorruptRevocationIssuerName) + { + writer.WriteEncodedValue(s_nonParticipatingName.RawData); + } + else + { + writer.WriteEncodedValue(_cert.SubjectName.RawData); + } - if (RevocationExpiration.HasValue) - { - // thisUpdate - writer.WriteUtcTime(_cert.NotBefore); + if (RevocationExpiration.HasValue) + { + // thisUpdate + writer.WriteUtcTime(_cert.NotBefore); - // nextUpdate - writer.WriteUtcTime(RevocationExpiration.Value); - } - else - { - // thisUpdate - writer.WriteUtcTime(now); + // nextUpdate + writer.WriteUtcTime(RevocationExpiration.Value); + } + else + { + // thisUpdate + writer.WriteUtcTime(now); - // nextUpdate - writer.WriteUtcTime(newExpiry); - } + // nextUpdate + writer.WriteUtcTime(newExpiry); + } - // revokedCertificates (don't write down if empty) - if (_revocationList?.Count > 0) + // revokedCertificates (don't write down if empty) + if (_revocationList?.Count > 0) + { + // SEQUENCE OF + using (writer.PushSequence()) { - // SEQUENCE OF - writer.PushSequence(); + foreach ((byte[] serial, DateTimeOffset when) in _revocationList) { - foreach ((byte[] serial, DateTimeOffset when) in _revocationList) + // Anonymous CRL Entry type + using (writer.PushSequence()) { - // Anonymous CRL Entry type - writer.PushSequence(); - { - writer.WriteInteger(serial); - writer.WriteUtcTime(when); - - writer.PopSequence(); - } + writer.WriteInteger(serial); + writer.WriteUtcTime(when); } - - writer.PopSequence(); } } + } - // extensions [0] EXPLICIT Extensions - writer.PushSequence(s_context0); + // extensions [0] EXPLICIT Extensions + using (writer.PushSequence(s_context0)) + { + // Extensions (SEQUENCE OF) + using (writer.PushSequence()) { - // Extensions (SEQUENCE OF) - writer.PushSequence(); + if (_akidExtension == null) { - if (_akidExtension == null) - { - _akidExtension = CreateAkidExtension(); - } - - // Authority Key Identifier Extension - writer.PushSequence(); - { - writer.WriteObjectIdentifier(_akidExtension.Oid.Value); + _akidExtension = CreateAkidExtension(); + } - if (_akidExtension.Critical) - { - writer.WriteBoolean(true); - } + // Authority Key Identifier Extension + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier(_akidExtension.Oid.Value); - writer.WriteOctetString(_akidExtension.RawData); - writer.PopSequence(); + if (_akidExtension.Critical) + { + writer.WriteBoolean(true); } - // CRL Number Extension - writer.PushSequence(); - { - writer.WriteObjectIdentifier("2.5.29.20"); + writer.WriteOctetString(_akidExtension.RawData); + } - using (AsnWriter nested = new AsnWriter(AsnEncodingRules.DER)) - { - nested.WriteInteger(_crlNumber); - writer.WriteOctetString(nested.Encode()); - } + // CRL Number Extension + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.20"); - writer.PopSequence(); + using (writer.PushOctetString()) + { + writer.WriteInteger(_crlNumber); } - - writer.PopSequence(); } - - writer.PopSequence(s_context0); } - - writer.PopSequence(); } + } - byte[] tbsCertList = writer.Encode(); - writer.Reset(); - - byte[] signature; + byte[] tbsCertList = writer.Encode(); + writer.Reset(); - using (RSA key = _cert.GetRSAPrivateKey()) - { - signature = - key.SignData(tbsCertList, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + byte[] signature; - if (CorruptRevocationSignature) - { - signature[5] ^= 0xFF; - } - } + using (RSA key = _cert.GetRSAPrivateKey()) + { + signature = + key.SignData(tbsCertList, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - // CertificateList - writer.PushSequence(); + if (CorruptRevocationSignature) { - writer.WriteEncodedValue(tbsCertList); - writer.WriteEncodedValue(signatureAlgId); - writer.WriteBitString(signature); - - writer.PopSequence(); + signature[5] ^= 0xFF; } + } - _crl = writer.Encode(); + // CertificateList + using (writer.PushSequence()) + { + writer.WriteEncodedValue(tbsCertList); + writer.WriteEncodedValue(signatureAlgId); + writer.WriteBitString(signature); } + _crl = writer.Encode(); + _crlExpiry = newExpiry; _crlNumber++; return _crl; @@ -445,10 +426,9 @@ internal byte[] BuildOcspResponse( CertStatus status = CheckRevocation(certId, ref revokedTime); X509Certificate2 responder = (_ocspResponder ?? _cert); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - /* - + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + /* ResponseData ::= SEQUENCE { version [0] EXPLICIT Version DEFAULT v1, responderID ResponderID, @@ -456,179 +436,152 @@ internal byte[] BuildOcspResponse( responses SEQUENCE OF SingleResponse, responseExtensions [1] EXPLICIT Extensions OPTIONAL } */ - writer.PushSequence(); + using (writer.PushSequence()) + { + // Skip version (v1) + + /* +ResponderID ::= CHOICE { + byName [1] Name, + byKey [2] KeyHash } + */ + + using (writer.PushSequence(s_context1)) { - // Skip version (v1) + if (CorruptRevocationIssuerName) + { + writer.WriteEncodedValue(s_nonParticipatingName.RawData); + } + else + { + writer.WriteEncodedValue(responder.SubjectName.RawData); + } + } + + writer.WriteGeneralizedTime(now); + using (writer.PushSequence()) + { /* - ResponderID ::= CHOICE { - byName [1] Name, - byKey [2] KeyHash } +SingleResponse ::= SEQUENCE { + certID CertID, + certStatus CertStatus, + thisUpdate GeneralizedTime, + nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + singleExtensions [1] EXPLICIT Extensions OPTIONAL } */ - - writer.PushSequence(s_context1); + using (writer.PushSequence()) { - if (CorruptRevocationIssuerName) + writer.WriteEncodedValue(certId.Span); + + if (status == CertStatus.OK) { - writer.WriteEncodedValue(s_nonParticipatingName.RawData); + writer.WriteNull(s_context0); + } + else if (status == CertStatus.Revoked) + { + writer.PushSequence(s_context1); + writer.WriteGeneralizedTime(revokedTime); + writer.PopSequence(s_context1); } else { - writer.WriteEncodedValue(responder.SubjectName.RawData); + Assert.Equal(CertStatus.Unknown, status); + writer.WriteNull(s_context2); } - writer.PopSequence(s_context1); - } - - writer.WriteGeneralizedTime(now); - - writer.PushSequence(); - { - /* - SingleResponse ::= SEQUENCE { - certID CertID, - certStatus CertStatus, - thisUpdate GeneralizedTime, - nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, - singleExtensions [1] EXPLICIT Extensions OPTIONAL } - */ - writer.PushSequence(); + if (RevocationExpiration.HasValue) { - writer.WriteEncodedValue(certId.Span); + writer.WriteGeneralizedTime( + _cert.NotBefore, + omitFractionalSeconds: true); - if (status == CertStatus.OK) - { - writer.WriteNull(s_context0); - } - else if (status == CertStatus.Revoked) - { - writer.PushSequence(s_context1); - writer.WriteGeneralizedTime(revokedTime); - writer.PopSequence(s_context1); - } - else - { - Assert.Equal(CertStatus.Unknown, status); - writer.WriteNull(s_context2); - } - - if (RevocationExpiration.HasValue) + using (writer.PushSequence(s_context0)) { writer.WriteGeneralizedTime( - _cert.NotBefore, + RevocationExpiration.Value, omitFractionalSeconds: true); - - writer.PushSequence(s_context0); - { - writer.WriteGeneralizedTime( - RevocationExpiration.Value, - omitFractionalSeconds: true); - - writer.PopSequence(s_context0); - } - } - else - { - writer.WriteGeneralizedTime(now, omitFractionalSeconds: true); } - - writer.PopSequence(); } - - writer.PopSequence(); - } - - if (!nonceExtension.IsEmpty) - { - writer.PushSequence(s_context1); + else { - writer.PushSequence(); - writer.WriteEncodedValue(nonceExtension.Span); - writer.PopSequence(); - writer.PopSequence(s_context1); + writer.WriteGeneralizedTime(now, omitFractionalSeconds: true); } } - - writer.PopSequence(); } - byte[] tbsResponseData = writer.Encode(); - writer.Reset(); - - /* - BasicOCSPResponse ::= SEQUENCE { - tbsResponseData ResponseData, - signatureAlgorithm AlgorithmIdentifier, - signature BIT STRING, - certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } - */ - writer.PushSequence(); + if (!nonceExtension.IsEmpty) { - writer.WriteEncodedValue(tbsResponseData); - - writer.PushSequence(); + using (writer.PushSequence(s_context1)) + using (writer.PushSequence()) { - writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); - writer.WriteNull(); - writer.PopSequence(); + writer.WriteEncodedValue(nonceExtension.Span); } + } + } - using (RSA rsa = responder.GetRSAPrivateKey()) - { - byte[] signature = rsa.SignData( - tbsResponseData, - HashAlgorithmName.SHA256, - RSASignaturePadding.Pkcs1); + byte[] tbsResponseData = writer.Encode(); + writer.Reset(); + + /* + BasicOCSPResponse ::= SEQUENCE { + tbsResponseData ResponseData, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING, + certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + using (writer.PushSequence()) + { + writer.WriteEncodedValue(tbsResponseData); - if (CorruptRevocationSignature) - { - signature[5] ^= 0xFF; - } + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + } - writer.WriteBitString(signature); - } + using (RSA rsa = responder.GetRSAPrivateKey()) + { + byte[] signature = rsa.SignData( + tbsResponseData, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); - if (_ocspResponder != null) + if (CorruptRevocationSignature) { - writer.PushSequence(s_context0); - { - writer.PushSequence(); - { - writer.WriteEncodedValue(_ocspResponder.RawData); - writer.PopSequence(); - } - - writer.PopSequence(s_context0); - } + signature[5] ^= 0xFF; } - writer.PopSequence(); + writer.WriteBitString(signature); } - byte[] responseBytes = writer.Encode(); - writer.Reset(); - - writer.PushSequence(); + if (_ocspResponder != null) { - writer.WriteEnumeratedValue(OcspResponseStatus.Successful); - - writer.PushSequence(s_context0); + using (writer.PushSequence(s_context0)) + using (writer.PushSequence()) { - writer.PushSequence(); - { - writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1.1"); - writer.WriteOctetString(responseBytes); - writer.PopSequence(); - } - - writer.PopSequence(s_context0); + writer.WriteEncodedValue(_ocspResponder.RawData); + writer.PopSequence(); } - - writer.PopSequence(); } + } + + byte[] responseBytes = writer.Encode(); + writer.Reset(); + + using (writer.PushSequence()) + { + writer.WriteEnumeratedValue(OcspResponseStatus.Successful); - return writer.Encode(); + using (writer.PushSequence(s_context0)) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1.1"); + writer.WriteOctetString(responseBytes); + } } + + return writer.Encode(); } private CertStatus CheckRevocation(ReadOnlyMemory certId, ref DateTimeOffset revokedTime) @@ -639,7 +592,7 @@ private CertStatus CheckRevocation(ReadOnlyMemory certId, ref DateTimeOffs AsnReader algIdReader = idReader.ReadSequence(); - if (algIdReader.ReadObjectIdentifierAsString() != "1.3.14.3.2.26") + if (algIdReader.ReadObjectIdentifier() != "1.3.14.3.2.26") { return CertStatus.Unknown; } @@ -658,7 +611,7 @@ private CertStatus CheckRevocation(ReadOnlyMemory certId, ref DateTimeOffs } } - if (!idReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory reqDn)) + if (!idReader.TryReadPrimitiveOctetString(out ReadOnlyMemory reqDn)) { idReader.ThrowIfNotEmpty(); } @@ -668,7 +621,7 @@ private CertStatus CheckRevocation(ReadOnlyMemory certId, ref DateTimeOffs return CertStatus.Unknown; } - if (!idReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory reqKeyHash)) + if (!idReader.TryReadPrimitiveOctetString(out ReadOnlyMemory reqKeyHash)) { idReader.ThrowIfNotEmpty(); } @@ -699,69 +652,55 @@ private CertStatus CheckRevocation(ReadOnlyMemory certId, ref DateTimeOffs private static X509Extension CreateAiaExtension(string ocspStem) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + // AuthorityInfoAccessSyntax (SEQUENCE OF) + using (writer.PushSequence()) { - // AuthorityInfoAccessSyntax (SEQUENCE OF) - writer.PushSequence(); + // AccessDescription + using (writer.PushSequence()) { - // AccessDescription - writer.PushSequence(); - { - writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); - - writer.WriteCharacterString( - new Asn1Tag(TagClass.ContextSpecific, 6), - UniversalTagNumber.IA5String, - ocspStem); + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); - writer.PopSequence(); - } - - writer.PopSequence(); + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + ocspStem, + new Asn1Tag(TagClass.ContextSpecific, 6)); } - - return new X509Extension("1.3.6.1.5.5.7.1.1", writer.Encode(), false); } + + return new X509Extension("1.3.6.1.5.5.7.1.1", writer.Encode(), false); } private static X509Extension CreateCdpExtension(string cdp) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + // SEQUENCE OF + using (writer.PushSequence()) { - // SEQUENCE OF - writer.PushSequence(); + // DistributionPoint + using (writer.PushSequence()) { - // DistributionPoint - writer.PushSequence(); + // Because DistributionPointName is a CHOICE type this tag is explicit. + // (ITU-T REC X.680-201508 C.3.2.2(g)(3rd bullet)) + // distributionPoint [0] DistributionPointName + using (writer.PushSequence(s_context0)) { - // Because DistributionPointName is a CHOICE type this tag is explicit. - // (ITU-T REC X.680-201508 C.3.2.2(g)(3rd bullet)) - // distributionPoint [0] DistributionPointName - writer.PushSequence(s_context0); + // [0] DistributionPointName (GeneralNames (SEQUENCE OF)) + using (writer.PushSequence(s_context0)) { - // [0] DistributionPointName (GeneralNames (SEQUENCE OF)) - writer.PushSequence(s_context0); - { - // GeneralName ([6] IA5String) - writer.WriteCharacterString( - new Asn1Tag(TagClass.ContextSpecific, 6), - UniversalTagNumber.IA5String, - cdp); - - writer.PopSequence(s_context0); - } - - writer.PopSequence(s_context0); + // GeneralName ([6] IA5String) + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + cdp, + new Asn1Tag(TagClass.ContextSpecific, 6)); } - - writer.PopSequence(); } - - writer.PopSequence(); } - - return new X509Extension("2.5.29.31", writer.Encode(), false); } + + return new X509Extension("2.5.29.31", writer.Encode(), false); } private X509Extension CreateAkidExtension() @@ -769,53 +708,49 @@ private X509Extension CreateAkidExtension() X509SubjectKeyIdentifierExtension skid = _cert.Extensions.OfType().SingleOrDefault(); - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + // AuthorityKeyIdentifier + using (writer.PushSequence()) { - // AuthorityKeyIdentifier - writer.PushSequence(); + if (skid == null) { - if (skid == null) - { - // authorityCertIssuer [1] GeneralNames (SEQUENCE OF) - writer.PushSequence(s_context1); - { - // directoryName [4] Name - byte[] dn = _cert.SubjectName.RawData; - - if (!s_context4.TryEncode(dn, out int written) || written != 1) - { - throw new InvalidOperationException(); - } - - writer.WriteEncodedValue(dn); - writer.PopSequence(s_context1); - } - - // authorityCertSerialNumber [2] CertificateSerialNumber (INTEGER) - byte[] serial = _cert.GetSerialNumber(); - Array.Reverse(serial); - writer.WriteInteger(s_context2, serial); - } - else + // authorityCertIssuer [1] GeneralNames (SEQUENCE OF) + using (writer.PushSequence(s_context1)) { - // keyIdentifier [0] KeyIdentifier (OCTET STRING) - AsnReader reader = new AsnReader(skid.RawData, AsnEncodingRules.BER); - ReadOnlyMemory contents; + // directoryName [4] Name + byte[] dn = _cert.SubjectName.RawData; - if (!reader.TryReadPrimitiveOctetStringBytes(out contents)) + if (s_context4.Encode(dn) != 1) { throw new InvalidOperationException(); } - reader.ThrowIfNotEmpty(); - writer.WriteOctetString(s_context0, contents.Span); + writer.WriteEncodedValue(dn); } - writer.PopSequence(); + // authorityCertSerialNumber [2] CertificateSerialNumber (INTEGER) + byte[] serial = _cert.GetSerialNumber(); + Array.Reverse(serial); + writer.WriteInteger(serial, s_context2); } + else + { + // keyIdentifier [0] KeyIdentifier (OCTET STRING) + AsnReader reader = new AsnReader(skid.RawData, AsnEncodingRules.BER); + ReadOnlyMemory contents; - return new X509Extension("2.5.29.35", writer.Encode(), false); + if (!reader.TryReadPrimitiveOctetString(out contents)) + { + throw new InvalidOperationException(); + } + + reader.ThrowIfNotEmpty(); + writer.WriteOctetString(contents.Span, s_context0); + } } + + return new X509Extension("2.5.29.35", writer.Encode(), false); } private enum OcspResponseStatus diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/RevocationResponder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/RevocationResponder.cs index cec998680408cc..43de608b0442d8 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/RevocationResponder.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/RevocationResponder.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Formats.Asn1; using System.Net; -using System.Security.Cryptography.Asn1; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -316,7 +316,7 @@ private static void DecodeOcspRequest( ReadOnlyMemory wholeExtension = requestExtensions.PeekEncodedValue(); AsnReader extension = requestExtensions.ReadSequence(); - if (extension.ReadObjectIdentifierAsString() == "1.3.6.1.5.5.7.48.1.2") + if (extension.ReadObjectIdentifier() == "1.3.6.1.5.5.7.48.1.2") { nonceExtension = wholeExtension; } diff --git a/src/libraries/pkg/baseline/packageIndex.json b/src/libraries/pkg/baseline/packageIndex.json index 5a90065e0e6640..65540c64d3fbfe 100644 --- a/src/libraries/pkg/baseline/packageIndex.json +++ b/src/libraries/pkg/baseline/packageIndex.json @@ -2871,6 +2871,12 @@ "monoandroid10": "Any" } }, + "System.Formats.Asn1": { + "InboxOn": {}, + "AssemblyVersionInPackageVersion": { + "5.0.0.0": "5.0.0" + } + }, "System.Globalization": { "StableVersions": [ "4.0.0", diff --git a/src/libraries/pkg/descriptions.json b/src/libraries/pkg/descriptions.json index 87b23a37dad8c7..c099b491c37d2a 100644 --- a/src/libraries/pkg/descriptions.json +++ b/src/libraries/pkg/descriptions.json @@ -940,6 +940,14 @@ "System.Runtime.CompilerServices.ConditionalWeakTable" ] }, + { + "Name": "System.Formats.Asn1", + "Description": "Provides classes that can read and write the ASN.1 BER, CER, and DER data formats.", + "CommonTypes": [ + "System.Formats.Asn1.AsnReader", + "System.Formats.Asn1.AsnWriter" + ] + }, { "Name": "System.Globalization", "Description": "Provides classes that define culture-related information, including language, country/region, calendars in use, format patterns for dates, currency, and numbers, and sort order for strings.",