From 47bd7a1f7ddb32e9e7432b05930be44be377514f Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 8 May 2020 11:34:48 -0700 Subject: [PATCH 1/4] Move current ASN src and tests into new tree with no modifications AsnReader partials are named AsnDecoder to better match the final name for the core logic they contain. --- .../src/Resources/Strings.resx | 93 +++++++++++++++++++ .../Formats/Asn1}/Asn1Tag.Accelerators.cs | 0 .../src/System/Formats/Asn1}/Asn1Tag.cs | 0 .../Asn1}/AsnCharacterStringEncodings.cs | 0 .../Formats/Asn1/AsnDecoder.BitString.cs} | 0 .../Formats/Asn1/AsnDecoder.Boolean.cs} | 0 .../Formats/Asn1/AsnDecoder.Enumerated.cs} | 0 .../Asn1/AsnDecoder.GeneralizedTime.cs} | 0 .../Formats/Asn1/AsnDecoder.Integer.cs} | 0 .../Formats/Asn1/AsnDecoder.NamedBitList.cs} | 0 .../System/Formats/Asn1/AsnDecoder.Null.cs} | 0 .../Formats/Asn1/AsnDecoder.OctetString.cs} | 0 .../System/Formats/Asn1/AsnDecoder.Oid.cs} | 0 .../Formats/Asn1/AsnDecoder.Sequence.cs} | 0 .../System/Formats/Asn1/AsnDecoder.SetOf.cs} | 0 .../System/Formats/Asn1/AsnDecoder.Text.cs} | 0 .../Formats/Asn1/AsnDecoder.UtcTime.cs} | 0 .../src/System/Formats/Asn1/AsnDecoder.cs} | 0 .../System/Formats/Asn1}/AsnEncodingRules.cs | 0 .../Formats/Asn1}/AsnWriter.BitString.cs | 0 .../System/Formats/Asn1}/AsnWriter.Boolean.cs | 0 .../Formats/Asn1}/AsnWriter.Enumerated.cs | 0 .../Asn1}/AsnWriter.GeneralizedTime.cs | 0 .../System/Formats/Asn1}/AsnWriter.Integer.cs | 0 .../Formats/Asn1}/AsnWriter.NamedBitList.cs | 0 .../System/Formats/Asn1}/AsnWriter.Null.cs | 0 .../Formats/Asn1}/AsnWriter.OctetString.cs | 0 .../src/System/Formats/Asn1}/AsnWriter.Oid.cs | 0 .../Formats/Asn1}/AsnWriter.Sequence.cs | 0 .../System/Formats/Asn1}/AsnWriter.SetOf.cs | 0 .../System/Formats/Asn1}/AsnWriter.Text.cs | 0 .../System/Formats/Asn1}/AsnWriter.UtcTime.cs | 0 .../src/System/Formats/Asn1}/AsnWriter.cs | 0 .../Formats/Asn1}/SetOfValueComparer.cs | 0 .../src/System/Formats/Asn1}/TagClass.cs | 0 .../Formats/Asn1}/UniversalTagNumber.cs | 0 .../tests}/Reader/Asn1ReaderTests.cs | 0 .../tests}/Reader/ComprehensiveReadTests.cs | 0 .../tests}/Reader/ParseTag.cs | 0 .../tests}/Reader/PeekTests.cs | 0 .../tests}/Reader/ReadBMPString.cs | 0 .../tests}/Reader/ReadBitString.cs | 0 .../tests}/Reader/ReadBoolean.cs | 0 .../tests}/Reader/ReadEnumerated.cs | 0 .../tests}/Reader/ReadGeneralizedTime.cs | 0 .../tests}/Reader/ReadIA5String.cs | 0 .../tests}/Reader/ReadInteger.cs | 0 .../tests}/Reader/ReadLength.cs | 0 .../tests}/Reader/ReadNamedBitList.cs | 0 .../tests}/Reader/ReadNull.cs | 0 .../tests}/Reader/ReadObjectIdentifier.cs | 0 .../tests}/Reader/ReadOctetString.cs | 0 .../tests}/Reader/ReadSequence.cs | 0 .../tests}/Reader/ReadSetOf.cs | 0 .../tests}/Reader/ReadT61String.cs | 0 .../tests}/Reader/ReadUTF8String.cs | 0 .../tests}/Reader/ReadUtcTime.cs | 0 .../tests}/Reader/ReaderStateTests.cs | 0 .../tests}/Writer/Asn1WriterTests.cs | 0 .../tests}/Writer/ComprehensiveWriteTest.cs | 0 .../tests}/Writer/PushPopSequence.cs | 0 .../tests}/Writer/PushPopSetOf.cs | 0 .../tests}/Writer/WriteBMPString.cs | 0 .../tests}/Writer/WriteBitString.cs | 0 .../tests}/Writer/WriteBoolean.cs | 0 .../tests}/Writer/WriteCharacterString.cs | 0 .../tests}/Writer/WriteEnumerated.cs | 0 .../tests}/Writer/WriteGeneralizedTime.cs | 0 .../tests}/Writer/WriteIA5String.cs | 0 .../tests}/Writer/WriteInteger.cs | 0 .../tests}/Writer/WriteNamedBitList.cs | 0 .../tests}/Writer/WriteNull.cs | 0 .../tests}/Writer/WriteObjectIdentifier.cs | 0 .../tests}/Writer/WriteOctetString.cs | 0 .../tests}/Writer/WriteUtcTime.cs | 0 .../tests}/Writer/WriteUtf8String.cs | 0 76 files changed, 93 insertions(+) create mode 100644 src/libraries/System.Formats.Asn1/src/Resources/Strings.resx rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/Asn1Tag.Accelerators.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/Asn1Tag.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnCharacterStringEncodings.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs} (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnEncodingRules.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.BitString.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Boolean.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Enumerated.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.GeneralizedTime.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Integer.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.NamedBitList.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Null.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.OctetString.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Oid.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Sequence.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.SetOf.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Text.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.UtcTime.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/SetOfValueComparer.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/TagClass.cs (100%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/UniversalTagNumber.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/Asn1ReaderTests.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ComprehensiveReadTests.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ParseTag.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/PeekTests.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadBMPString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadBitString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadBoolean.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadEnumerated.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadGeneralizedTime.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadIA5String.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadInteger.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadLength.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadNamedBitList.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadNull.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadObjectIdentifier.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadOctetString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadSequence.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadSetOf.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadT61String.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadUTF8String.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadUtcTime.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReaderStateTests.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/Asn1WriterTests.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/ComprehensiveWriteTest.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/PushPopSequence.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/PushPopSetOf.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteBMPString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteBitString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteBoolean.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteCharacterString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteEnumerated.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteGeneralizedTime.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteIA5String.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteInteger.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteNamedBitList.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteNull.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteObjectIdentifier.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteOctetString.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteUtcTime.cs (100%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteUtf8String.cs (100%) 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..5a31f717c81530 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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. + + + The OID value was invalid. + + + ASN.1 Enumerated values only apply to enum types without the [Flags] attribute. + + + Named bit list operations require an enum with the [Flags] attribute. + + + The encoded named bit list value is larger than the value size of the '{0}' enum. + + + Tags with TagClass Universal must have the appropriate TagValue value for the data type being read or written. + + + Unused bit count must be between 0 and 7, inclusive. + + + Encode cannot be called while a Sequence or SetOf is still open. + + + Cannot pop the requested tag as it is not currently in progress. + + + ASN1 corrupted data. + + + The input to WriteEncodedValue must represent a single encoded value with no trailing data. + + 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 100% 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 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 100% 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 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs 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 100% 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 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs 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 100% 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 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 100% 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 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs 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 100% 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 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs similarity index 100% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs 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 100% 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 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 100% 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 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 100% 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 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 100% 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 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 100% 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 diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/Asn1ReaderTests.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/Asn1ReaderTests.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs b/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs similarity index 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs 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 100% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs From 43b965920b33ac318a29fbe6ca14bc182845c0f4 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Sat, 9 May 2020 11:27:40 -0700 Subject: [PATCH 2/4] Make System.Formats.Asn1 package --- .../System.Formats.Asn1/Directory.Build.props | 6 +++ .../System.Formats.Asn1.sln | 53 ++++++++++++++++++ .../pkg/System.Formats.Asn1.pkgproj | 9 ++++ .../ref/System.Formats.Asn1.cs | 7 +++ .../ref/System.Formats.Asn1.csproj | 12 +++++ .../src/System.Formats.Asn1.csproj | 54 +++++++++++++++++++ .../tests/System.Formats.Asn1.Tests.csproj | 51 ++++++++++++++++++ src/libraries/pkg/baseline/packageIndex.json | 6 +++ src/libraries/pkg/descriptions.json | 8 +++ 9 files changed, 206 insertions(+) create mode 100644 src/libraries/System.Formats.Asn1/Directory.Build.props create mode 100644 src/libraries/System.Formats.Asn1/System.Formats.Asn1.sln create mode 100644 src/libraries/System.Formats.Asn1/pkg/System.Formats.Asn1.pkgproj create mode 100644 src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs create mode 100644 src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj create mode 100644 src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj create mode 100644 src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj 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..ba1f965d83cae7 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/Directory.Build.props @@ -0,0 +1,6 @@ + + + + Open + + 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..94ec97db2fac35 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs @@ -0,0 +1,7 @@ +// 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. +// ------------------------------------------------------------------------------ + 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/System.Formats.Asn1.csproj b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj new file mode 100644 index 00000000000000..9e3d2fc25a62c4 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj @@ -0,0 +1,54 @@ + + + true + enable + netstandard2.0 + + + + Common\System\Memory\PointerMemoryManager.cs + + + Common\System\Security\Cryptography\CryptoPool.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..2ce3905bcb494e --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj @@ -0,0 +1,51 @@ + + + true + $(NetCoreAppCurrent);$(NetFrameworkCurrent) + + + + CommonTest\System\Security\Cryptography\ByteUtils.cs + + + 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.", From 6d9e8fbdf443fdce5bf3d82d0df19125df4f094b Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 12 May 2020 10:53:45 -0700 Subject: [PATCH 3/4] Apply changes from API review Some additional tests were written to increase code coverage after the changes --- .../ref/System.Formats.Asn1.cs | 236 +++ .../src/Resources/Strings.resx | 95 +- .../src/System.Formats.Asn1.csproj | 5 +- .../Formats/Asn1/Asn1Tag.Accelerators.cs | 6 +- .../src/System/Formats/Asn1/Asn1Tag.cs | 176 +-- .../Asn1/AsnCharacterStringEncodings.cs | 92 +- .../Formats/Asn1/AsnContentException.cs | 32 + .../Formats/Asn1/AsnDecoder.BitString.cs | 821 +++++----- .../System/Formats/Asn1/AsnDecoder.Boolean.cs | 138 +- .../Formats/Asn1/AsnDecoder.Enumerated.cs | 527 +++---- .../Asn1/AsnDecoder.GeneralizedTime.cs | 179 +-- .../System/Formats/Asn1/AsnDecoder.Integer.cs | 1318 ++++++----------- .../Formats/Asn1/AsnDecoder.NamedBitList.cs | 584 +++++--- .../System/Formats/Asn1/AsnDecoder.Null.cs | 105 +- .../Formats/Asn1/AsnDecoder.OctetString.cs | 761 ++++------ .../src/System/Formats/Asn1/AsnDecoder.Oid.cs | 214 +-- .../Formats/Asn1/AsnDecoder.Sequence.cs | 185 +-- .../System/Formats/Asn1/AsnDecoder.SetOf.cs | 237 +-- .../System/Formats/Asn1/AsnDecoder.Text.cs | 1225 ++++++--------- .../System/Formats/Asn1/AsnDecoder.UtcTime.cs | 196 ++- .../src/System/Formats/Asn1/AsnDecoder.cs | 594 +++++--- .../System/Formats/Asn1/AsnEncodingRules.cs | 4 +- .../System/Formats/Asn1/AsnReaderOptions.cs | 53 + .../Formats/Asn1/AsnWriter.BitString.cs | 230 +-- .../System/Formats/Asn1/AsnWriter.Boolean.cs | 20 +- .../Formats/Asn1/AsnWriter.Enumerated.cs | 110 +- .../Formats/Asn1/AsnWriter.GeneralizedTime.cs | 47 +- .../System/Formats/Asn1/AsnWriter.Integer.cs | 130 +- .../Formats/Asn1/AsnWriter.NamedBitList.cs | 157 +- .../src/System/Formats/Asn1/AsnWriter.Null.cs | 22 +- .../Formats/Asn1/AsnWriter.OctetString.cs | 68 +- .../src/System/Formats/Asn1/AsnWriter.Oid.cs | 149 +- .../System/Formats/Asn1/AsnWriter.Sequence.cs | 66 +- .../System/Formats/Asn1/AsnWriter.SetOf.cs | 74 +- .../src/System/Formats/Asn1/AsnWriter.Text.cs | 182 +-- .../System/Formats/Asn1/AsnWriter.UtcTime.cs | 70 +- .../src/System/Formats/Asn1/AsnWriter.cs | 400 +++-- .../System/Formats/Asn1/SetOfValueComparer.cs | 9 +- .../src/System/Formats/Asn1/TagClass.cs | 4 +- .../System/Formats/Asn1/UniversalTagNumber.cs | 4 +- .../System.Formats.Asn1/tests/Asn1TagTests.cs | 245 +++ .../tests/Reader/Asn1ReaderTests.cs | 26 - .../tests/Reader/ComprehensiveReadTests.cs | 38 +- .../tests/Reader/OverlappedReads.cs | 177 +++ .../tests/Reader/ParseTag.cs | 103 +- .../tests/Reader/PeekTests.cs | 71 +- .../tests/Reader/ReadBMPString.cs | 380 +++-- .../tests/Reader/ReadBitString.cs | 453 +++--- .../tests/Reader/ReadBoolean.cs | 165 ++- .../tests/Reader/ReadEnumerated.cs | 1012 +++++++------ .../tests/Reader/ReadGeneralizedTime.cs | 213 +-- .../tests/Reader/ReadIA5String.cs | 396 ++--- .../tests/Reader/ReadInteger.cs | 502 +++---- .../tests/Reader/ReadLength.cs | 130 +- .../tests/Reader/ReadNamedBitList.cs | 347 ++++- .../tests/Reader/ReadNull.cs | 107 +- .../tests/Reader/ReadObjectIdentifier.cs | 196 ++- .../tests/Reader/ReadOctetString.cs | 353 +++-- .../tests/Reader/ReadSequence.cs | 267 ++-- .../tests/Reader/ReadSetOf.cs | 322 ++-- .../tests/Reader/ReadT61String.cs | 37 +- .../tests/Reader/ReadUTF8String.cs | 387 +++-- .../tests/Reader/ReadUtcTime.cs | 200 ++- .../tests/Reader/ReaderStateTests.cs | 5 +- .../tests/System.Formats.Asn1.Tests.csproj | 10 +- .../tests/Writer/Asn1WriterTests.cs | 13 +- .../tests/Writer/ComprehensiveWriteTest.cs | 522 ++++--- .../tests/Writer/PushPopOctetString.cs | 230 +++ .../tests/Writer/PushPopSequence.cs | 659 ++++----- .../tests/Writer/PushPopSetOf.cs | 663 ++++----- .../tests/Writer/SimpleWriterTests.cs | 143 ++ .../tests/Writer/WriteBMPString.cs | 55 +- .../tests/Writer/WriteBitString.cs | 415 +++--- .../tests/Writer/WriteBoolean.cs | 130 +- .../tests/Writer/WriteCharacterString.cs | 475 +++--- .../tests/Writer/WriteEncodedValue.cs | 102 ++ .../tests/Writer/WriteEnumerated.cs | 637 +++----- .../tests/Writer/WriteGeneralizedTime.cs | 140 +- .../tests/Writer/WriteIA5String.cs | 55 +- .../tests/Writer/WriteInteger.cs | 705 +++++---- .../tests/Writer/WriteNamedBitList.cs | 502 ++++--- .../tests/Writer/WriteNull.cs | 80 +- .../tests/Writer/WriteObjectIdentifier.cs | 504 ++----- .../tests/Writer/WriteOctetString.cs | 178 +-- .../tests/Writer/WriteUtcTime.cs | 276 ++-- .../tests/Writer/WriteUtf8String.cs | 55 +- 86 files changed, 10972 insertions(+), 11234 deletions(-) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnContentException.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs delete mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/Asn1ReaderTests.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/OverlappedReads.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs diff --git a/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs index 94ec97db2fac35..921b832ac44220 100644 --- a/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs +++ b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs @@ -5,3 +5,239 @@ // 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/src/Resources/Strings.resx b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx index 5a31f717c81530..18ac1f416a1f66 100644 --- a/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx +++ b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx @@ -60,34 +60,97 @@ The destination is too small to hold the encoded value. - - The OID value was invalid. - - + 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 encoded named bit list value is larger than the value size of the '{0}' enum. + + 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. - - Encode cannot be called while a Sequence or SetOf is still open. + + 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. - - ASN1 corrupted data. + + A constructed tag used a definite length encoding, which is invalid for CER data. The input may be encoded with BER or DER. - - The input to WriteEncodedValue must represent a single encoded value with no trailing data. + + 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 index 9e3d2fc25a62c4..ddd7a52c3e5f41 100644 --- a/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj +++ b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj @@ -5,15 +5,13 @@ netstandard2.0 - - Common\System\Memory\PointerMemoryManager.cs - Common\System\Security\Cryptography\CryptoPool.cs + @@ -29,6 +27,7 @@ + diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs index f34f6d99e2f332..82c6305f2448de 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs index daefd32116442b..3ecf79dd79ddc8 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs index c8d24878a15066..a9d24cf0de6543 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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 index e777cc12191043..f87776fd54d63d 100644 --- 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 @@ -2,185 +2,194 @@ // 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; +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 BIT STRING with tag UNIVERSAL 3, returning the contents - /// as a over the original data. + /// 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 over the original data - /// corresponding to the value of the BIT STRING. + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Bit String. + /// This parameter is treated as uninitialized. /// - /// - /// 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. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 3). /// /// - /// 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. + /// if the Bit String value has a primitive encoding and all of the bits + /// reported as unused are set to 0; + /// otherwise, . /// - /// - /// 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 bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, + /// + public static bool TryReadPrimitiveBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, out int unusedBitCount, - out ReadOnlySpan value) + out ReadOnlySpan value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - bool isPrimitive = TryReadPrimitiveBitStringValue( - expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out unusedBitCount, - out value, - out byte normalizedLastByte); - - if (isPrimitive) + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + contentsLength: out _, + headerLength: out _, + out int localUbc, + out ReadOnlySpan localValue, + out int consumed, + out byte normalizedLastByte)) { - // 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]) + // 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 = 0; - value = default; - return false; + unusedBitCount = localUbc; + value = localValue; + bytesConsumed = consumed; + return true; } - - // Skip the tag+length (header) and the unused bit count byte (1) and the contents. - _data = _data.Slice(headerLength + value.Length + 1); } - return isPrimitive; + unusedBitCount = 0; + value = default; + bytesConsumed = 0; + return false; } /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. + /// 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. /// - /// - /// 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. + /// + /// 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 . + /// 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). /// /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. + /// if is large enough to receive the + /// value of the Bit String; + /// otherwise, . /// - /// - /// 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 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 + /// the method. + /// + /// -or- + /// + /// overlaps . /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, + /// + /// + public static bool TryReadBitString( + ReadOnlySpan source, Span destination, + AsnEncodingRules ruleSet, out int unusedBitCount, - out int bytesWritten) + out int bytesConsumed, + out int bytesWritten, + Asn1Tag? expectedTag = null) { - if (TryReadPrimitiveBitStringValue( - expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out unusedBitCount, + 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 byte normalizedLastByte)) + out consumed, + out normalizedLastByte)) { if (value.Length > destination.Length) { + bytesConsumed = 0; bytesWritten = 0; unusedBitCount = 0; return false; @@ -189,126 +198,155 @@ public bool TryCopyBitStringBytes( 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); + bytesConsumed = consumed; + unusedBitCount = localUbc; return true; } - Debug.Assert(actualTag.IsConstructed); + // If we get here, the tag was appropriate, but the encoding was constructed. - bool read = TryCopyConstructedBitStringValue( - Slice(_data, headerLength, contentsLength), + if (TryCopyConstructedBitStringValue( + Slice(source, headerLength, contentsLength), + ruleSet, destination, contentsLength == null, - out unusedBitCount, + out localUbc, out int bytesRead, - out bytesWritten); - - if (read) + out int written)) { - _data = _data.Slice(headerLength + bytesRead); + unusedBitCount = localUbc; + bytesConsumed = headerLength + bytesRead; + bytesWritten = written; + return true; } - return read; + bytesWritten = bytesConsumed = unusedBitCount = 0; + return false; } /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. + /// 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. /// - /// - /// 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. + /// + /// 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). /// /// - /// a copy of the value in a newly allocated, precisely sized, array. + /// An array containing the contents of the Bit String 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 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 + /// the method. /// - /// - /// - public byte[] ReadBitString(Asn1Tag expectedTag, out int unusedBitCount) + /// + /// + public static byte[] ReadBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - ReadOnlySpan span; - - if (TryReadPrimitiveBitStringValue(expectedTag, out unusedBitCount, out span)) + 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)) { - return span.ToArray(); + 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; } - span = PeekEncodedValue(); + // If we get here, the tag was appropriate, but the encoding was constructed. // Guaranteed long enough - byte[] rented = CryptoPool.Rent(span.Length); - int dataLength = 0; + int tooBig = contentsLength ?? SeekEndOfContents(source.Slice(headerLength), ruleSet); - try - { - if (!TryCopyBitStringBytes(expectedTag, rented, out unusedBitCount, out dataLength)) - { - Debug.Fail("TryCopyBitStringBytes failed with a pre-allocated buffer"); - throw new CryptographicException(); - } + byte[] rented = CryptoPool.Rent(tooBig); - byte[] alloc = new byte[dataLength]; - rented.AsSpan(0, dataLength).CopyTo(alloc); - return alloc; - } - finally + if (TryCopyConstructedBitStringValue( + Slice(source, headerLength, contentsLength), + ruleSet, + rented, + contentsLength == null, + out localUbc, + out int bytesRead, + out int written)) { - CryptoPool.Return(rented, dataLength); + 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 void ParsePrimitiveBitStringContents( + 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) + if (ruleSet == AsnEncodingRules.CER && source.Length > MaxCERSegmentSize) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); } // T-REC-X.690-201508 sec 8.6.2.3 if (source.Length == 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } unusedBitCount = source[0]; @@ -316,7 +354,7 @@ private void ParsePrimitiveBitStringContents( // T-REC-X.690-201508 sec 8.6.2.2 if (unusedBitCount > 7) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } if (source.Length == 1) @@ -324,7 +362,7 @@ private void ParsePrimitiveBitStringContents( // T-REC-X.690-201508 sec 8.6.2.4 if (unusedBitCount > 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } Debug.Assert(unusedBitCount == 0); @@ -344,9 +382,9 @@ private void ParsePrimitiveBitStringContents( if (maskedByte != lastByte) { // T-REC-X.690-201508 sec 11.2.1 - if (RuleSet == AsnEncodingRules.DER || RuleSet == AsnEncodingRules.CER) + if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } } @@ -374,12 +412,16 @@ private static void CopyBitStringValue( destination[value.Length - 1] = normalizedLastByte; } - private int CountConstructedBitString(ReadOnlySpan source, bool isIndefinite) + private static int CountConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + bool isIndefinite) { Span destination = Span.Empty; return ProcessConstructedBitString( source, + ruleSet, destination, null, isIndefinite, @@ -387,8 +429,9 @@ private int CountConstructedBitString(ReadOnlySpan source, bool isIndefini out _); } - private void CopyConstructedBitString( + private static void CopyConstructedBitString( ReadOnlySpan source, + AsnEncodingRules ruleSet, Span destination, bool isIndefinite, out int unusedBitCount, @@ -399,6 +442,7 @@ private void CopyConstructedBitString( bytesWritten = ProcessConstructedBitString( source, + ruleSet, tmpDest, (value, lastByte, dest) => CopyBitStringValue(value, lastByte, dest), isIndefinite, @@ -406,8 +450,9 @@ private void CopyConstructedBitString( out bytesRead); } - private int ProcessConstructedBitString( + private static int ProcessConstructedBitString( ReadOnlySpan source, + AsnEncodingRules ruleSet, Span destination, BitStringCopyAction? copyAction, bool isIndefinite, @@ -418,8 +463,7 @@ private int ProcessConstructedBitString( bytesRead = 0; int lastSegmentLength = MaxCERSegmentSize; - ReadOnlySpan originalSpan = _data; - AsnValueReader tmpReader = OpenUnchecked(source, RuleSet); + ReadOnlySpan cur = source; Stack<(int Offset, int Length, bool Indefinite, int BytesRead)>? readerStack = null; int totalLength = 0; Asn1Tag tag = Asn1Tag.ConstructedBitString; @@ -427,9 +471,9 @@ private int ProcessConstructedBitString( while (true) { - while (tmpReader.HasData) + while (!cur.IsEmpty) { - tag = tmpReader.ReadTagAndLength(out int? length, out int headerLength); + tag = ReadTagAndLength(cur, ruleSet, out int? length, out int headerLength); if (tag == Asn1Tag.PrimitiveBitString) { @@ -437,26 +481,27 @@ private int ProcessConstructedBitString( { // 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); + throw new AsnContentException(); } - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) { // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); } Debug.Assert(length != null); - ReadOnlySpan encodedValue = Slice(tmpReader._data, headerLength, length.Value); + ReadOnlySpan encodedValue = Slice(cur, headerLength, length.Value); ParsePrimitiveBitStringContents( encodedValue, + ruleSet, out lastUnusedBitCount, out ReadOnlySpan contents, out byte normalizedLastByte); int localLen = headerLength + encodedValue.Length; - tmpReader._data = tmpReader._data.Slice(localLen); + cur = cur.Slice(localLen); bytesRead += localLen; totalLength += contents.Length; @@ -477,8 +522,8 @@ private int ProcessConstructedBitString( 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); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + cur = topSpan.Slice(bytesRead); bytesRead += pushedBytesRead; isIndefinite = wasIndefinite; @@ -491,10 +536,10 @@ private int ProcessConstructedBitString( } else if (tag == Asn1Tag.ConstructedBitString) { - if (RuleSet == AsnEncodingRules.CER) + if (ruleSet == AsnEncodingRules.CER) { // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } if (readerStack == null) @@ -502,36 +547,36 @@ private int ProcessConstructedBitString( readerStack = new Stack<(int, int, bool, int)>(); } - if (!originalSpan.Overlaps(tmpReader._data, out int curOffset)) + if (!source.Overlaps(cur, out int curOffset)) { Debug.Fail("Non-overlapping data encountered..."); - throw new CryptographicException(); + throw new AsnContentException(); } - readerStack.Push((curOffset, tmpReader._data.Length, isIndefinite, bytesRead)); + readerStack.Push((curOffset, cur.Length, isIndefinite, bytesRead)); - tmpReader._data = Slice(tmpReader._data, headerLength, length); + 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 CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } } if (isIndefinite && tag != Asn1Tag.EndOfContents) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } 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); + ReadOnlySpan tmpSpan = source.Slice(topOffset, topLength); + cur = tmpSpan.Slice(bytesRead); isIndefinite = wasIndefinite; bytesRead += pushedBytesRead; @@ -543,8 +588,9 @@ private int ProcessConstructedBitString( } } - private bool TryCopyConstructedBitStringValue( + private static bool TryCopyConstructedBitStringValue( ReadOnlySpan source, + AsnEncodingRules ruleSet, Span dest, bool isIndefinite, out int unusedBitCount, @@ -553,15 +599,15 @@ private bool TryCopyConstructedBitStringValue( { // 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); + 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) + if (ruleSet == AsnEncodingRules.CER && contentLength < MaxCERSegmentSize) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } if (dest.Length < contentLength) @@ -574,6 +620,7 @@ private bool TryCopyConstructedBitStringValue( CopyConstructedBitString( source, + ruleSet, dest, isIndefinite, out unusedBitCount, @@ -584,76 +631,59 @@ private bool TryCopyConstructedBitStringValue( return true; } - private bool TryReadPrimitiveBitStringValue( + private static bool TryReadPrimitiveBitStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, - out Asn1Tag actualTag, out int? contentsLength, out int headerLength, out int unusedBitCount, out ReadOnlySpan value, + out int bytesConsumed, out byte normalizedLastByte) { - actualTag = ReadTagAndLength(out contentsLength, out headerLength); + 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) + if (ruleSet == AsnEncodingRules.DER) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderDer_TryBerOrCer); } unusedBitCount = 0; value = default; normalizedLastByte = 0; + bytesConsumed = 0; return false; } Debug.Assert(contentsLength.HasValue); - ReadOnlySpan encodedValue = Slice(_data, headerLength, contentsLength.Value); ParsePrimitiveBitStringContents( encodedValue, + ruleSet, out unusedBitCount, out value, out normalizedLastByte); + bytesConsumed = headerLength + encodedValue.Length; return true; } } - internal partial class AsnReader + public 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. @@ -662,81 +692,61 @@ public bool TryReadPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlyM /// 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). + /// /// - /// 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. + /// 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 + /// + /// 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 bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, + /// + public bool TryReadPrimitiveBitString( out int unusedBitCount, - out ReadOnlyMemory value) + out ReadOnlyMemory value, + Asn1Tag? expectedTag = null) { - AsnValueReader reader = OpenValueReader(); + bool ret = AsnDecoder.TryReadPrimitiveBitString( + _data.Span, + RuleSet, + out unusedBitCount, + out ReadOnlySpan span, + out int consumed, + expectedTag); - if (reader.TryReadPrimitiveBitStringValue(expectedTag, out unusedBitCount, out ReadOnlySpan span)) + if (ret) { - value = AsnValueReader.Slice(_data, span); - reader.MatchSlice(ref _data); - return true; + value = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + value = default; } - 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); + return ret; } /// /// 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 @@ -745,174 +755,99 @@ public bool TryCopyBitStringBytes( /// /// On success, receives the number of bytes written to . /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// /// - /// true and advances the reader if had sufficient + /// and advances the reader if had sufficient /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 bool TryCopyBitStringBytes( - Asn1Tag expectedTag, + /// + /// + public bool TryReadBitString( Span destination, out int unusedBitCount, - out int bytesWritten) + out int bytesWritten, + Asn1Tag? expectedTag = null) { - AsnValueReader reader = OpenValueReader(); + bool ret = AsnDecoder.TryReadBitString( + _data.Span, + destination, + RuleSet, + out unusedBitCount, + out int consumed, + out bytesWritten, + expectedTag); - if (reader.TryCopyBitStringBytes(expectedTag, destination, out unusedBitCount, out bytesWritten)) + if (ret) { - reader.MatchSlice(ref _data); - return true; + _data = _data.Slice(consumed); } - 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); + return ret; } /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. + /// Reads the next value as a BIT STRING with a specified tag, returning the value + /// in a byte array. /// - /// 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 . + /// + /// The tag to check for before reading, or for the default tag (Universal 1). /// /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - ArraySegment destination, - out int unusedBitCount, - out int bytesWritten) + /// + /// + public byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = null) { - return TryCopyBitStringBytes( - expectedTag, - destination.AsSpan(), + byte[] ret = AsnDecoder.ReadBitString( + _data.Span, + RuleSet, out unusedBitCount, - out bytesWritten); - } + out int consumed, + expectedTag); - /// - /// 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); + _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 index 8743adfbdec90a..dc2b113924ede6 100644 --- 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 @@ -2,122 +2,118 @@ // 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 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a Boolean with tag UNIVERSAL 1. + /// Reads a Boolean value from with a specified tag under + /// the specified encoding rules. /// - /// 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 + /// 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. /// - 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 + /// + /// 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 bool ReadBoolean(Asn1Tag expectedTag) + public static bool ReadBoolean( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - 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); - } + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag ?? Asn1Tag.Boolean, + UniversalTagNumber.Boolean, + out int consumed); - 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) + if (contents.Length != 1) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } - byte val = source[0]; + 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 CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } + bytesConsumed = consumed; return true; } } - internal partial class AsnReader + public 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 + /// + /// 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 + /// the method. /// - public bool ReadBoolean(Asn1Tag expectedTag) + public bool ReadBoolean(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - bool ret = valueReader.ReadBoolean(expectedTag); - valueReader.MatchSlice(ref _data); + 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 index 296e596d1da446..605d44b35153ab 100644 --- 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 @@ -5,188 +5,210 @@ using System.Diagnostics; using System.Runtime.InteropServices; -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 Enumerated value with tag UNIVERSAL 10, - /// returning the contents as a over the original data. + /// 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 bytes of the Enumerated value, in signed big-endian form. + /// The slice of the buffer containing 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 not defined. /// - /// - 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 + /// + /// 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 ReadOnlySpan ReadEnumeratedBytes(Asn1Tag expectedTag) + public static ReadOnlySpan ReadEnumeratedBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - // 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; + return GetIntegerContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.Enumerated, + UniversalTagNumber.Enumerated, + out bytesConsumed); } /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . + /// 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 . + /// 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 defined. /// - /// - /// 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 + /// + /// 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 not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public TEnum ReadEnumeratedValue(Asn1Tag expectedTag) where TEnum : struct + 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(expectedTag, tEnum)); + return (TEnum)Enum.ToObject( + tEnum, + ReadEnumeratedValue( + source, + ruleSet, + tEnum, + out bytesConsumed, + expectedTag)); } /// - /// 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 . + /// Reads an Enumerated from with a specified tag under + /// the specified encoding rules, converting it to the + /// non-[] enum specified by . /// - /// The tag to check for before reading. - /// Type object representing the destination type. + /// 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 . + /// 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 + /// + /// 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 + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is /// , but - /// . is not correct for - /// the method + /// . is not correct for + /// the method. + /// + /// + /// is . /// - public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) + public static Enum ReadEnumeratedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Type enumType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - const UniversalTagNumber tagNumber = UniversalTagNumber.Enumerated; + 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 = tEnum.GetEnumUnderlyingType(); + Type backingType = enumType.GetEnumUnderlyingType(); - if (tEnum.IsDefined(typeof(FlagsAttribute), false)) + if (enumType.IsDefined(typeof(FlagsAttribute), false)) { throw new ArgumentException( - SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, - nameof(tEnum)); + SR.Argument_EnumeratedValueRequiresNonFlagsEnum, + nameof(enumType)); } // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. @@ -197,12 +219,20 @@ public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) backingType == typeof(short) || backingType == typeof(sbyte)) { - if (!TryReadSignedInteger(sizeLimit, expectedTag, tagNumber, out long value)) + if (!TryReadSignedInteger( + source, + ruleSet, + sizeLimit, + localTag, + TagNumber, + out long value, + out int consumed)) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_EnumeratedValueTooBig); } - return (Enum)Enum.ToObject(tEnum, value); + bytesConsumed = consumed; + return (Enum)Enum.ToObject(enumType, value); } if (backingType == typeof(uint) || @@ -210,191 +240,172 @@ public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) backingType == typeof(ushort) || backingType == typeof(byte)) { - if (!TryReadUnsignedInteger(sizeLimit, expectedTag, tagNumber, out ulong value)) + if (!TryReadUnsignedInteger( + source, + ruleSet, + sizeLimit, + localTag, + TagNumber, + out ulong value, + out int consumed)) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_EnumeratedValueTooBig); } - return (Enum)Enum.ToObject(tEnum, value); + bytesConsumed = consumed; + return (Enum)Enum.ToObject(enumType, value); } - Debug.Fail($"No handler for type {backingType.Name}"); - throw new CryptographicException(); + throw new AsnContentException( + SR.Format( + SR.Argument_EnumeratedValueBackingTypeNotSupported, + backingType.FullName)); } } - internal partial class AsnReader + public 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 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 + /// + /// 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 ReadOnlyMemory ReadEnumeratedBytes(Asn1Tag expectedTag) + /// + public ReadOnlyMemory ReadEnumeratedBytes(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan bytes = valueReader.ReadEnumeratedBytes(expectedTag); - ReadOnlyMemory memory = AsnValueReader.Slice(_data, bytes); + ReadOnlySpan bytes = + AsnDecoder.ReadEnumeratedBytes(_data.Span, RuleSet, out int consumed, expectedTag); - valueReader.MatchSlice(ref _data); - return memory; - } + ReadOnlyMemory memory = AsnDecoder.Slice(_data, bytes); - /// - /// 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)); + _data = _data.Slice(consumed); + return memory; } /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . + /// 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. + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// /// Destination enum type /// - /// the Enumerated value converted to a . + /// 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 + /// + /// 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 not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public TEnum ReadEnumeratedValue(Asn1Tag expectedTag) where TEnum : struct + public TEnum ReadEnumeratedValue(Asn1Tag? expectedTag = null) where TEnum : Enum { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum)); + TEnum ret = AsnDecoder.ReadEnumeratedValue(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; } /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . + /// 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. + /// 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 . + /// 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 + /// + /// 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 + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is /// , but - /// . is not correct for - /// the method + /// . is not correct for + /// the method. + /// + /// + /// is . /// - public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) + public Enum ReadEnumeratedValue(Type enumType, Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - Enum ret = valueReader.ReadEnumeratedValue(expectedTag, tEnum); - valueReader.MatchSlice(ref _data); + Enum ret = AsnDecoder.ReadEnumeratedValue(_data.Span, RuleSet, enumType, out int consumed, expectedTag); + _data = _data.Slice(consumed); return ret; } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs index 9c55f218db1dc0..1962f185d26ce6 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.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 index 3939428bdaae35..5e6e084ab9d4ba 100644 --- 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 @@ -2,94 +2,108 @@ // 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; -#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 Integer with tag UNIVERSAL 2, returning the contents - /// as a over the original data. + /// 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 bytes of the Integer value, in signed big-endian form. + /// The slice of the buffer containing 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 not defined. /// - 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 + /// + /// 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 ReadOnlySpan ReadIntegerBytes(Asn1Tag expectedTag) + public static ReadOnlySpan ReadIntegerBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength); - - _data = _data.Slice(headerLength + contents.Length); - return contents; + return GetIntegerContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out bytesConsumed); } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a . + /// 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 bytes of the Integer value, in signed big-endian form. + /// The decoded numeric 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. /// - 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 + /// + /// 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 BigInteger ReadInteger(Asn1Tag expectedTag) + public static BigInteger ReadInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength); + 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); @@ -112,115 +126,69 @@ public BigInteger ReadInteger(Asn1Tag expectedTag) CryptoPool.Return(tmp); } - _data = _data.Slice(headerLength + contents.Length); + bytesConsumed = consumed; return value; } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . + /// 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 value represented + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . /// - /// - /// 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. /// - 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 + /// + /// 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) + /// the method. + /// + public static bool TryReadInt32( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - if (TryReadUnsignedInteger(sizeof(uint), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) + if (TryReadSignedInteger( + source, + ruleSet, + sizeof(int), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out long longValue, + out bytesConsumed)) { - value = (uint)ulongValue; + value = (int)longValue; return true; } @@ -229,152 +197,65 @@ public bool TryReadUInt32(Asn1Tag expectedTag, out uint value) } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . + /// 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 value represented + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . /// - /// - /// 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. /// - 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 + /// + /// 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) + /// the method. + /// + [CLSCompliant(false)] + public static bool TryReadUInt32( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out uint value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - if (TryReadSignedInteger(sizeof(short), expectedTag, UniversalTagNumber.Integer, out long longValue)) + if (TryReadUnsignedInteger( + source, + ruleSet, + sizeof(uint), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out ulong ulongValue, + out bytesConsumed)) { - value = (short)longValue; + value = (uint)ulongValue; return true; } @@ -383,220 +264,184 @@ public bool TryReadInt16(Asn1Tag expectedTag, out short value) } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . + /// 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 value represented + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . /// - /// - /// 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. /// - 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 + /// + /// 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 TryReadInt8(Asn1Tag expectedTag, out sbyte value) + /// the method. + /// + public static bool TryReadInt64( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out long value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - if (TryReadSignedInteger(sizeof(sbyte), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (sbyte)longValue; - return true; - } - - value = 0; - return false; + return TryReadSignedInteger( + source, + ruleSet, + sizeof(long), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out value, + out bytesConsumed); } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . + /// 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 value represented + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. /// - /// - /// 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 + /// + /// 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). /// /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . /// - /// - /// 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 - /// - public bool TryReadUInt8(Asn1Tag expectedTag, out byte value) + /// the method. + /// + [CLSCompliant(false)] + public static bool TryReadUInt64( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out ulong value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - if (TryReadUnsignedInteger(sizeof(byte), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (byte)ulongValue; - return true; - } - - value = 0; - return false; + return TryReadUnsignedInteger( + source, + ruleSet, + sizeof(ulong), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out value, + out bytesConsumed); } - private ReadOnlySpan GetIntegerContents( + private static ReadOnlySpan GetIntegerContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber tagNumber, - out int headerLength) + out int bytesConsumed) { - Asn1Tag tag = ReadTagAndLength(out int? length, out headerLength); - CheckExpectedTag(tag, expectedTag, tagNumber); + // 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 (tag.IsConstructed || length < 1) + if (contents.IsEmpty) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } - // 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) + if (BinaryPrimitives.TryReadUInt16BigEndian(contents, out ushort bigEndianValue)) { - 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); + throw new AsnContentException(); } } + bytesConsumed = consumed; return contents; } - internal bool TryReadSignedInteger( + private static bool TryReadSignedInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, int sizeLimit, Asn1Tag expectedTag, UniversalTagNumber tagNumber, - out long value) + out long value, + out int bytesConsumed) { Debug.Assert(sizeLimit <= sizeof(long)); - ReadOnlySpan contents = GetIntegerContents(expectedTag, tagNumber, out int headerLength); + ReadOnlySpan contents = GetIntegerContents( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); if (contents.Length > sizeLimit) { value = 0; + bytesConsumed = 0; return false; } @@ -609,26 +454,34 @@ internal bool TryReadSignedInteger( accum |= contents[i]; } - _data = _data.Slice(headerLength + contents.Length); + bytesConsumed = consumed; value = accum; return true; } - internal bool TryReadUnsignedInteger( + private static bool TryReadUnsignedInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, int sizeLimit, Asn1Tag expectedTag, UniversalTagNumber tagNumber, - out ulong value) + out ulong value, + out int bytesConsumed) { Debug.Assert(sizeLimit <= sizeof(ulong)); - ReadOnlySpan contents = GetIntegerContents(expectedTag, tagNumber, out int headerLength); - int contentLength = contents.Length; + ReadOnlySpan contents = GetIntegerContents( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); bool isNegative = (contents[0] & 0x80) != 0; if (isNegative) { + bytesConsumed = 0; value = 0; return false; } @@ -641,6 +494,7 @@ internal bool TryReadUnsignedInteger( if (contents.Length > sizeLimit) { + bytesConsumed = 0; value = 0; return false; } @@ -653,565 +507,241 @@ internal bool TryReadUnsignedInteger( accum |= contents[i]; } - _data = _data.Slice(headerLength + contentLength); + bytesConsumed = consumed; value = accum; return true; } } - internal partial class AsnReader + public 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 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 + /// + /// 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 ReadOnlyMemory ReadIntegerBytes(Asn1Tag expectedTag) + public ReadOnlyMemory ReadIntegerBytes(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan bytes = valueReader.ReadIntegerBytes(expectedTag); - ReadOnlyMemory memory = AsnValueReader.Slice(_data, bytes); + ReadOnlySpan bytes = + AsnDecoder.ReadIntegerBytes(_data.Span, RuleSet, out int consumed, expectedTag); - valueReader.MatchSlice(ref _data); - return memory; + ReadOnlyMemory ret = AsnDecoder.Slice(_data, bytes); + + _data = _data.Slice(consumed); + return ret; } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a . + /// 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 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 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 BigInteger ReadInteger(Asn1Tag expectedTag) + public BigInteger ReadInteger(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - BigInteger ret = valueReader.ReadInteger(expectedTag); - valueReader.MatchSlice(ref _data); + BigInteger ret = AsnDecoder.ReadInteger(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); return ret; } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . + /// Attempts to read the next value as an Integer with a specified tag, + /// as a signed 32-bit value. /// /// - /// On success, receives the value represented + /// On success, receives the decoded value. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between + /// 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 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 + /// the method. /// - public bool TryReadInt32(Asn1Tag expectedTag, out int value) + public bool TryReadInt32(out int value, Asn1Tag? expectedTag = null) { - if (TryReadSignedInteger(sizeof(int), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (int)longValue; - return true; - } - - value = 0; - return false; + bool ret = AsnDecoder.TryReadInt32(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . + /// Attempts to read the next value as an Integer with a specified tag, + /// as an unsigned 32-bit value. /// /// - /// On success, receives the value represented + /// On success, receives the decoded value. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between + /// 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 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 + /// the method. /// - public bool TryReadUInt32(Asn1Tag expectedTag, out uint value) + [CLSCompliant(false)] + public bool TryReadUInt32(out uint value, Asn1Tag? expectedTag = null) { - if (TryReadUnsignedInteger(sizeof(uint), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (uint)ulongValue; - return true; - } - - value = 0; - return false; + bool ret = AsnDecoder.TryReadUInt32(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . + /// Attempts to read the next value as an Integer with a specified tag, + /// as a signed 64-bit value. /// /// - /// On success, receives the value represented + /// On success, receives the decoded value. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between + /// 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 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 + /// the method. /// - public bool TryReadInt64(Asn1Tag expectedTag, out long value) + public bool TryReadInt64(out long value, Asn1Tag? expectedTag = null) { - return TryReadSignedInteger(sizeof(long), expectedTag, UniversalTagNumber.Integer, out value); + bool ret = AsnDecoder.TryReadInt64(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; } /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . + /// Attempts to read the next value as an Integer with a specified tag, + /// as an unsigned 64-bit value. /// /// - /// On success, receives the value represented + /// On success, receives the decoded value. /// - /// - /// 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 + /// + /// The tag to check for before reading, or for the default tag (Universal 2). /// /// - /// false and does not advance the reader if the value is not between + /// 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 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 + /// the method. /// - public bool TryReadUInt64(Asn1Tag expectedTag, out ulong value) + [CLSCompliant(false)] + public bool TryReadUInt64(out ulong value, Asn1Tag? expectedTag = null) { - 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; + 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 index d972df2324837e..3000f8817eb486 100644 --- 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 @@ -2,58 +2,64 @@ // 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.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . + /// 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 . + /// 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 defined. /// - /// - /// 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 + /// + /// 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-- + /// is not an enum type. + /// + /// -or- + /// /// was not declared with - /// --OR-- + /// + /// -or- + /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// /// The bit alignment performed by this method is to interpret the most significant bit @@ -97,151 +103,265 @@ public TFlagsEnum ReadNamedBitListValue() where TFlagsEnum : struct /// the example enum uses values thar are different from /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. /// - public TFlagsEnum ReadNamedBitListValue(Asn1Tag expectedTag) where TFlagsEnum : struct + public static TFlagsEnum ReadNamedBitListValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + where TFlagsEnum : Enum { Type tFlagsEnum = typeof(TFlagsEnum); - return (TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, 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 the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules, converting it to the + /// [] enum specified by . /// - /// Type object representing the destination type. + /// 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 . + /// 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 defined. /// - /// - /// 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 + /// + /// 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 not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// /// . is /// , but /// . is not correct for - /// the method + /// the method. + /// + /// + /// is /// - /// - public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) + /// + 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 = tFlagsEnum.GetEnumUnderlyingType(); + Type backingType = flagsEnumType.GetEnumUnderlyingType(); - if (!tFlagsEnum.IsDefined(typeof(FlagsAttribute), false)) + if (!flagsEnumType.IsDefined(typeof(FlagsAttribute), false)) { throw new ArgumentException( - SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, - nameof(tFlagsEnum)); + SR.Argument_NamedBitListRequiresFlagsEnum, + nameof(flagsEnumType)); } + Span stackSpan = stackalloc byte[sizeof(ulong)]; int sizeLimit = Marshal.SizeOf(backingType); - ReadOnlySpan saveData = _data; - Span stackSpan; + stackSpan = stackSpan.Slice(0, sizeLimit); + + bool read = TryReadBitString( + source, + stackSpan, + ruleSet, + out int unusedBitCount, + out int consumed, + out int bytesWritten, + expectedTag); - unsafe + if (!read) { - byte* stackPtr = stackalloc byte[sizeLimit]; - stackSpan = new Span(stackPtr, sizeLimit); + throw new AsnContentException( + SR.Format(SR.ContentException_NamedBitListValueTooBig, flagsEnumType.Name)); } - // 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 + Enum ret; + + if (bytesWritten == 0) { - if (!TryCopyBitStringBytes(expectedTag, stackSpan, out int unusedBitCount, out int bytesWritten)) - { - throw new CryptographicException( - SR.Format(SR.Cryptography_Asn_NamedBitListValueTooBig, tFlagsEnum.Name)); - } + // The mode isn't relevant, zero is always zero. + ret = (Enum)Enum.ToObject(flagsEnumType, 0); + bytesConsumed = consumed; + return ret; + } - if (bytesWritten == 0) - { - // The mode isn't relevant, zero is always zero. - return (Enum)Enum.ToObject(tFlagsEnum, 0); - } + ReadOnlySpan valueSpan = stackSpan.Slice(0, bytesWritten); - 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]; - // 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) + // 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) { - byte lastByte = valueSpan[bytesWritten - 1]; + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + } - // 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); + // 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; + } - if ((lastByte & testBit) == 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } + /// + /// 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 _); - // 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)); + // Get the last ArgumentException out of the way before we rent arrays + if (expectedTag != null) + { + CheckExpectedTag(actualTag, expectedTag.Value, UniversalTagNumber.BitString); } - catch + + // 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)) { - _data = saveData; - throw; + 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) @@ -270,55 +390,64 @@ private static long InterpretNamedBitListReversed(ReadOnlySpan valueSpan) 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; + } + } } - internal partial class AsnReader + public 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 . + /// 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 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 + /// + /// 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-- + /// is not an enum type. + /// + /// -or- + /// /// was not declared with - /// --OR-- + /// + /// -or- + /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// /// The bit alignment performed by this method is to interpret the most significant bit @@ -362,65 +491,102 @@ public TFlagsEnum ReadNamedBitListValue() where TFlagsEnum : struct /// the example enum uses values thar are different from /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. /// - public TFlagsEnum ReadNamedBitListValue(Asn1Tag expectedTag) where TFlagsEnum : struct + public TFlagsEnum ReadNamedBitListValue(Asn1Tag? expectedTag = null) where TFlagsEnum : Enum { - Type tFlagsEnum = typeof(TFlagsEnum); + TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue( + _data.Span, + RuleSet, + out int consumed, + expectedTag); - return (TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum)); + _data = _data.Slice(consumed); + return ret; } /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . + /// Reads the next value as a NamedBitList with a specified tag, converting it to the + /// [] enum specified by . /// - /// Type object representing the destination type. + /// The tag to check for before reading. + /// Type object representing the destination type. /// - /// the NamedBitList value converted to a . + /// 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 + /// + /// 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 + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is /// - /// - public Enum ReadNamedBitListValue(Type tFlagsEnum) => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString, tFlagsEnum); + /// + 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 tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . + /// Reads the next value as a NamedBitList with a specified tag. /// /// The tag to check for before reading. - /// Type object representing the destination type. /// - /// the NamedBitList value converted to a . + /// 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 --OR--- - /// the encoded value is too big to fit in a 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 an enum type --OR-- - /// was not declared with - /// --OR-- /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// - public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) + /// + public BitArray ReadNamedBitList(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - Enum ret = valueReader.ReadNamedBitListValue(expectedTag, tFlagsEnum); - valueReader.MatchSlice(ref _data); + 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 index c7f781d46d152d..f8c91c2b4cab46 100644 --- 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 @@ -2,83 +2,96 @@ // 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 ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a NULL with tag UNIVERSAL 5. + /// Reads a Null 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 5). + /// + /// + /// is not defined. /// - 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 + /// + /// 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 void ReadNull(Asn1Tag expectedTag) + public static void ReadNull( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.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 (tag.IsConstructed || length != 0) + if (contents.Length != 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } - _data = _data.Slice(headerLength); + bytesConsumed = consumed; } } - internal partial class AsnReader + public 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 + /// + /// 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 + /// the method. /// - public void ReadNull(Asn1Tag expectedTag) + public void ReadNull(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - valueReader.ReadNull(expectedTag); - valueReader.MatchSlice(ref _data); + 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 index 4b3756c45c4dec..2f7422d5baacbc 100644 --- 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 @@ -2,100 +2,104 @@ // 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; +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 an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. + /// 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. - /// - /// On success, receives the number of bytes written to . + /// 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. /// - /// - /// 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 . + /// 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). /// /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. + /// if is large enough to receive the + /// value of the Octet String; + /// otherwise, . /// - /// - /// 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. + /// + /// -or- + /// + /// overlaps . /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, + /// + /// + public static bool TryReadOctetString( + ReadOnlySpan source, Span destination, - out int bytesWritten) + AsnEncodingRules ruleSet, + out int bytesConsumed, + out int bytesWritten, + Asn1Tag? expectedTag = null) { - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, + 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 ReadOnlySpan contents, + out int consumed)) { if (contents.Length > destination.Length) { bytesWritten = 0; + bytesConsumed = 0; return false; } contents.CopyTo(destination); bytesWritten = contents.Length; - _data = _data.Slice(headerLength + contents.Length); + bytesConsumed = consumed; return true; } - Debug.Assert(actualTag.IsConstructed); - bool copied = TryCopyConstructedOctetStringContents( - Slice(_data, headerLength, contentLength), + Slice(source, headerLength, contentLength), + ruleSet, destination, contentLength == null, out int bytesRead, @@ -103,212 +107,207 @@ public bool TryCopyOctetStringBytes( if (copied) { - _data = _data.Slice(headerLength + bytesRead); + bytesConsumed = headerLength + bytesRead; + } + else + { + bytesConsumed = 0; } return copied; } /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. + /// 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). + /// /// - /// a copy of the contents in a newly allocated, precisely sized, array. + /// An array containing the contents of the Octet String 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. /// - /// - /// - /// - 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 + /// + /// 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 byte[] ReadOctetString(Asn1Tag expectedTag) + /// + /// + public static byte[] ReadOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - ReadOnlySpan span; - - if (TryReadPrimitiveOctetStringBytes(expectedTag, out span)) - { - return span.ToArray(); - } + byte[]? rented = null; - span = PeekEncodedValue(); + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + out int consumed, + ref rented); - // Guaranteed long enough - byte[] rented = CryptoPool.Rent(span.Length); - int dataLength = 0; + byte[] ret = contents.ToArray(); - try + if (rented != null) { - 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); + CryptoPool.Return(rented, contents.Length); } + + bytesConsumed = consumed; + return ret; } - private bool TryReadPrimitiveOctetStringBytes( + private static bool TryReadPrimitiveOctetStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, - out Asn1Tag actualTag, + UniversalTagNumber universalTagNumber, out int? contentLength, out int headerLength, out ReadOnlySpan contents, - UniversalTagNumber universalTagNumber = UniversalTagNumber.OctetString) + out int bytesConsumed) { - actualTag = ReadTagAndLength(out contentLength, out headerLength); + 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) + if (ruleSet == AsnEncodingRules.DER) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderDer_TryBerOrCer); } contents = default; + bytesConsumed = 0; return false; } Debug.Assert(contentLength.HasValue); - ReadOnlySpan encodedValue = Slice(_data, headerLength, contentLength.Value); - if (RuleSet == AsnEncodingRules.CER && encodedValue.Length > MaxCERSegmentSize) + if (ruleSet == AsnEncodingRules.CER && encodedValue.Length > MaxCERSegmentSize) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); } contents = encodedValue; + bytesConsumed = headerLength + encodedValue.Length; 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. + /// 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. /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the OCTET STRING. + /// 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. /// - /// - /// 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. + /// + /// 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). /// /// - /// 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. + /// if the Octet String value has a primitive encoding; + /// otherwise, . /// - /// - /// 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 bool TryReadPrimitiveOctetStringBytes(Asn1Tag expectedTag, out ReadOnlySpan contents) + /// + public static bool TryReadPrimitiveOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out ReadOnlySpan value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - return TryReadPrimitiveOctetStringBytes(expectedTag, UniversalTagNumber.OctetString, out contents); + return TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + contentLength: out _, + headerLength: out _, + out value, + out bytesConsumed); } - private int CountConstructedOctetString(ReadOnlySpan source, bool isIndefinite) + 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) + if (ruleSet == AsnEncodingRules.CER && contentLength <= MaxCERSegmentSize) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } return contentLength; } - private void CopyConstructedOctetString( + private static void CopyConstructedOctetString( ReadOnlySpan source, + AsnEncodingRules ruleSet, Span destination, bool isIndefinite, out int bytesRead, @@ -316,14 +315,16 @@ private void CopyConstructedOctetString( { bytesWritten = CopyConstructedOctetString( source, + ruleSet, destination, true, isIndefinite, out bytesRead); } - private int CopyConstructedOctetString( + private static int CopyConstructedOctetString( ReadOnlySpan source, + AsnEncodingRules ruleSet, Span destination, bool write, bool isIndefinite, @@ -332,8 +333,7 @@ private int CopyConstructedOctetString( bytesRead = 0; int lastSegmentLength = MaxCERSegmentSize; - ReadOnlySpan originalSpan = _data; - AsnValueReader tmpReader = OpenUnchecked(source, RuleSet); + ReadOnlySpan cur = source; Stack<(int Offset, int Length, bool IsIndefinite, int BytesRead)>? readerStack = null; int totalLength = 0; Asn1Tag tag = Asn1Tag.ConstructedBitString; @@ -341,35 +341,35 @@ private int CopyConstructedOctetString( while (true) { - while (tmpReader.HasData) + while (!cur.IsEmpty) { - tag = tmpReader.ReadTagAndLength(out int? length, out int headerLength); + tag = ReadTagAndLength(cur, ruleSet, out int? length, out int headerLength); if (tag == Asn1Tag.PrimitiveOctetString) { - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) { // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + 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(tmpReader._data, headerLength, length.Value); + ReadOnlySpan contents = Slice(cur, headerLength, length.Value); int localLen = headerLength + contents.Length; - tmpReader._data = tmpReader._data.Slice(localLen); + cur = cur.Slice(localLen); bytesRead += localLen; totalLength += contents.Length; lastSegmentLength = contents.Length; - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength > MaxCERSegmentSize) + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength > MaxCERSegmentSize) { // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } if (write) @@ -387,8 +387,8 @@ private int CopyConstructedOctetString( 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); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + cur = topSpan.Slice(bytesRead); bytesRead += pushedBytesRead; isIndefinite = wasIndefinite; @@ -401,10 +401,10 @@ private int CopyConstructedOctetString( } else if (tag == Asn1Tag.ConstructedOctetString) { - if (RuleSet == AsnEncodingRules.CER) + if (ruleSet == AsnEncodingRules.CER) { // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); } if (readerStack == null) @@ -412,36 +412,36 @@ private int CopyConstructedOctetString( readerStack = new Stack<(int, int, bool, int)>(); } - if (!originalSpan.Overlaps(tmpReader._data, out int curOffset)) + if (!source.Overlaps(cur, out int curOffset)) { Debug.Fail("Non-overlapping data encountered..."); - throw new CryptographicException(); + throw new AsnContentException(); } - readerStack.Push((curOffset, tmpReader._data.Length, isIndefinite, bytesRead)); + readerStack.Push((curOffset, cur.Length, isIndefinite, bytesRead)); - tmpReader._data = Slice(tmpReader._data, headerLength, length); + 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 CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } } if (isIndefinite && tag != Asn1Tag.EndOfContents) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } if (readerStack?.Count > 0) { (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); - tmpReader._data = topSpan.Slice(bytesRead); + cur = topSpan.Slice(bytesRead); isIndefinite = wasIndefinite; bytesRead += pushedBytesRead; @@ -453,8 +453,9 @@ private int CopyConstructedOctetString( } } - private bool TryCopyConstructedOctetStringContents( + private static bool TryCopyConstructedOctetStringContents( ReadOnlySpan source, + AsnEncodingRules ruleSet, Span dest, bool isIndefinite, out int bytesRead, @@ -462,7 +463,7 @@ private bool TryCopyConstructedOctetStringContents( { bytesRead = 0; - int contentLength = CountConstructedOctetString(source, isIndefinite); + int contentLength = CountConstructedOctetString(source, ruleSet, isIndefinite); if (dest.Length < contentLength) { @@ -470,275 +471,167 @@ private bool TryCopyConstructedOctetStringContents( return false; } - CopyConstructedOctetString(source, dest, isIndefinite, out bytesRead, out bytesWritten); + CopyConstructedOctetString(source, ruleSet, dest, isIndefinite, out bytesRead, out bytesWritten); Debug.Assert(bytesWritten == contentLength); return true; } - private ReadOnlySpan GetOctetStringContents( + private static ReadOnlySpan GetOctetStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, - out int bytesRead, + out int bytesConsumed, ref byte[]? rented, Span tmpSpace = default) { Debug.Assert(rented == null); - if (TryReadPrimitiveOctetStringBytes( + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, expectedTag, - out Asn1Tag actualTag, + universalTagNumber, out int? contentLength, out int headerLength, - out ReadOnlySpan contentsOctets, - universalTagNumber)) + out ReadOnlySpan contents, + out bytesConsumed)) { - bytesRead = headerLength + contentsOctets.Length; - return contentsOctets; + return contents; } - Debug.Assert(actualTag.IsConstructed); + // 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); - ReadOnlySpan source = Slice(_data, headerLength, contentLength); - bool isIndefinite = contentLength == null; - int octetStringLength = CountConstructedOctetString(source, isIndefinite); + if (tmpSpace.Length > 0 && tooBig > tmpSpace.Length) + { + bool isIndefinite = contentLength == null; + tooBig = CountConstructedOctetString(contents, ruleSet, isIndefinite); + } - if (tmpSpace.Length < octetStringLength) + if (tooBig > tmpSpace.Length) { - rented = CryptoPool.Rent(octetStringLength); + rented = CryptoPool.Rent(tooBig); tmpSpace = rented; } - CopyConstructedOctetString( - source, + if (TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, tmpSpace, - isIndefinite, - out int localBytesRead, - out int bytesWritten); - - Debug.Assert(bytesWritten == octetStringLength); + contentLength == null, + out int bytesRead, + out int bytesWritten)) + { + bytesConsumed = headerLength + bytesRead; + return tmpSpace.Slice(0, bytesWritten); + } - bytesRead = headerLength + localBytesRead; - return tmpSpace.Slice(0, bytesWritten); + Debug.Fail("TryCopyConstructedOctetStringContents failed with a pre-allocated buffer"); + throw new AsnContentException(); } } - internal partial class AsnReader + public 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 . /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// /// - /// true and advances the reader if had sufficient + /// and advances the reader if had sufficient /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, + /// + /// + public bool TryReadOctetString( Span destination, - out int bytesWritten) + out int bytesWritten, + Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); + bool ret = AsnDecoder.TryReadOctetString( + _data.Span, + destination, + RuleSet, + out int bytesConsumed, + out bytesWritten, + expectedTag); - if (valueReader.TryCopyOctetStringBytes(expectedTag, destination, out bytesWritten)) + if (ret) { - valueReader.MatchSlice(ref _data); - return true; + _data = _data.Slice(bytesConsumed); } - 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); + 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. + /// + /// 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. + /// 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 + /// + /// 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 byte[] ReadOctetString(Asn1Tag expectedTag) + /// + /// + public byte[] ReadOctetString(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - byte[] ret = valueReader.ReadOctetString(expectedTag); - valueReader.MatchSlice(ref _data); + byte[] ret = AsnDecoder.ReadOctetString(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); 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 + /// 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. @@ -747,43 +640,47 @@ public bool TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents) /// 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. + /// 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 + /// + /// 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 bool TryReadPrimitiveOctetStringBytes(Asn1Tag expectedTag, out ReadOnlyMemory contents) - { - return TryReadPrimitiveOctetStringBytes(expectedTag, UniversalTagNumber.OctetString, out contents); - } - - private bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out ReadOnlyMemory contents) + /// + public bool TryReadPrimitiveOctetString(out ReadOnlyMemory contents, Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan span; - - if (valueReader.TryReadPrimitiveOctetStringBytes(expectedTag, universalTagNumber, out span)) + bool ret = AsnDecoder.TryReadPrimitiveOctetString( + _data.Span, + RuleSet, + out ReadOnlySpan span, + out int consumed, + expectedTag); + + if (ret) { - contents = AsnValueReader.Slice(_data, span); - valueReader.MatchSlice(ref _data); - return true; + contents = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + contents = default; } - contents = default; - return false; + return ret; } } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs index 75692397e97a80..5f3b3b39275c50 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.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 index 6f89866a3f84cc..dde93f9348a99b 100644 --- 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 @@ -2,140 +2,147 @@ // 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 ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// 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). + /// Reads a Sequence or Sequence-Of value from with a specified tag + /// under the specified encoding rules. /// - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// + /// 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, and may contain data - /// which is not valid under the current encoding rules. + /// 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. /// - /// - /// 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. /// - /// - 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 + /// + /// 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 AsnValueReader ReadSequence(Asn1Tag expectedTag) + public static void ReadSequence( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + Asn1Tag? expectedTag = default) { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Sequence); + 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 CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException( + SR.Format( + SR.ContentException_ConstructedEncodingRequired, + UniversalTagNumber.Sequence)); } - int suffix = 0; - - if (length == null) + if (length.HasValue) { - length = SeekEndOfContents(_data.Slice(headerLength)); - suffix = EndOfContentsEncodedLength; - } + if (length.Value + headerLength > source.Length) + { + throw GetValidityException(LengthValidity.LengthExceedsInput); + } - ReadOnlySpan contents = Slice(_data, headerLength, length.Value); + contentLength = length.Value; + contentOffset = headerLength; + bytesConsumed = contentLength + headerLength; + } + else + { + int len = SeekEndOfContents(source.Slice(headerLength), ruleSet); - _data = _data.Slice(headerLength + contents.Length + suffix); - return OpenUnchecked(contents, RuleSet); + contentLength = len; + contentOffset = headerLength; + bytesConsumed = len + headerLength + EndOfContentsEncodedLength; + } } } - internal partial class AsnReader + public 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). + /// 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. + /// + /// The tag to check for before reading, or for the default tag (Universal 16). + /// /// - /// an positioned at the first - /// value in the sequence (or with == false). + /// 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 + /// + /// 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 AsnReader ReadSequence(Asn1Tag expectedTag) + public AsnReader ReadSequence(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - AsnValueReader innerValueReader = valueReader.ReadSequence(expectedTag); - - AsnReader ret = new AsnReader(_data, RuleSet); - innerValueReader.MatchSlice(ref ret._data); + AsnDecoder.ReadSequence( + _data.Span, + RuleSet, + out int contentStart, + out int contentLength, + out int bytesConsumed, + expectedTag); - valueReader.MatchSlice(ref _data); + 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 index cd3adaa4cb8cb5..ee91de368eea31 100644 --- 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 @@ -2,182 +2,225 @@ // 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 ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// 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). + /// Reads a Set-Of value from with a specified tag + /// under the specified encoding rules. /// - /// - /// 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). + /// 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. /// - /// - /// 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 + /// 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). /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// /// - /// 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 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. /// - /// - /// 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 AsnValueReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) + 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(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.SetOf); + 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 CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException( + SR.Format( + SR.ContentException_ConstructedEncodingRequired, + UniversalTagNumber.SetOf)); } - int suffix = 0; + int suffix; + ReadOnlySpan contents; - if (length == null) + if (length.HasValue) + { + suffix = 0; + contents = Slice(source, headerLength, length.Value); + } + else { - length = SeekEndOfContents(_data.Slice(headerLength)); + int actualLength = SeekEndOfContents(source.Slice(headerLength), ruleSet); + contents = Slice(source, headerLength, actualLength); 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) + if (ruleSet == AsnEncodingRules.DER || + ruleSet == AsnEncodingRules.CER) { - AsnValueReader reader = OpenUnchecked(contents, RuleSet); - ReadOnlySpan current = ReadOnlySpan.Empty; + ReadOnlySpan remaining = contents; + ReadOnlySpan previous = default; - while (reader.HasData) + while (!remaining.IsEmpty) { - ReadOnlySpan previous = current; - current = reader.ReadEncodedValue(); + 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 CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_SetOfNotSorted); } + + previous = current; } } } - _data = _data.Slice(headerLength + contents.Length + suffix); - return OpenUnchecked(contents, RuleSet); + contentOffset = headerLength; + contentLength = contents.Length; + bytesConsumed = headerLength + contents.Length + suffix; } } - internal partial class AsnReader + public 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). + /// 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 ). /// - /// - /// 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). + /// + /// The tag to check for before reading, or for the default tag (Universal 17). /// /// - /// an positioned at the first - /// value in the set-of (or with == false). + /// 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 + /// + /// 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); + /// + /// . 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 an positioned at the first - /// value in the set-of (or with == false). + /// and returns the result as a new reader positioned at the first + /// value in the set-of (or with == ). /// - /// 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 + /// 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). + /// /// - /// an positioned at the first - /// value in the set-of (or with == false). + /// 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 + /// + /// 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 AsnReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) + public AsnReader ReadSetOf(bool skipSortOrderValidation, Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - AsnValueReader innerValueReader = valueReader.ReadSetOf(expectedTag, skipSortOrderValidation); - - AsnReader ret = new AsnReader(_data, RuleSet); - innerValueReader.MatchSlice(ref ret._data); + AsnDecoder.ReadSetOf( + _data.Span, + RuleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + skipSortOrderValidation, + expectedTag); - valueReader.MatchSlice(ref _data); + 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 index 6775e776771008..1ac341a83634ba 100644 --- 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 @@ -2,267 +2,235 @@ // 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.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 character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the contents as an unprocessed - /// over the original data. + /// 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. /// - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the character string. + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// The tag to check for before reading. /// - /// - /// 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 slice of the input buffer that corresponds to + /// the value of the Bit String. + /// This parameter is treated as uninitialized. /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// /// - /// 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. + /// 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. /// - /// - /// 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 the same as - /// . + /// . is not a character + /// string tag type. /// - /// - public bool TryReadPrimitiveCharacterStringBytes( + /// + public static bool TryReadPrimitiveCharacterStringBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, - UniversalTagNumber encodingType, - out ReadOnlySpan contents) + out ReadOnlySpan value, + out int bytesConsumed) { - CheckCharacterStringEncodingType(encodingType); + // 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 TryReadPrimitiveOctetStringBytes(expectedTag, encodingType, out contents); + return TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + contentLength: out _, + headerLength: out _, + out value, + out bytesConsumed); } /// - /// 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. + /// 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. /// - /// - /// A corresponding to the value type to process. - /// + /// The buffer containing encoded data. /// 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 encoding constraints to use when interpreting the data. /// The tag to check for before reading. - /// - /// A corresponding to 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 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. + /// 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 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 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 the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, + /// . 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) { - CheckCharacterStringEncodingType(encodingType); + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } - bool copied = TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination, - out int bytesRead, - out bytesWritten); + // 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 (copied) + if (expectedTag.TagClass == TagClass.Universal) { - _data = _data.Slice(bytesRead); + universalTagNumber = (UniversalTagNumber)expectedTag.TagValue; + + if (!IsCharacterStringEncodingType(universalTagNumber)) + { + throw new ArgumentException(SR.Argument_Tag_NotCharacterString, nameof(expectedTag)); + } } - return copied; + return TryReadCharacterStringBytesCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + destination, + out bytesConsumed, + out bytesWritten); } /// - /// 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. + /// 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. /// - /// A corresponding to the value type to process. + /// One of the enumeration values which represents the value type to process. /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// 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. + /// + /// When this method returns, the number of chars written to . + /// This parameter is treated as uninitialized. /// - /// 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 universal tag that is + /// appropriate to the requested encoding type. /// /// - /// true and advances the reader if had sufficient + /// and advances the reader if had sufficient /// length to receive the value, otherwise - /// false and the reader does not advance. + /// and the reader does not advance. /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// /// + /// 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 + /// + /// 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 @@ -270,87 +238,71 @@ public bool TryCopyCharacterStringBytes( /// . is not the same as /// . /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, + /// + /// + public static bool TryReadCharacterString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) + out int bytesConsumed, + out int charsWritten, + Asn1Tag? expectedTag = null) { - return TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination.AsSpan(), - out bytesWritten); - } + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - /// - /// 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), + 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, copying the decoded value into a provided destination buffer. + /// encoding type, returning the decoded string. /// - /// The tag to check for before reading. + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. /// - /// A corresponding to the value type to process. + /// One of the enumeration values which represents the value type to process. /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . + /// + /// 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. /// /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 @@ -358,133 +310,82 @@ public bool TryCopyCharacterString( /// . is not the same as /// . /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, + /// + /// + /// + public static string ReadCharacterString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, UniversalTagNumber encodingType, - Span destination, - out int charsWritten) + out int bytesConsumed, + Asn1Tag? expectedTag = null) { 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); + return ReadCharacterStringCore( + source, + ruleSet, + expectedTag ?? new Asn1Tag(encodingType), + encodingType, + encoding, + out bytesConsumed); } // T-REC-X.690-201508 sec 8.23 - private bool TryCopyCharacterStringBytes( + private static bool TryReadCharacterStringBytesCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, Span destination, - out int bytesRead, + out int bytesConsumed, out int bytesWritten) { // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - if (TryReadPrimitiveOctetStringBytes( + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, expectedTag, - out Asn1Tag actualTag, + universalTagNumber, out int? contentLength, out int headerLength, out ReadOnlySpan contents, - universalTagNumber)) + out int consumed)) { - bytesWritten = contents.Length; - - if (destination.Length < bytesWritten) + if (contents.Length > destination.Length) { bytesWritten = 0; - bytesRead = 0; + bytesConsumed = 0; return false; } contents.CopyTo(destination); - bytesRead = headerLength + bytesWritten; + bytesWritten = contents.Length; + bytesConsumed = consumed; return true; } - Debug.Assert(actualTag.IsConstructed); - bool copied = TryCopyConstructedOctetStringContents( - Slice(_data, headerLength, contentLength), + Slice(source, headerLength, contentLength), + ruleSet, destination, contentLength == null, - out int contentBytesRead, + out int bytesRead, out bytesWritten); if (copied) { - bytesRead = headerLength + contentBytesRead; + bytesConsumed = headerLength + bytesRead; } else { - bytesRead = 0; + bytesConsumed = 0; } return copied; } - private static unsafe bool TryCopyCharacterString( + private static unsafe bool TryReadCharacterStringCore( ReadOnlySpan source, Span destination, Text.Encoding encoding, @@ -514,106 +415,110 @@ private static unsafe bool TryCopyCharacterString( } catch (DecoderFallbackException e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + throw new AsnContentException(SR.ContentException_DefaultMessage, e); } return true; } } - private string ReadCharacterString( + private static string ReadCharacterStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber universalTagNumber, - Text.Encoding encoding) + 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); - try - { - string str; + string str; - if (contents.Length == 0) - { - str = string.Empty; - } - else + if (contents.Length == 0) + { + str = string.Empty; + } + else + { + unsafe { - unsafe + fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents)) { - fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents)) + try + { + str = encoding.GetString(bytePtr, contents.Length); + } + catch (DecoderFallbackException e) { - try - { - str = encoding.GetString(bytePtr, contents.Length); - } - catch (DecoderFallbackException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } + throw new AsnContentException(SR.ContentException_DefaultMessage, e); } } } - - _data = _data.Slice(bytesRead); - return str; } - finally + + if (rented != null) { - if (rented != null) - { - CryptoPool.Return(rented, contents.Length); - } + CryptoPool.Return(rented, contents.Length); } + + bytesConsumed = bytesRead; + return str; } - private bool TryCopyCharacterString( + 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); - try - { - bool copied = TryCopyCharacterString( - contents, - destination, - encoding, - out charsWritten); + bool copied = TryReadCharacterStringCore( + contents, + destination, + encoding, + out charsWritten); - if (copied) - { - _data = _data.Slice(bytesRead); - } + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } - return copied; + if (copied) + { + bytesConsumed = bytesRead; } - finally + else { - if (rented != null) - { - CryptoPool.Return(rented, contents.Length); - } + bytesConsumed = 0; } + + return copied; } - private static void CheckCharacterStringEncodingType(UniversalTagNumber encodingType) + private static bool IsCharacterStringEncodingType(UniversalTagNumber encodingType) { // T-REC-X.680-201508 sec 41 switch (encodingType) @@ -631,356 +536,168 @@ private static void CheckCharacterStringEncodingType(UniversalTagNumber encoding case UniversalTagNumber.UTF8String: case UniversalTagNumber.VideotexString: // VisibleString is an alias for ISO646String (already listed) - return; + return true; } - throw new ArgumentOutOfRangeException(nameof(encodingType)); + return false; } } - internal partial class AsnReader + public 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. + /// corresponding to the value of the character 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. + /// 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 + /// + /// 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 - /// . + /// . is not a character + /// string tag type. /// - /// + /// public bool TryReadPrimitiveCharacterStringBytes( Asn1Tag expectedTag, - UniversalTagNumber encodingType, out ReadOnlyMemory contents) { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan span; + bool ret = AsnDecoder.TryReadPrimitiveCharacterStringBytes( + _data.Span, + RuleSet, + expectedTag, + out ReadOnlySpan span, + out int consumed); - if (valueReader.TryReadPrimitiveCharacterStringBytes(expectedTag, encodingType, out span)) + if (ret) { - contents = AsnValueReader.Slice(_data, span); - valueReader.MatchSlice(ref _data); - return true; + contents = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); } - - 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)) + else { - valueReader.MatchSlice(ref _data); - return true; + contents = default; } - return false; + return ret; } /// - /// 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. + /// Reads the next value as character string with the specified tag, + /// copying the unprocessed bytes 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 + /// and advances the reader if had sufficient /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 - /// . + /// . is not a character + /// string tag type. /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( + /// + /// + /// + public bool TryReadCharacterStringBytes( + Span destination, Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, out int bytesWritten) { - return TryCopyCharacterStringBytes( + bool ret = AsnDecoder.TryReadCharacterStringBytes( + _data.Span, + destination, + RuleSet, expectedTag, - encodingType, - destination.AsSpan(), + out int consumed, 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); + 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. /// - /// The tag to check for before reading. /// - /// A corresponding to the value type to process. + /// 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. + /// /// - /// true and advances the reader if had sufficient + /// and advances the reader if had sufficient /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 @@ -988,92 +705,59 @@ public bool TryCopyCharacterString( /// . is not the same as /// . /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, + /// + /// + /// + public bool TryReadCharacterString( 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) + out int charsWritten, + Asn1Tag? expectedTag = null) { - return TryCopyCharacterString( - new Asn1Tag(encodingType), + bool ret = AsnDecoder.TryReadCharacterString( + _data.Span, + destination, + RuleSet, encodingType, - destination.AsSpan(), - out charsWritten); + 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, copying the decoded value into a provided destination buffer. + /// encoding type, returning the decoded value as a string. /// - /// The tag to check for before reading. /// - /// A corresponding to the value type to process. + /// 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. /// /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. + /// 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 + /// + /// 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 @@ -1081,82 +765,19 @@ public bool TryCopyCharacterString( /// . is not the same as /// . /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int charsWritten) + /// + /// + /// + public string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = null) { - return TryCopyCharacterString( - expectedTag, + string ret = AsnDecoder.ReadCharacterString( + _data.Span, + RuleSet, encodingType, - destination.AsSpan(), - out charsWritten); - } + out int consumed, + expectedTag); - /// - /// 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); + _data = _data.Slice(consumed); return ret; } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs index 4760ebf6944707..adf1781ace9d38 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.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 index 81bcbfd27d50e2..6a3f34bb26a439 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs @@ -5,12 +5,12 @@ using System.Buffers.Text; using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// - /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. + /// Provides stateless methods for decoding BER-, CER-, or DER-encoded ASN.1 data. /// - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { // T-REC-X.690-201508 sec 9.2 internal const int MaxCERSegmentSize = 1000; @@ -18,163 +18,227 @@ internal ref partial struct AsnValueReader // 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. + /// Attempts locate the contents range for the encoded value at the beginning of the + /// buffer using the specified encoding rules. /// - public bool HasData => !_data.IsEmpty; - - /// - /// Construct an over with a given ruleset. - /// - /// The data to read. - /// The encoding constraints for the reader. + /// 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 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. + /// + /// 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 AsnValueReader(ReadOnlySpan data, AsnEncodingRules ruleSet) + public static bool TryReadEncodedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out Asn1Tag tag, + out int contentOffset, + out int contentLength, + out int bytesConsumed) { CheckEncodingRules(ruleSet); - _data = data; - RuleSet = ruleSet; - } - - // Reversed parameter order, to be used by OpenUnchecked. - private AsnValueReader(AsnEncodingRules ruleSet, ReadOnlySpan data) - { - _data = data; - RuleSet = 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; - internal static AsnValueReader OpenUnchecked(ReadOnlySpan data, AsnEncodingRules ruleSet) - { - return new AsnValueReader(ruleSet, data); - } + LengthValidity validity = ValidateLength( + source.Slice(headerLength), + ruleSet, + localTag, + encodedLength, + out int len, + out int consumed); - /// - /// 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); + if (validity == LengthValidity.Valid) + { + tag = localTag; + contentOffset = headerLength; + contentLength = len; + bytesConsumed = headerLength + consumed; + return true; + } } + + tag = default; + contentOffset = contentLength = bytesConsumed = 0; + return false; } /// - /// Read the encoded tag at the next data position, without advancing the reader. + /// 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 decoded value. + /// The tag identifying the content. /// - /// - /// a tag could not be decoded at the reader's current position. + /// + /// + /// 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 Asn1Tag PeekTag() + /// + /// 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) { - if (Asn1Tag.TryDecode(_data, out Asn1Tag tag, out int bytesRead)) + 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 new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw GetValidityException(validity); } - /// - /// 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() + private static ReadOnlySpan GetPrimitiveContentSpan( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out int bytesConsumed) { - return Slice(_data, 0, GetNextEncodedValueLength()); - } + CheckEncodingRules(ruleSet); - internal int GetNextEncodedValueLength() - { - ReadTagAndLength(out int? length, out int bytesRead); + Asn1Tag localTag = Asn1Tag.Decode(source, out int tagLength); + int? encodedLength = ReadLength(source.Slice(tagLength), ruleSet, out int lengthLength); + int headerLength = tagLength + lengthLength; - if (length == null) + // 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) { - int contentsLength = SeekEndOfContents(_data.Slice(bytesRead)); - return bytesRead + contentsLength + AsnReader.EndOfContentsEncodedLength; + throw new AsnContentException( + SR.Format(SR.ContentException_PrimitiveEncodingRequired, tagNumber)); } - return bytesRead + length.Value; + if (encodedLength == null) + { + throw new AsnContentException(); + } + + ReadOnlySpan ret = Slice(source, headerLength, encodedLength.Value); + bytesConsumed = headerLength + ret.Length; + return ret; } - /// - /// 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() + private static bool TryReadLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? length, + out int bytesRead) { - (int offset, int length) = GetNextContentRange(); - return Slice(_data, offset, length); + return DecodeLength(source, ruleSet, out length, out bytesRead) == LengthDecodeStatus.Success; } - internal (int Offset, int Length) GetNextContentRange() + private static int? ReadLength(ReadOnlySpan source, AsnEncodingRules ruleSet, out int bytesConsumed) { - ReadTagAndLength(out int? length, out int bytesRead); + LengthDecodeStatus status = DecodeLength(source, ruleSet, out int? length, out bytesConsumed); - if (length == null) + switch (status) { - return (bytesRead, SeekEndOfContents(_data.Slice(bytesRead))); + 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; } - - 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( + private static LengthDecodeStatus DecodeLength( ReadOnlySpan source, AsnEncodingRules ruleSet, out int? length, @@ -187,7 +251,7 @@ private static bool TryReadLength( if (source.IsEmpty) { - return false; + return LengthDecodeStatus.NeedMoreData; } // T-REC-X.690-201508 sec 8.1.3 @@ -206,23 +270,23 @@ private static bool TryReadLength( if (ruleSet == AsnEncodingRules.DER) { bytesRead = 0; - return false; + return LengthDecodeStatus.DerIndefinite; } // Null length == indefinite. - return true; + return LengthDecodeStatus.Success; } if (lengthOrLengthLength < MultiByteMarker) { length = lengthOrLengthLength; - return true; + return LengthDecodeStatus.Success; } if (lengthOrLengthLength == 0xFF) { bytesRead = 0; - return false; + return LengthDecodeStatus.ReservedValue; } byte lengthLength = (byte)(lengthOrLengthLength & ~MultiByteMarker); @@ -231,7 +295,7 @@ private static bool TryReadLength( if (lengthLength + 1 > source.Length) { bytesRead = 0; - return false; + return LengthDecodeStatus.NeedMoreData; } // T-REC-X.690-201508 sec 9.1 (CER: Length forms) @@ -239,13 +303,13 @@ private static bool TryReadLength( bool minimalRepresentation = ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER; - // The ITU-T specifications tecnically allow lengths up to ((2^128) - 1), but + // 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 false; + return LengthDecodeStatus.LengthTooBig; } uint parsedLength = 0; @@ -260,7 +324,7 @@ private static bool TryReadLength( if (minimalRepresentation && current == 0) { bytesRead = 0; - return false; + return LengthDecodeStatus.LaxEncodingProhibited; } if (!minimalRepresentation && current != 0) @@ -272,7 +336,7 @@ private static bool TryReadLength( if (lengthLength - i > sizeof(int)) { bytesRead = 0; - return false; + return LengthDecodeStatus.LengthTooBig; } } } @@ -285,47 +349,48 @@ private static bool TryReadLength( if (parsedLength > int.MaxValue) { bytesRead = 0; - return false; + return LengthDecodeStatus.LengthTooBig; } if (minimalRepresentation && parsedLength < MultiByteMarker) { bytesRead = 0; - return false; + return LengthDecodeStatus.LaxEncodingProhibited; } Debug.Assert(bytesRead > 0); length = (int)parsedLength; - return true; + return LengthDecodeStatus.Success; } - internal Asn1Tag ReadTagAndLength(out int? contentsLength, out int bytesRead) + private static Asn1Tag ReadTagAndLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + 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; + Asn1Tag tag = Asn1Tag.Decode(source, out int tagBytesRead); + int? length = ReadLength(source.Slice(tagBytesRead), ruleSet, out int 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) + 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) { - // 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); + throw GetValidityException(LengthValidity.CerRequiresIndefinite); } - - bytesRead = allBytesRead; - contentsLength = length; - return tag; + } + 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); } - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + bytesRead = allBytesRead; + contentsLength = length; + return tag; } private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLength) @@ -333,38 +398,93 @@ private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLe // 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); + throw new AsnContentException(); } } - /// - /// Get the number of bytes between the start of and - /// the End-of-Contents marker - /// - private int SeekEndOfContents(ReadOnlySpan source) + private static LengthValidity ValidateLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag localTag, + int? encodedLength, + out int actualLength, + out int bytesConsumed) { - return SeekEndOfContents(source, RuleSet); + 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 /// - internal static int SeekEndOfContents(ReadOnlySpan source, AsnEncodingRules ruleSet) + private 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) + while (!cur.IsEmpty) { - Asn1Tag tag = tmpReader.ReadTagAndLength(out int? length, out int bytesRead); + Asn1Tag tag = ReadTagAndLength(cur, ruleSet, out int? length, out int bytesRead); if (tag == Asn1Tag.EndOfContents) { @@ -387,21 +507,21 @@ internal static int SeekEndOfContents(ReadOnlySpan source, AsnEncodingRule if (length == null) { depth++; - tmpReader._data = tmpReader._data.Slice(bytesRead); + cur = cur.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); + // 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. - tmpReader._data = tmpReader._data.Slice(tlv.Length); + cur = cur.Slice(tlv.Length); totalLen += tlv.Length; } } - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan data, int bytesToRead) @@ -412,7 +532,7 @@ private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan data, int return value; } - internal static int ParseNonNegativeInt(ReadOnlySpan data) + private static int ParseNonNegativeInt(ReadOnlySpan data) { if (Utf8Parser.TryParse(data, out uint value, out int consumed) && value <= int.MaxValue && @@ -421,7 +541,7 @@ internal static int ParseNonNegativeInt(ReadOnlySpan data) return (int)value; } - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } private static ReadOnlySpan SliceAtMost(ReadOnlySpan source, int longestPermitted) @@ -436,7 +556,7 @@ private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, i if (length < 0 || source.Length - offset < length) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_LengthExceedsPayload); } return source.Slice(offset, length); @@ -455,7 +575,7 @@ private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, i if (lengthVal < 0 || source.Length - offset < lengthVal) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_LengthExceedsPayload); } return source.Slice(offset, lengthVal); @@ -474,18 +594,7 @@ internal static ReadOnlyMemory Slice(ReadOnlyMemory bigger, ReadOnly } 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); + throw new AsnContentException(); } [Conditional("DEBUG")] @@ -504,40 +613,71 @@ internal static void CheckEncodingRules(AsnEncodingRules ruleSet) } } - internal static void CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber) + private 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, + SR.Argument_UniversalValueIsFixed, nameof(expectedTag)); } if (expectedTag.TagClass != tag.TagClass || expectedTag.TagValue != tag.TagValue) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + 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. /// - internal partial class AsnReader + public partial class AsnReader { - internal const int MaxCERSegmentSize = AsnValueReader.MaxCERSegmentSize; - internal const int EndOfContentsEncodedLength = AsnValueReader.EndOfContentsEncodedLength; + internal const int MaxCERSegmentSize = AsnDecoder.MaxCERSegmentSize; private ReadOnlyMemory _data; + private readonly AsnReaderOptions _options; /// - /// The in use by this reader. + /// Gets the encoding rules in use by this reader. /// + /// + /// The encoding rules in use by this reader. + /// public AsnEncodingRules RuleSet { get; } /// - /// An indication of whether or not the reader has remaining data available to process. + /// 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; /// @@ -545,6 +685,7 @@ internal partial class AsnReader /// /// 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. @@ -555,17 +696,18 @@ internal partial class AsnReader /// /// is not defined. /// - public AsnReader(ReadOnlyMemory data, AsnEncodingRules ruleSet) + public AsnReader(ReadOnlyMemory data, AsnEncodingRules ruleSet, AsnReaderOptions options = default) { - AsnValueReader.CheckEncodingRules(ruleSet); + AsnDecoder.CheckEncodingRules(ruleSet); _data = data; RuleSet = ruleSet; + _options = options; } /// - /// Throws a standardized if the reader has remaining - /// data, performs no function if returns false. + /// 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 @@ -575,7 +717,7 @@ public void ThrowIfNotEmpty() { if (HasData) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(SR.ContentException_TooMuchData); } } @@ -583,24 +725,14 @@ public void ThrowIfNotEmpty() /// Read the encoded tag at the next data position, without advancing the reader. /// /// - /// The decoded value. + /// The decoded tag 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); + return Asn1Tag.Decode(_data.Span, out _); } /// @@ -608,8 +740,10 @@ private AsnValueReader OpenValueReader() /// advancing the reader. For indefinite length encodings this includes the /// End of Contents marker. /// - /// A view of the next encoded value. - /// + /// + /// 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. /// @@ -617,8 +751,8 @@ private AsnValueReader OpenValueReader() /// public ReadOnlyMemory PeekEncodedValue() { - AsnValueReader reader = OpenValueReader(); - return Slice(_data, 0, reader.GetNextEncodedValueLength()); + AsnDecoder.ReadEncodedValue(_data.Span, RuleSet, out _, out _, out int bytesConsumed); + return _data.Slice(0, bytesConsumed); } /// @@ -626,18 +760,23 @@ public ReadOnlyMemory PeekEncodedValue() /// next encoded value without advancing the reader. /// /// - /// A view of the contents octets of the next encoded value. + /// 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() { - AsnValueReader reader = OpenValueReader(); - (int offset, int length) = reader.GetNextContentRange(); - return Slice(_data, offset, length); + AsnDecoder.ReadEncodedValue( + _data.Span, + RuleSet, + out int contentOffset, + out int contentLength, + out _); + + return _data.Slice(contentOffset, contentLength); } /// @@ -645,7 +784,9 @@ public ReadOnlyMemory PeekContentBytes() /// 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. + /// + /// A view of the next encoded value. + /// /// public ReadOnlyMemory ReadEncodedValue() { @@ -654,22 +795,9 @@ public ReadOnlyMemory ReadEncodedValue() 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) + private AsnReader CloneAtSlice(int start, 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); + return new AsnReader(_data.Slice(start, length), RuleSet, _options); } } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs index 50628400f5e564..c0d87eb5678394 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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 index 64eb1102611c10..1be9f1eddcef7d 100644 --- 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 @@ -2,64 +2,44 @@ // 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 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public 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 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 + /// 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] - /// - /// - /// 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]. /// - /// The writer has been Disposed. - public void WriteBitString(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount = 0) + public void WriteBitString(ReadOnlySpan value, int unusedBitCount = 0, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.BitString); // Primitive or constructed, doesn't matter. - WriteBitStringCore(tag, bitString, unusedBitCount); + WriteBitStringCore(tag ?? Asn1Tag.PrimitiveBitString, value, unusedBitCount); } // T-REC-X.690-201508 sec 8.6 @@ -71,15 +51,13 @@ private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int u throw new ArgumentOutOfRangeException( nameof(unusedBitCount), unusedBitCount, - SR.Cryptography_Asn_UnusedBitCountRange); + SR.Argument_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); + throw new ArgumentException(SR.Argument_UnusedBitCountMustBeZero, nameof(unusedBitCount)); } byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1]; @@ -91,8 +69,7 @@ private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int u // BER for now. if (!CheckValidLastByte(lastByte, unusedBitCount)) { - // TODO: Probably warrants a distinct message. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new ArgumentException(SR.Argument_UnusedBitWasSet, nameof(unusedBitCount)); } if (RuleSet == AsnEncodingRules.CER) @@ -118,173 +95,6 @@ private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int u _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. diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs index f65a8a7eef2b1d..9197b9095359ad 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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 index 695c958ccdeb0d..abf7e77be9f94c 100644 --- 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 @@ -2,79 +2,41 @@ // 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 sealed partial class AsnWriter + public sealed partial class AsnWriter { /// /// Write a non-[] enum value as an Enumerated with /// tag UNIVERSAL 10. /// - /// The boxed enumeration value to write + /// The boxed enumeration value to write. + /// The tag to write, or for the default tag (Universal 10). /// - /// 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 . /// /// /// . is /// , but /// . is not correct for - /// the method --OR-- - /// is not a boxed enum value --OR-- - /// the unboxed type of is declared [] + /// 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) + /// + /// + public void WriteEnumeratedValue(Enum value, Asn1Tag? tag = null) { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - WriteEnumeratedValue(tag.AsPrimitive(), enumValue.GetType(), enumValue); + WriteEnumeratedValue(tag?.AsPrimitive() ?? Asn1Tag.Enumerated, value.GetType(), value); } /// @@ -82,28 +44,32 @@ public void WriteEnumeratedValue(Asn1Tag tag, object enumValue) /// tag UNIVERSAL 10. /// /// The tag to write. - /// The boxed enumeration value to write. + /// The boxed enumeration value to write. /// - /// is null + /// is . /// /// /// . is /// , but /// . is not correct for - /// the method --OR-- - /// is not an enum --OR-- - /// is declared [] + /// 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 + /// + public void WriteEnumeratedValue(TEnum value, Asn1Tag? tag = null) where TEnum : Enum { - WriteEnumeratedValue(tag.AsPrimitive(), typeof(TEnum), enumValue); + WriteEnumeratedValue(tag?.AsPrimitive() ?? Asn1Tag.Enumerated, typeof(TEnum), value); } // T-REC-X.690-201508 sec 8.4 - private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object enumValue) + private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object value) { CheckUniversalTag(tag, UniversalTagNumber.Enumerated); @@ -112,20 +78,20 @@ private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object enumValue) if (tEnum.IsDefined(typeof(FlagsAttribute), false)) { throw new ArgumentException( - SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, + SR.Argument_EnumeratedValueRequiresNonFlagsEnum, nameof(tEnum)); } if (backingType == typeof(ulong)) { - ulong numericValue = Convert.ToUInt64(enumValue); + 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(enumValue); + long numericValue = Convert.ToInt64(value); // T-REC-X.690-201508 sec 8.4 WriteIntegerCore(tag, numericValue); } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs index d0600d28213c5c..4fc3c6ef887458 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs index b8f436ba16d810..88444bd31c7446 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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 index 76c7d0d263c827..9008475748bcb7 100644 --- 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 @@ -2,107 +2,112 @@ // 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.Collections; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// /// Write a [] enum value as a NamedBitList with - /// tag UNIVERSAL 3. + /// a specified tag. /// - /// The boxed enumeration value to write - /// - /// is null - /// + /// The boxed enumeration value to write + /// The tag to write, or for the default tag (Universal 3). /// - /// is not a boxed enum value --OR-- - /// the unboxed type of is not declared [] + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not a boxed enum value. + /// + /// -or- + /// + /// the unboxed type of is not declared []. + /// + /// + /// is . /// - /// The writer has been Disposed. - /// - /// - public void WriteNamedBitList(object enumValue) + public void WriteNamedBitList(Enum value, Asn1Tag? tag = null) { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); - } + CheckUniversalTag(tag, UniversalTagNumber.BitString); - /// - /// 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); + WriteNamedBitList(tag, value.GetType(), value); } /// /// Write a [] enum value as a NamedBitList with /// a specified tag. /// - /// The tag to write. - /// The boxed enumeration value to write + /// 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 a boxed enum value --OR-- - /// the unboxed type of is not declared [] - /// - /// - /// is null + /// the method. + /// + /// -or- + /// + /// is not an enum value. + /// + /// -or- + /// + /// is not declared []. /// - /// The writer has been Disposed. - public void WriteNamedBitList(Asn1Tag tag, object enumValue) + public void WriteNamedBitList(TEnum value, Asn1Tag? tag = null) where TEnum : Enum { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - CheckUniversalTag(tag, UniversalTagNumber.BitString); - WriteNamedBitList(tag, enumValue.GetType(), enumValue); + WriteNamedBitList(tag, typeof(TEnum), value); } /// - /// Write a [] enum value as a NamedBitList with - /// a specified tag. + /// Write a bit array value as a NamedBitList with a specified tag. /// - /// The tag to write. - /// The enumeration value to write + /// The bits 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 [] + /// the method. /// - /// The writer has been Disposed. - public void WriteNamedBitList(Asn1Tag tag, TEnum enumValue) where TEnum : struct + /// + /// 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); - WriteNamedBitList(tag, typeof(TEnum), enumValue); + WriteBitArray(value, tag); } - private void WriteNamedBitList(Asn1Tag tag, Type tEnum, object enumValue) + private void WriteNamedBitList(Asn1Tag? tag, Type tEnum, Enum value) { Type backingType = tEnum.GetEnumUnderlyingType(); if (!tEnum.IsDefined(typeof(FlagsAttribute), false)) { throw new ArgumentException( - SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, + SR.Argument_NamedBitListRequiresFlagsEnum, nameof(tEnum)); } @@ -110,12 +115,12 @@ private void WriteNamedBitList(Asn1Tag tag, Type tEnum, object enumValue) if (backingType == typeof(ulong)) { - integralValue = Convert.ToUInt64(enumValue); + integralValue = Convert.ToUInt64(value); } else { // All other types fit in a (signed) long. - long numericValue = Convert.ToInt64(enumValue); + long numericValue = Convert.ToInt64(value); integralValue = unchecked((ulong)numericValue); } @@ -124,7 +129,7 @@ private void WriteNamedBitList(Asn1Tag tag, Type tEnum, object enumValue) // 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) + 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. @@ -145,7 +150,7 @@ private void WriteNamedBitList(Asn1Tag tag, ulong integralValue) { // No bits were set; this is an empty bit string. // T-REC-X.690-201508 sec 11.2.2-note2 - WriteBitString(tag, ReadOnlySpan.Empty); + WriteBitString(ReadOnlySpan.Empty, tag: tag); } else { @@ -159,10 +164,36 @@ private void WriteNamedBitList(Asn1Tag tag, ulong integralValue) int unusedBitCount = 7 - (indexOfHighestSetBit % 8); WriteBitString( - tag, temp.Slice(0, byteLen), - unusedBitCount); + 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/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs index de2bc92e5237d9..dbe3670a53ecc1 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs index 1dc9b0ac0364d2..e65053b3988c05 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs index 6363c76b8d40a5..2ce7d540339e2f 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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 index 78d4268233921e..885ea205289abc 100644 --- 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 @@ -2,91 +2,69 @@ // 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 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. + /// 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 + /// the method. /// - /// The writer has been Disposed. - /// - public void PushSequence(Asn1Tag tag) + /// + public Scope PushSequence(Asn1Tag? tag = null) { 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); + 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. + /// The tag to write, or for the default tag (Universal 16). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// - /// the writer is not currently positioned within a Sequence with the specified tag + /// the writer is not currently positioned within a Sequence with the specified tag. /// - /// The writer has been Disposed. - /// - /// - public void PopSequence(Asn1Tag 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()); + PopSequenceCore(tag?.AsConstructed() ?? Asn1Tag.Sequence); } // T-REC-X.690-201508 sec 8.9, 8.10 - private void PushSequenceCore(Asn1Tag tag) + private Scope PushSequenceCore(Asn1Tag tag) { - PushTag(tag.AsConstructed(), UniversalTagNumber.Sequence); + 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/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs index d1add9cb5f75da..33f12962da95bf 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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 index 83cb6e8b140414..e791fb4ac99719 100644 --- 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 @@ -4,196 +4,126 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security.Cryptography; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public 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. + /// One of the enumeration values representing the encoding to use. /// - /// The string to write. - /// is null + /// 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 not a restricted character string encoding type. + /// + /// -or- + /// /// is a restricted character string encoding type that is not - /// currently supported by this method + /// currently supported by this method. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, string str) + public void WriteCharacterString(UniversalTagNumber encodingType, string value, Asn1Tag? tag = null) { - if (str == null) - throw new ArgumentNullException(nameof(str)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - WriteCharacterString(tag, encodingType, str.AsSpan()); + WriteCharacterString(encodingType, value.AsSpan(), tag); } /// /// 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. + /// 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 not a restricted character string encoding type. + /// + /// -or- + /// /// is a restricted character string encoding type that is not - /// currently supported by this method + /// currently supported by this method. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, ReadOnlySpan str) + public void WriteCharacterString(UniversalTagNumber encodingType, ReadOnlySpan str, Asn1Tag? tag = null) { CheckUniversalTag(tag, encodingType); Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - WriteCharacterStringCore(tag, encoding, str); + 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 = -1; + int size = encoding.GetByteCount(str); // T-REC-X.690-201508 sec 9.2 if (RuleSet == AsnEncodingRules.CER) { - // TODO: Split this for netstandard vs netcoreapp for span?. - unsafe + // If it exceeds the primitive segment size, use the constructed encoding. + if (size > AsnReader.MaxCERSegmentSize) { - 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; - } - } + 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); + // Clear the constructed tag, if present. + WriteTag(tag.AsPrimitive()); + WriteLength(size); + Span dest = _buffer.AsSpan(_offset, size); - if (written != size) - { - Debug.Fail($"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); - throw new InvalidOperationException(); - } - } + int written = encoding.GetBytes(str, dest); - _offset += size; - } + 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; + byte[] tmp = CryptoPool.Rent(size); + int written = encoding.GetBytes(str, tmp); - unsafe + if (written != size) { - 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(); - } - } - } + Debug.Fail( + $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); } WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size)); diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs index 60c1d71cbd603f..429c41b4c64714 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs index e9c7af34eb0308..dcd7ead4d363eb 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs index ad9a9b8f649fa1..3f33f2b95eeb95 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs index 87b7249a2ae318..81cb7c8c33c3ff 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs index c5c2ad1f5e04cc..8709f1ce8c90d2 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/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.Formats.Asn1/tests/Reader/Asn1ReaderTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/Asn1ReaderTests.cs deleted file mode 100644 index a609c6661e195d..00000000000000 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs index 2291e69b11dccc..7640b13aec50fe 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ParseTag.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs index 15e5c69786740b..5042a4cc0419f4 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/PeekTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs index 1ed43701da4b18..4ccb24c3506a88 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ReadBMPString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs index 48afbb1c7e79a8..751802f61ad5b2 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ReadBitString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs index 6282bbd21f903e..43667481bf5e4b 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index 02d97d4b2dfb50..75eab9e395c18b 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs @@ -2,38 +2,37 @@ // 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 ReadBoolean : Asn1ReaderTests + public sealed class ReadBoolean { [Theory] - [InlineData(PublicEncodingRules.BER, false, 3, "010100")] - [InlineData(PublicEncodingRules.BER, true, 3, "010101")] + [InlineData(AsnEncodingRules.BER, false, 3, "010100")] + [InlineData(AsnEncodingRules.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")] + [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(PublicEncodingRules.DER, true, 3, "8001FF0500")] + [InlineData(AsnEncodingRules.DER, true, 3, "8001FF0500")] // Application 31 - [InlineData(PublicEncodingRules.DER, true, 4, "5F1F01FF0500")] + [InlineData(AsnEncodingRules.DER, true, 4, "5F1F01FF0500")] // Private 253 - [InlineData(PublicEncodingRules.CER, false, 5, "DF817D01000500")] + [InlineData(AsnEncodingRules.CER, false, 5, "DF817D01000500")] public static void ReadBoolean_Success( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, bool expectedValue, int expectedBytesRead, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Asn1Tag tag = reader.PeekTag(); bool value; @@ -67,13 +66,13 @@ public static void ReadBoolean_Success( } [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 = { 1, 1, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -81,7 +80,8 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -91,13 +91,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 = { 0x80, 1, 0xFF }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -105,15 +105,17 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadBoolean()); + Assert.Throws(() => reader.ReadBoolean()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.Application, 0))); + 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.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -123,73 +125,76 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - bool val1 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, true)); + AsnReader reader = new AsnReader(inputData, ruleSet); + bool val1 = reader.ReadBoolean(new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - bool val2 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, false)); + 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", 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")] + [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, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Asn1Tag tag = default(Asn1Tag); if (inputData.Length > 0) @@ -199,11 +204,11 @@ public static void ReadBoolean_Failure( if (tag.TagClass == TagClass.Universal) { - Assert.Throws(() => reader.ReadBoolean()); + Assert.Throws(() => reader.ReadBoolean()); } else { - Assert.Throws(() => reader.ReadBoolean(tag)); + Assert.Throws(() => reader.ReadBoolean(tag)); } if (inputData.Length == 0) diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs index a2966ad2c32e4c..d19fbe618f05e9 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs @@ -3,13 +3,12 @@ // 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 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadEnumerated : Asn1ReaderTests + public sealed class ReadEnumerated { public enum ByteBacked : byte { @@ -65,30 +64,30 @@ public enum ULongBacked : ulong } private static void GetExpectedValue( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, TEnum expectedValue, string inputHex) - where TEnum : struct + where TEnum : Enum { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ByteBacked expectedValue, string inputHex) { @@ -96,18 +95,18 @@ public static void GetExpectedValue_ByteBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, SByteBacked expectedValue, string inputHex) { @@ -115,18 +114,18 @@ public static void GetExpectedValue_SByteBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ShortBacked expectedValue, string inputHex) { @@ -134,19 +133,19 @@ public static void GetExpectedValue_ShortBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, UShortBacked expectedValue, string inputHex) { @@ -154,18 +153,18 @@ public static void GetExpectedValue_UShortBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, IntBacked expectedValue, string inputHex) { @@ -173,19 +172,19 @@ public static void GetExpectedValue_IntBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, UIntBacked expectedValue, string inputHex) { @@ -193,18 +192,18 @@ public static void GetExpectedValue_UIntBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, LongBacked expectedValue, string inputHex) { @@ -212,20 +211,20 @@ public static void GetExpectedValue_LongBacked( } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ULongBacked expectedValue, string inputHex) { @@ -233,411 +232,427 @@ public static void GetExpectedValue_ULongBacked( } [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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + Assert.Throws(() => reader.ReadEnumeratedValue()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedValue_NonEnumType(PublicEncodingRules ruleSet) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedValue()); + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedValue_FlagsEnum(PublicEncodingRules ruleSet) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); AssertExtensions.Throws( - "tEnum", + "enumType", () => reader.ReadEnumeratedValue()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedBytes(PublicEncodingRules ruleSet) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); ReadOnlyMemory contents = reader.ReadEnumeratedBytes(); Assert.Equal(0x10, contents.Length); @@ -645,43 +660,43 @@ public static void ReadEnumeratedBytes(PublicEncodingRules ruleSet) } [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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadEnumeratedBytes()); + Assert.Throws(() => reader.ReadEnumeratedBytes()); } [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 = { 0x0A, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -689,7 +704,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -700,13 +715,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, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -714,16 +729,16 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadEnumeratedValue()); + Assert.Throws(() => reader.ReadEnumeratedValue()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.Application, 0))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -734,27 +749,54 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + 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, (AsnEncodingRules)ruleSet); - ShortBacked val1 = reader.ReadEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, true)); + + AsnReader reader = new AsnReader(inputData, ruleSet); + ShortBacked val1 = reader.ReadEnumeratedValue(constructedTag); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ShortBacked val2 = reader.ReadEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, false)); + + 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.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs index 294e55ea2f9750..86abaf6f1be9a1 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ReadIA5String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs index 9fe1323f8b5d11..ae7c6ededa53d7 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index 37d58824c572ff..351455ec3ab01d 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs @@ -3,220 +3,62 @@ // 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 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadInteger : Asn1ReaderTests + public sealed class ReadInteger { [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")] + [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, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); - Assert.Throws(() => reader.ReadIntegerBytes()); + 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))] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, int expectedValue) { byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadInt32(out int value); @@ -225,15 +67,15 @@ public static void ReadInt32_Success( } [Theory] - [InlineData(PublicEncodingRules.BER, "02050102030405")] - [InlineData(PublicEncodingRules.CER, "02050102030405")] - [InlineData(PublicEncodingRules.DER, "02050102030405")] + [InlineData(AsnEncodingRules.BER, "02050102030405")] + [InlineData(AsnEncodingRules.CER, "02050102030405")] + [InlineData(AsnEncodingRules.DER, "02050102030405")] public static void ReadInt32_TooMuchData( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadInt32(out int value); @@ -242,22 +84,22 @@ public static void ReadInt32_TooMuchData( } [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(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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, uint expectedValue) { byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadUInt32(out uint value); @@ -266,20 +108,20 @@ public static void ReadUInt32_Success( } [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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadUInt32(out uint value); @@ -288,39 +130,39 @@ public static void ReadUInt32_Failure(PublicEncodingRules ruleSet, string inputH } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, long expectedValue) { byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadInt64(out long value); @@ -329,15 +171,15 @@ public static void ReadInt64_Success( } [Theory] - [InlineData(PublicEncodingRules.BER, "0209010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0209010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.BER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0209010203040506070809")] public static void ReadInt64_TooMuchData( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadInt64(out long value); @@ -346,30 +188,30 @@ public static void ReadInt64_TooMuchData( } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, ulong expectedValue) { byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadUInt64(out ulong value); @@ -378,24 +220,24 @@ public static void ReadUInt64_Success( } [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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); bool didRead = reader.TryReadUInt64(out ulong value); @@ -418,10 +260,10 @@ public static void ReadIntegerBytes() } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetBigInteger(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetBigInteger(AsnEncodingRules ruleSet) { byte[] inputData = ( "0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + @@ -446,15 +288,15 @@ public static void GetBigInteger(PublicEncodingRules ruleSet) "1367336140572971505716740825623220507359429297584634909330541150" + "79473593821332264673455059897928082590541"); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.Equal(expected, reader.ReadInteger()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetNegativeBigInteger(PublicEncodingRules ruleSet) + [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. @@ -484,15 +326,15 @@ public static void GetNegativeBigInteger(PublicEncodingRules ruleSet) "6924486017215979427815833587119797658480104671279234402026468966" + "86109928634475300812601680679147599027"); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.Equal(expected, reader.ReadInteger()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetDiminutiveBigInteger(PublicEncodingRules ruleSet) + [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. @@ -519,18 +361,18 @@ public static void GetDiminutiveBigInteger(PublicEncodingRules ruleSet) "1367336140572971505716740825623220507359429297584634909330541150" + "79473593821332264673455059897928082590541") >> 8; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.Equal(expected, reader.ReadInteger()); } [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 = { 2, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -538,7 +380,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -548,13 +390,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, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -562,15 +404,15 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadIntegerBytes()); + Assert.Throws(() => reader.ReadIntegerBytes()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.Application, 0))); + 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.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -580,24 +422,24 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ReadOnlyMemory val1 = reader.ReadIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, true)); + AsnReader reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory val1 = reader.ReadIntegerBytes(new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ReadOnlyMemory val2 = reader.ReadIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, false)); + 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 index de04782d5a6301..c0a8587912ac10 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs @@ -3,26 +3,23 @@ // 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 +namespace System.Formats.Asn1.Tests.Reader { - internal static class AsnReaderExtensions + public sealed class ReadLength { - private delegate Asn1Tag ReadTagAndLengthDelegate(out int? parsedLength, out int bytesRead); + private delegate Asn1Tag ReadTagAndLengthDelegate( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + 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); - } - } + private static ReadTagAndLengthDelegate ReadTagAndLength = (ReadTagAndLengthDelegate) + typeof(AsnDecoder).GetMethod("ReadTagAndLength", BindingFlags.Static | BindingFlags.NonPublic) + .CreateDelegate(typeof(ReadTagAndLengthDelegate)); - public sealed class ReadLength : Asn1ReaderTests - { [Theory] [InlineData(4, 0, "0400")] [InlineData(1, 1, "0101")] @@ -35,19 +32,14 @@ public static void MinimalPrimitiveLength(int tagValue, int length, string input { byte[] inputBytes = inputHex.HexToByteArray(); - foreach (PublicEncodingRules rules in Enum.GetValues(typeof(PublicEncodingRules))) + foreach (AsnEncodingRules rules in Enum.GetValues(typeof(AsnEncodingRules))) { - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)rules); - - Asn1Tag tag = reader.ReadTagAndLength(out int ? parsedLength, out int bytesRead); + 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); - - // ReadTagAndLength doesn't move the _data span forward. - Assert.True(reader.HasData, "reader.HasData"); } } @@ -72,62 +64,67 @@ public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) public static void ReadWithInsufficientData(string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.DER); - Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); + Assert.Throws( + () => ReadTagAndLength(inputData, AsnEncodingRules.DER, 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")] + [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, - PublicEncodingRules rules, + AsnEncodingRules rules, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)rules); + AsnReader reader = new AsnReader(inputData, rules); - Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); + Assert.Throws( + () => ReadTagAndLength(inputData, rules, out _, out _)); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void IndefiniteLength(PublicEncodingRules ruleSet) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(data, ruleSet); - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); + Asn1Tag tag = ReadTagAndLength( + data, + ruleSet, + out int? length, + out int bytesRead); Assert.Equal(2, bytesRead); Assert.False(length.HasValue, "length.HasValue"); @@ -144,7 +141,11 @@ 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); + Asn1Tag tag = ReadTagAndLength( + inputData, + AsnEncodingRules.BER, + out int? length, + out int bytesRead); Assert.Equal(inputData.Length, bytesRead); Assert.Equal(expectedLength, length.Value); @@ -153,20 +154,23 @@ public static void BerNonMinimalLength(int expectedLength, string inputHex) } [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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules 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); + Asn1Tag tag = ReadTagAndLength( + inputData, + ruleSet, + out int? length, + out int bytesRead); Assert.Equal(expectedBytesRead, bytesRead); Assert.Equal(tagValue, tag.TagValue); diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs index b0917c450f277c..4aa9e539f507b4 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs @@ -2,13 +2,15 @@ // 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.Collections; +using System.Linq; +using System.Security.Cryptography.X509Certificates; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadNamedBitList : Asn1ReaderTests + public sealed class ReadNamedBitList { [Flags] public enum X509KeyUsageCSharpStyle @@ -46,62 +48,62 @@ public enum LongFlags : long [Theory] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, typeof(X509KeyUsageCSharpStyle), (long)(X509KeyUsageCSharpStyle.None), "030100")] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, typeof(X509KeyUsageCSharpStyle), (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), "0303070480")] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, typeof(X509KeyUsageCSharpStyle), (long)(X509KeyUsageCSharpStyle.KeyAgreement), "03020308")] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, typeof(LongFlags), (long)(LongFlags.Mid | LongFlags.Max), "0309010000000080000002")] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, typeof(LongFlags), (long)(LongFlags.Mid | LongFlags.Min), "0309000000000080000001")] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, typeof(LongFlags), (long)(LongFlags.Min | LongFlags.Max), "0309000000000000000003")] // BER: Unused bits are unmapped, regardless of value. [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, typeof(X509KeyUsageCSharpStyle), (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), "030307048F")] // BER: Trailing zeros are permitted. [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.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, + AsnEncodingRules.BER, typeof(X509KeyUsageCSharpStyle), (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), "0303001480")] public static void VerifyReadNamedBitListEncodings( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, Type enumType, long enumValue, string inputHex) { byte[] inputBytes = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputBytes, ruleSet); Enum readValue = reader.ReadNamedBitListValue(enumType); Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); @@ -109,42 +111,42 @@ public static void VerifyReadNamedBitListEncodings( [Theory] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, typeof(ULongFlags), (ulong)(ULongFlags.Mid | ULongFlags.Max), "0309000000000080000001")] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, typeof(ULongFlags), (ulong)(ULongFlags.Min | ULongFlags.Mid), "0306078000000080")] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, typeof(ULongFlags), (ulong)(ULongFlags.Min | ULongFlags.Max), "0309008000000000000001")] public static void VerifyReadNamedBitListEncodings_ULong( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, Type enumType, ulong enumValue, string inputHex) { byte[] inputBytes = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputBytes, 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) + [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(), (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); ULongFlags uLongFlags = reader.ReadNamedBitListValue(); LongFlags longFlags = reader.ReadNamedBitListValue(); @@ -155,75 +157,91 @@ public static void VerifyGenericReadNamedBitList(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_RequiresFlags(PublicEncodingRules ruleSet) + [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(), (AsnEncodingRules)ruleSet); + 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( - "tFlagsEnum", + "flagsEnumType", () => 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) + [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(), (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); - Assert.Throws( + Assert.Throws( () => reader.ReadNamedBitListValue()); Assert.True(reader.HasData, "reader.HasData"); } [Theory] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_ExcessiveBytes(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBytes(AsnEncodingRules ruleSet) { string inputHex = "03050014800000"; - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); - Assert.Throws( + Assert.Throws( () => reader.ReadNamedBitListValue()); Assert.True(reader.HasData, "reader.HasData"); } [Theory] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_ExcessiveBits(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBits(AsnEncodingRules ruleSet) { string inputHex = "0303061480"; - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); - Assert.Throws( + 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -231,7 +249,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -243,13 +261,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, 2, 4 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -257,17 +275,17 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadNamedBitListValue()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.Application, 0))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -280,36 +298,221 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [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)] + [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( - 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.Equal( X509KeyUsageCSharpStyle.DecipherOnly, reader.ReadNamedBitListValue( - new Asn1Tag((TagClass)tagClass, tagValue, true))); + new Asn1Tag(tagClass, tagValue, true))); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.Equal( X509KeyUsageCSharpStyle.DecipherOnly, reader.ReadNamedBitListValue( - new Asn1Tag((TagClass)tagClass, tagValue, false))); + 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 index fd78f55d2c1316..f71e37946f156f 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs @@ -2,63 +2,62 @@ // 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 ReadNull : Asn1ReaderTests + public sealed class ReadNull { [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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, 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) + [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, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadNull()); + Assert.Throws(() => reader.ReadNull()); } [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 = { 5, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -66,7 +65,7 @@ public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -75,13 +74,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, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -89,15 +88,15 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadNull()); + Assert.Throws(() => reader.ReadNull()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.Application, 0))); + 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.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 1))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -106,25 +105,25 @@ public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, true)); + AsnReader reader = new AsnReader(inputData, ruleSet); + reader.ReadNull(new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, false)); + reader = new AsnReader(inputData, ruleSet); + reader.ReadNull(new Asn1Tag(tagClass, tagValue, false)); Assert.False(reader.HasData); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs index 8ff87d12c39050..50a3276e621305 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ReadOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs index 8919d94a363ab6..57bc752006ce2c 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index d87cea376d3e5d..ed43a254b522ed 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs @@ -2,34 +2,33 @@ // 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 ReadSequence : Asn1ReaderTests + public sealed class ReadSequence { [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, bool expectDataRemaining, int expectedSequenceTagNumber) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AsnReader sequence = reader.ReadSequence(); if (expectDataRemaining) @@ -55,44 +54,44 @@ public static void ReadSequence_Success( } [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")] + [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, - 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.ReadSequence()); + Assert.Throws(() => reader.ReadSequence()); } private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) @@ -108,14 +107,14 @@ private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) ReadOnlyMemory publicKeyValue; int unusedBitCount; - if (!spkiReader.TryReadPrimitiveBitStringValue(out unusedBitCount, out publicKeyValue)) + 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.TryCopyBitStringBytes(buf, out unusedBitCount, out int bytesWritten)) + if (spkiReader.TryReadBitString(buf, out unusedBitCount, out int bytesWritten)) { publicKeyValue = new ReadOnlyMemory(buf, 0, bytesWritten); break; @@ -126,15 +125,15 @@ private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) Assert.False(spkiReader.HasData, "spkiReader.HasData after reading subjectPublicKey"); Assert.True(algorithmReader.HasData, "algorithmReader.HasData before reading"); - Oid algorithmOid = algorithmReader.ReadObjectIdentifier(); + string algorithmOid = algorithmReader.ReadObjectIdentifier(); Assert.True(algorithmReader.HasData, "algorithmReader.HasData after reading first OID"); - Assert.Equal("1.2.840.10045.2.1", algorithmOid.Value); + Assert.Equal("1.2.840.10045.2.1", algorithmOid); - Oid curveOid = algorithmReader.ReadObjectIdentifier(); + string curveOid = algorithmReader.ReadObjectIdentifier(); Assert.False(algorithmReader.HasData, "algorithmReader.HasData after reading second OID"); - Assert.Equal("1.2.840.10045.3.1.7", curveOid.Value); + Assert.Equal("1.2.840.10045.3.1.7", curveOid); const string PublicKeyValue = "04" + @@ -146,9 +145,9 @@ private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEcPublicKey_DefiniteLength(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEcPublicKey_DefiniteLength(AsnEncodingRules ruleSet) { const string InputHex = "3059" + @@ -162,13 +161,13 @@ public static void ReadEcPublicKey_DefiniteLength(PublicEncodingRules ruleSet) "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; byte[] inputData = InputHex.HexToByteArray(); - ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); + ReadEcPublicKey(ruleSet, inputData); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void ReadEcPublicKey_IndefiniteLength(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void ReadEcPublicKey_IndefiniteLength(AsnEncodingRules ruleSet) { const string InputHex = "3080" + @@ -184,16 +183,16 @@ public static void ReadEcPublicKey_IndefiniteLength(PublicEncodingRules ruleSet) "0000"; byte[] inputData = InputHex.HexToByteArray(); - ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); + ReadEcPublicKey(ruleSet, inputData); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(AsnEncodingRules ruleSet) { byte[] inputData = "30020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -201,7 +200,7 @@ public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleS Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -213,12 +212,12 @@ public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleS } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(AsnEncodingRules ruleSet) { byte[] inputData = "308005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -226,7 +225,7 @@ public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules rul Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -238,12 +237,12 @@ public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules rul } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(AsnEncodingRules ruleSet) { byte[] inputData = "A5020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -251,16 +250,16 @@ public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadSequence()); + Assert.Throws(() => reader.ReadSequence()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -272,12 +271,12 @@ public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(AsnEncodingRules ruleSet) { byte[] inputData = "A58005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", @@ -285,16 +284,16 @@ public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSe Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadSequence()); + Assert.Throws(() => reader.ReadSequence()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( + Assert.Throws( () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( + Assert.Throws( () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); @@ -306,34 +305,106 @@ public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSe } [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)] + [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( - 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); - AsnReader val1 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, true)); + AsnReader val1 = reader.ReadSequence(new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); - AsnReader val2 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, false)); + 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 index b2d370cfc39a5b..2dfb2af5354f78 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs @@ -2,35 +2,34 @@ // 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 ReadSetOf : Asn1ReaderTests + public sealed class ReadSetOf { [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, bool expectDataRemaining, int expectedSequenceTagNumber) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AsnReader sequence = reader.ReadSetOf(); if (expectDataRemaining) @@ -56,85 +55,90 @@ public static void ReadSetOf_Success( } [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")] + [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, - 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.ReadSetOf()); + Assert.Throws(() => reader.ReadSetOf()); } [Theory] // BER can read out of order (indefinite) - [InlineData(PublicEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] + [InlineData(AsnEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] // BER can read out of order (definite) - [InlineData(PublicEncodingRules.BER, "3106" + "0101FF" + "010100", true, 1)] + [InlineData(AsnEncodingRules.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)] + [InlineData(AsnEncodingRules.CER, "3180" + "0500" + "010100" + "0000", false, 1)] + [InlineData(AsnEncodingRules.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)] + [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(PublicEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] - [InlineData(PublicEncodingRules.DER, "3105" + "0500" + "010100", false, 1)] + [InlineData(AsnEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] + [InlineData(AsnEncodingRules.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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, bool expectSuccess, int lastTagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + 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, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => alsoReader.ReadSetOf()); + AsnReader alsoReader = new AsnReader(inputData, ruleSet); + Assert.Throws(() => alsoReader.ReadSetOf()); + Assert.Throws(() => laxReader.ReadSetOf(false)); setOf = reader.ReadSetOf(skipSortOrderValidation: true); } @@ -151,24 +155,38 @@ public static void ReadSetOf_DataSorting( } 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(AsnEncodingRules ruleSet) { byte[] inputData = "31020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -179,21 +197,21 @@ public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleS } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(AsnEncodingRules ruleSet) { byte[] inputData = "318005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -204,102 +222,174 @@ public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules rul } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(AsnEncodingRules ruleSet) { byte[] inputData = "A5020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadSetOf()); + Assert.Throws(() => reader.ReadSetOf()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.Application, 5))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); - AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(AsnEncodingRules ruleSet) { byte[] inputData = "A58005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws(() => reader.ReadSetOf()); + Assert.Throws(() => reader.ReadSetOf()); Assert.True(reader.HasData, "HasData after default tag"); - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.Application, 5))); Assert.True(reader.HasData, "HasData after wrong custom class"); - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); Assert.True(reader.HasData, "HasData after wrong custom tag value"); - AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); + 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(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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val1 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, true)); + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader val1 = reader.ReadSetOf(expectedTag: new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val2 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, false)); + 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.Formats.Asn1/tests/Reader/ReadT61String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs index 4c6cff568ff2e6..056785a99654c0 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Reader/ReadUTF8String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs index 4b9464e3e59490..c09de11b916252 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index cfe823f1de0030..ff9e333e2addcd 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs @@ -2,34 +2,33 @@ // 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 ReadUtcTime : Asn1ReaderTests + public sealed class ReadUtcTime { [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)] + [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(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)] + [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(PublicEncodingRules.BER, "170F313730393038313033352D30373030", 2017, 9, 8, 10, 35, 0, -7, 0)] - [InlineData(PublicEncodingRules.BER, "170F323730393038313033352B30393132", 2027, 9, 8, 10, 35, 0, 9, 12)] + [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(PublicEncodingRules.BER, "170B313230313032323335395A", 2012, 1, 2, 23, 59, 0, 0, 0)] - [InlineData(PublicEncodingRules.BER, "170B343931323331323335395A", 2049, 12, 31, 23, 59, 0, 0, 0)] + [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( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3780" + "04023132" + "04023031" + @@ -39,7 +38,7 @@ public sealed class ReadUtcTime : Asn1ReaderTests "0000", 2012, 1, 2, 23, 59, 0, 0, 0)] public static void ParseTime_Valid( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, int year, int month, @@ -52,7 +51,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.ReadUtcTime(); Assert.Equal(year, value.Year); @@ -70,7 +69,7 @@ public static void ParseTime_InvalidValue_LegalString() { byte[] inputData = "17113030303030303030303030302D31353030".HexToByteArray(); - var exception = Assert.Throws( + var exception = Assert.Throws( () => { AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); @@ -102,31 +101,73 @@ public static void ReadUtcTime_TwoYearMaximum(int maximum, int interpretedYear) } [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")] + [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, - 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.ReadUtcTime()); + Assert.Throws(() => reader.ReadUtcTime()); } [Fact] @@ -141,26 +182,26 @@ public static void ReadUtcTime_WayTooBig_Throws() AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - Assert.Throws(() => reader.ReadUtcTime()); + Assert.Throws(() => reader.ReadUtcTime()); } [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 = "170D3530303130323132333435365A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.ReadUtcTime(Asn1Tag.Null)); + () => reader.ReadUtcTime(expectedTag: Asn1Tag.Null)); Assert.True(reader.HasData, "HasData after bad universal tag"); - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 0))); + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); Assert.True(reader.HasData, "HasData after wrong tag"); @@ -172,64 +213,83 @@ 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) { + const int TwoDigitYearMax = 2052; byte[] inputData = "850D3530303130323132333435365A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); AssertExtensions.Throws( "expectedTag", - () => reader.ReadUtcTime(Asn1Tag.Null)); + () => 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.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(new Asn1Tag(TagClass.Application, 5))); + 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(new Asn1Tag(TagClass.ContextSpecific, 7))); + 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(new Asn1Tag(TagClass.ContextSpecific, 5))); + 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(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)] + [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( - 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.ReadUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); + DateTimeOffset val1 = reader.ReadUtcTime(expectedTag: new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); - DateTimeOffset val2 = reader.ReadUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); + DateTimeOffset val2 = reader.ReadUtcTime(expectedTag: new Asn1Tag(tagClass, tagValue, false)); Assert.False(reader.HasData); diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs index 0cd9ef5c0d8dc6..058213ec38430e 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index 2ce3905bcb494e..beb4397ba6c512 100644 --- a/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj +++ b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj @@ -3,9 +3,10 @@ true $(NetCoreAppCurrent);$(NetFrameworkCurrent) - + CommonTest\System\Security\Cryptography\ByteUtils.cs diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs b/src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs index 08d3a52e89d9f9..c20633d7f2be2c 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index 183c5b75ac78de..03c1eeb532b91b 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs @@ -2,257 +2,290 @@ // 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 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.Writer { public static class ComprehensiveWriteTest { [Fact] public static void WriteMicrosoftDotComCert() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - // Certificate - writer.PushSequence(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + // Certificate + using (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)) + using (writer.PushSequence()) { - 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(); + // 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); + } + } + } + } + } } - 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)) + // signatureAlgorithm + using (writer.PushSequence()) { - 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.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); } - 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" + @@ -267,24 +300,23 @@ public static void WriteMicrosoftDotComCert() "090807060504030201").HexToByteArray(); writer.WriteBitString(containsSignature.AsSpan(9, 256)); + } - // certificate - writer.PopSequence(); + Assert.Equal( + ComprehensiveReadTests.MicrosoftDotComSslCertBytes.ByteArrayToHex(), + writer.Encode().ByteArrayToHex()); - 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) { - writer.PushSetOf(); - writer.PushSequence(); - writer.WriteObjectIdentifier(oid); - writer.WriteCharacterString(valueType, value); - writer.PopSequence(); - writer.PopSetOf(); + 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 index e66448dbf55487..85d86768bd0c45 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs @@ -3,260 +3,227 @@ // 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 +namespace System.Formats.Asn1.Tests.Writer { public class PushPopSequence : Asn1WriterTests { [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSequence()); - } + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSequence()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } + AsnWriter writer = new AsnWriter(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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(); - Assert.Throws( - () => writer.PopSequence()); - } + Assert.Throws( + () => writer.PopSequence()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(); - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } + 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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - Assert.Throws( - () => writer.PopSequence()); - } + Assert.Throws( + () => writer.PopSequence()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } + 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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); - writer.PopSequence(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); + writer.PopSequence(); - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "30800000"); - } - else - { - Verify(writer, "3000"); - } + if (ruleSet == AsnEncodingRules.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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.Private, 5)); - writer.PopSequence(new Asn1Tag(TagClass.Private, 5, true)); + AsnWriter writer = new AsnWriter(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"); - } + if (ruleSet == AsnEncodingRules.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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "30800000"); - } - else - { - Verify(writer, "3000"); - } + if (ruleSet == AsnEncodingRules.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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules 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)); + 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 == PublicEncodingRules.CER ? "800000" : "00"; + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == AsnEncodingRules.CER ? "800000" : "00"; - Verify(writer, tagHex + rest); - } + Verify(writer, tagHex + rest); } [Fact] public static void BER_WritesDefinite_Empty() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.PushSequence(); - writer.PopSequence(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushSequence(); + writer.PopSequence(); - Verify(writer, "3000"); - } + Verify(writer, "3000"); } [Fact] public static void CER_WritesIndefinite_Empty() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.PushSequence(); - writer.PopSequence(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.PushSequence(); + writer.PopSequence(); - Verify(writer, "30800000"); - } + Verify(writer, "30800000"); } [Fact] - public static void DER_WritesDefinite_CustomTag_Empty() + public static void DER_WritesDefinite_Empty() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); - writer.PopSequence(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSequence(); + writer.PopSequence(); - Verify(writer, "3000"); - } + Verify(writer, "3000"); } [Fact] - public static void BER_WritesDefinite_CustomTag__Empty() + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSequence(tag); + writer.PopSequence(tag); - Verify(writer, "EF00"); - } + Verify(writer, "EF00"); } [Fact] - public static void CER_WritesIndefinite_CustomTag__Empty() + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSequence(tag); + writer.PopSequence(tag); - Verify(writer, "7F5B800000"); - } + Verify(writer, "7F5B800000"); } [Fact] - public static void DER_WritesDefinite_CustomTag__Empty() + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSequence(tag); + writer.PopSequence(tag); - Verify(writer, "BE00"); - } + Verify(writer, "BE00"); } private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) @@ -288,108 +255,28 @@ private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex [Fact] public static void BER_Nested() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); - TestNested(writer, alt, "300AFF7F003005FF7F023000"); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); - TestNested(writer, alt, "3080AC8000003080AC8030800000000000000000"); - } + 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); + 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))); - } + TestNested(writer, alt, "30086500300465023000"); } private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) @@ -412,9 +299,9 @@ private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void SimpleContentShift(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void SimpleContentShift(AsnEncodingRules ruleSet) { const string ExpectedHex = "308180" + @@ -424,10 +311,8 @@ public static void SimpleContentShift(PublicEncodingRules ruleSet) "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } + AsnWriter writer = new AsnWriter(ruleSet); + SimpleContentShiftCore(writer, ExpectedHex); } [Fact] @@ -442,60 +327,54 @@ public static void SimpleContentShift_CER() "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + "0000"; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } + 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(); + AsnWriter innerWriter = new AsnWriter(ruleSet); + byte[] paddedBigEndianN = ( + "00" + + "AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED369731156" + + "20968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925BCE" + + "624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8CBB" + + "5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF18" + + "7B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C94" + + "AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF2B" + + "82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6D9" + + "FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD8847").HexToByteArray(); - // AlgorithmIdentifier - outerWriter.PushSequence(); - outerWriter.WriteObjectIdentifier("1.2.840.113549.1.1.1"); - outerWriter.WriteNull(); - outerWriter.PopSequence(); + // Now it's padded little-endian. + Array.Reverse(paddedBigEndianN); + BigInteger n = new BigInteger(paddedBigEndianN); + const long e = 8589935681; - outerWriter.WriteBitString(innerWriter.Encode()); - outerWriter.PopSequence(); + innerWriter.PushSequence(); + innerWriter.WriteInteger(n); + innerWriter.WriteInteger(e); + innerWriter.PopSequence(); - Verify(outerWriter, expectedHex); - } - } + 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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void WriteRSAPublicKey(AsnEncodingRules ruleSet) { const string ExpectedHex = // CONSTRUCTED SEQUENCE @@ -527,7 +406,7 @@ public static void WriteRSAPublicKey(PublicEncodingRules ruleSet) // INTEGER (e) "02050200000441"; - WriteRSAPublicKeyCore((AsnEncodingRules)ruleSet, ExpectedHex); + WriteRSAPublicKeyCore(ruleSet, ExpectedHex); } [Fact] @@ -574,65 +453,149 @@ public static void WriteRSAPublicKey_CER() } [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)) + [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) { - if (customTag) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - } - else - { - writer.PushSequence(); - } + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSequence(); + } - int written = -5; + 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); + } - Assert.Throws(() => writer.Encode()); - Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); - 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); - byte[] buf = new byte[10]; - Assert.Throws(() => writer.TryEncode(buf, out written)); - Assert.Equal(-5, written); + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSequence_EndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_IdempotentDispose_Complex(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((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)) { - AssertExtensions.Throws( - "tag", - () => writer.PushSequence(Asn1Tag.EndOfContents)); + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSetOf_PopSequence(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_DisposeSilentNotContained(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + using (var scope = writer.PushSequence()) { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.PopSequence(); - writer.PushSetOf(tag); + // Since this has a different write offset, the equals is false, so no exception. + writer.PushSequence(); + writer.PushSequence(); - Assert.Throws( - () => writer.PopSequence(tag)); + 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 index adb4ff956f5278..aaf55ab1836849 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs @@ -2,261 +2,226 @@ // 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 +namespace System.Formats.Asn1.Tests.Writer { public class PushPopSetOf : Asn1WriterTests { [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSetOf()); - } + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSetOf()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } + AsnWriter writer = new AsnWriter(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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(); - Assert.Throws( - () => writer.PopSetOf()); - } + Assert.Throws( + () => writer.PopSetOf()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(); - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } + 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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - Assert.Throws( - () => writer.PopSetOf()); - } + Assert.Throws( + () => writer.PopSetOf()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } + 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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); - writer.PopSetOf(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + writer.PopSetOf(); - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "31800000"); - } - else - { - Verify(writer, "3100"); - } + if (ruleSet == AsnEncodingRules.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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.Private, 5)); - writer.PopSetOf(new Asn1Tag(TagClass.Private, 5, true)); + AsnWriter writer = new AsnWriter(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"); - } + if (ruleSet == AsnEncodingRules.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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "31800000"); - } - else - { - Verify(writer, "3100"); - } + if (ruleSet == AsnEncodingRules.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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules 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)); + 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 == PublicEncodingRules.CER ? "800000" : "00"; + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == AsnEncodingRules.CER ? "800000" : "00"; - Verify(writer, tagHex + rest); - } + Verify(writer, tagHex + rest); } [Fact] public static void BER_WritesDefinite_Empty() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.PushSetOf(); - writer.PopSetOf(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushSetOf(); + writer.PopSetOf(); - Verify(writer, "3100"); - } + Verify(writer, "3100"); } [Fact] public static void CER_WritesIndefinite_Empty() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.PushSetOf(); - writer.PopSetOf(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.PushSetOf(); + writer.PopSetOf(); - Verify(writer, "31800000"); - } + Verify(writer, "31800000"); } [Fact] public static void DER_WritesDefinite_CustomTag_Empty() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSetOf(); - writer.PopSetOf(); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSetOf(); + writer.PopSetOf(); - Verify(writer, "3100"); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); - Verify(writer, "EF00"); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); - Verify(writer, "7F5B800000"); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); - Verify(writer, "BE00"); - } + Verify(writer, "BE00"); } private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) @@ -289,34 +254,28 @@ private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex [Fact] public static void BER_Nested() { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); - TestNested(writer, alt, "310A3105FF7F023100FF7F00"); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); - TestNested(writer, alt, "31803180AC803180000000000000AC8000000000"); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); - TestNested(writer, alt, "31083104650231006500"); - } + TestNested(writer, alt, "31083104650231006500"); } private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) @@ -339,9 +298,9 @@ private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void SimpleContentShift(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void SimpleContentShift(AsnEncodingRules ruleSet) { const string ExpectedHex = "318180" + @@ -351,10 +310,8 @@ public static void SimpleContentShift(PublicEncodingRules ruleSet) "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } + AsnWriter writer = new AsnWriter(ruleSet); + SimpleContentShiftCore(writer, ExpectedHex); } [Fact] @@ -369,136 +326,58 @@ public static void SimpleContentShift_CER() "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))); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + SimpleContentShiftCore(writer, ExpectedHex); } private static void ValidateDataSorting(AsnEncodingRules ruleSet, string expectedHex) { - using (AsnWriter writer = new AsnWriter(ruleSet)) - { - writer.PushSetOf(); + 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); + // 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(); + 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); - } + // 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] @@ -557,65 +436,149 @@ public static void DER_SortsData() } [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)) + [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) { - if (customTag) - { - writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - } - else - { - writer.PushSetOf(); - } + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSetOf(); + } + + int written = -5; - int written = -5; + Assert.Throws(() => writer.GetEncodedLength()); + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); - 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); + } - byte[] buf = new byte[10]; - Assert.Throws(() => writer.TryEncode(buf, out written)); - 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSetOf_EndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_IdempotentDispose_Complex(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((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)) { - AssertExtensions.Throws( - "tag", - () => writer.PushSetOf(Asn1Tag.EndOfContents)); + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSequence_PopSetOf(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_DisposeSilentNotContained(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + using (var scope = writer.PushSetOf()) { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.PopSetOf(); - writer.PushSequence(tag); + // Since this has a different write offset, the equals is false, so no exception. + writer.PushSetOf(); + writer.PushSequence(); - Assert.Throws( - () => writer.PopSetOf(tag)); + 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.Formats.Asn1/tests/Writer/WriteBMPString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs index 890e04ff40e338..aeb39deffb3e69 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index dbe72ac6c66b57..d70c5adf2745ad 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs @@ -3,53 +3,48 @@ // 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 +namespace System.Formats.Asn1.Tests.Writer { public class WriteBitString : Asn1WriterTests { [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteEmpty(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteEmpty(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(ReadOnlySpan.Empty); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(ReadOnlySpan.Empty); - Verify(writer, "030100"); - } + 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) + [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]; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(data, unusedBitCount); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(data, unusedBitCount); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] @@ -67,31 +62,29 @@ public void WriteSegmentedCER(int length, int unusedBitCount, string hexStart, s data[i] = 0x88; } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteBitString(data, unusedBitCount); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteBitString(data, unusedBitCount); - Verify(writer, expectedHex); - } + 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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, int payloadLength, bool expectConstructed) { @@ -111,13 +104,11 @@ public void VerifyWriteBitString_PrimitiveOrConstructed( { Asn1Tag writtenTag; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(toTry, data); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(data, tag: toTry); - Assert.True(writer.TryEncode(answerBuf, out _)); - Assert.True(Asn1Tag.TryDecode(answerBuf, out writtenTag, out _)); - } + Assert.True(writer.TryEncode(answerBuf, out _)); + Assert.True(Asn1Tag.TryDecode(answerBuf, out writtenTag, out _)); if (expectConstructed) { @@ -134,233 +125,191 @@ public void VerifyWriteBitString_PrimitiveOrConstructed( } [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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, int unusedBitCount, string inputHex, bool expectThrow) { byte[] inputBytes = inputHex.HexToByteArray(); - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (expectThrow) { - if (expectThrow) - { - Assert.Throws( - () => writer.WriteBitString(inputBytes, unusedBitCount)); + AssertExtensions.Throws( + "unusedBitCount", + () => writer.WriteBitString(inputBytes, unusedBitCount)); - Assert.Throws( - () => writer.WriteBitString( - new Asn1Tag(TagClass.ContextSpecific, 3), - inputBytes, - unusedBitCount)); + AssertExtensions.Throws( + "unusedBitCount", + () => writer.WriteBitString( + inputBytes, + unusedBitCount, + new Asn1Tag(TagClass.ContextSpecific, 3))); - return; - } + return; + } - byte[] output = new byte[512]; - writer.WriteBitString(inputBytes, unusedBitCount); - Assert.True(writer.TryEncode(output, out int bytesWritten)); + 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]); + // 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)); + writer.WriteBitString(inputBytes, unusedBitCount, new Asn1Tag(TagClass.ContextSpecific, 9)); + Assert.True(writer.TryEncode(output, out bytesWritten)); - Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); - } + 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) + [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]; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - ArgumentOutOfRangeException exception = AssertExtensions.Throws( - nameof(unusedBitCount), - () => writer.WriteBitString(data, unusedBitCount)); + AsnWriter writer = new AsnWriter(ruleSet); - Assert.Equal(unusedBitCount, exception.ActualValue); + ArgumentOutOfRangeException exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(data, unusedBitCount)); - exception = AssertExtensions.Throws( - nameof(unusedBitCount), - () => writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 5), data, unusedBitCount)); + Assert.Equal(unusedBitCount, exception.ActualValue); - 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void EmptyData_Requires0UnusedBits(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EmptyData_Requires0UnusedBits(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); + AsnWriter writer = new AsnWriter(ruleSet); - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); - Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); - Assert.Throws( - () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 1)); + Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); - Assert.Throws( - () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 7)); + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 1, contextTag)); - writer.WriteBitString(ReadOnlySpan.Empty, 0); - writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 0); - } + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 7, contextTag)); + + writer.WriteBitString(ReadOnlySpan.Empty, 0); + writer.WriteBitString(ReadOnlySpan.Empty, 0, contextTag); } [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")] + [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( - PublicEncodingRules ruleSet, - PublicTagClass tagClass, + AsnEncodingRules ruleSet, + TagClass tagClass, int tagValue, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { + AsnWriter writer = new AsnWriter(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); + if (tagClass == TagClass.Universal) + { + Debug.Assert(tagValue == (int)UniversalTagNumber.BitString); + writer.WriteBitString(ReadOnlySpan.Empty, 0); } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteBitString_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + else { - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.EndOfContents, new byte[1])); + writer.WriteBitString(ReadOnlySpan.Empty, 0, new Asn1Tag(tagClass, tagValue)); } + + Verify(writer, expectedHex); } [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteBitString_Null(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty)); + AsnWriter writer = new AsnWriter(ruleSet); - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(ReadOnlySpan.Empty, tag: Asn1Tag.Null)); - 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)); - } + 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 index 556d3478f91dcd..b558b84737e288 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs @@ -2,112 +2,78 @@ // 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.Writer { 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value); - Verify(writer, expectedHex); - } + 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 3), value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value, new Asn1Tag(TagClass.ContextSpecific, 3)); - Verify(writer, expectedHex); - } + 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.EndOfContents, value)); - } + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBoolean(value, Asn1Tag.Null)); } [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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 7, true), value); - writer.WriteBoolean(new Asn1Tag(UniversalTagNumber.Boolean, true), 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"); - } + if (value) + { + Verify(writer, "8701FF0101FF"); } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + else { - 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)); + 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 index 5637e9dff85936..c29905370c8547 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs @@ -2,12 +2,11 @@ // 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.Writer { public abstract class WriteCharacterString : Asn1WriterTests { @@ -42,315 +41,259 @@ public abstract class WriteCharacterString : Asn1WriterTests protected void VerifyWrite_BER_String_Helper(string input, string expectedPayloadHex) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 14); + WriteString(writer, tag, input); - Verify(writer, Stringify(tag) + expectedPayloadHex); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 19); + WriteString(writer, tag, input); - Verify(writer, Stringify(tag) + expectedPayloadHex); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 2); + WriteString(writer, tag, input); - Verify(writer, Stringify(tag) + expectedPayloadHex); - } + 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } + 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()); + 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); - } + 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } + 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 30); + WriteSpan(writer, tag, input.AsSpan()); - Verify(writer, Stringify(tag) + expectedPayloadHex); - } + 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } + 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 31); + WriteSpan(writer, tag, input.AsSpan()); - Verify(writer, Stringify(tag) + expectedPayloadHex); - } + 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); + 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); - } + 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); + 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); - } + 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()); + 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); - } + 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()); + 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); - } + 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); + 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); - } + 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); + 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); - } + 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()); + 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); - } + 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()); + 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); - } + 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); + 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); - } + 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); + 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); - } + 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()); + 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); - } + 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()); + 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); - } + Verify(writer, Stringify(expected) + expectedPayloadHex); } - protected void VerifyWrite_String_Null_Helper(PublicEncodingRules ruleSet) + protected void VerifyWrite_String_Null_Helper(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "str", - () => WriteString(writer, null)); - } + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "value", + () => WriteString(writer, null)); } - protected void VerifyWrite_String_Null_CustomTag_Helper(PublicEncodingRules ruleSet) + protected void VerifyWrite_String_Null_CustomTag_Helper(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "str", - () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); - } + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "value", + () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); } - protected void VerifyWrite_EndOfContents_String_Helper(PublicEncodingRules ruleSet) + protected void VerifyWrite_Null_String_Helper(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => WriteString(writer, Asn1Tag.EndOfContents, "hi")); - } + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => WriteString(writer, Asn1Tag.Null, "hi")); } - protected void VerifyWrite_EndOfContents_Span_Helper(PublicEncodingRules ruleSet) + protected void VerifyWrite_Null_Span_Helper(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => WriteSpan(writer, Asn1Tag.EndOfContents, "hi".AsSpan())); - } + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => WriteSpan(writer, Asn1Tag.Null, "hi".AsSpan())); } - private void VerifyWrite_CERSegmented(AsnWriter writer, string tagHex, int contentByteCount) + private void VerifyWrite_CERSegmented_Helper(AsnWriter writer, string tagHex, int contentByteCount) { int div = Math.DivRem(contentByteCount, 1000, out int rem); @@ -379,170 +322,100 @@ private void VerifyWrite_CERSegmented(AsnWriter writer, string tagHex, int conte 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); + 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); - } + WriteString(writer, input); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteString(writer, tag, input); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteString(writer, tag, input); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteString(writer, prim, input); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteSpan(writer, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteSpan(writer, tag, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteSpan(writer, tag, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(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); + 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); - } + WriteSpan(writer, prim, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); } protected void VerifyWrite_String_NonEncodable_Helper(string input) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.Throws(() => WriteString(writer, input)); - } + 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)); - } + 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 index 20280d24514c0e..2ea07868854bbd 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs @@ -2,525 +2,372 @@ // 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.Tests.Reader; using System.Security.Cryptography.X509Certificates; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.SByteBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 30)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 30), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.ByteBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 26)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 26), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.ShortBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Private, 212)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 212), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.UShortBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Application, 13)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, 13), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.IntBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Application, short.MaxValue)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, short.MaxValue), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.UIntBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 97)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 97), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.LongBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 0)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + 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")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, ReadEnumerated.ULongBacked value, bool customTag, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Private, 1)); + } + else { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 1), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); + writer.WriteEnumeratedValue(value); } + + Verify(writer, expectedHex); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyFlagsBased(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyFlagsBased(AsnEncodingRules 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)); - } + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyNonEnum(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyNull(AsnEncodingRules 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)); + AsnWriter writer = new AsnWriter(ruleSet); - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)"hi")); + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue(ReadEnumerated.IntBacked.Pillow, Asn1Tag.Null)); - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)5)); - } + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue((Enum)ReadEnumerated.IntBacked.Pillow, Asn1Tag.Null)); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyEndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_NonNull(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, ReadEnumerated.IntBacked.Pillow)); + AsnWriter writer = new AsnWriter(ruleSet); - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, (object)ReadEnumerated.IntBacked.Pillow)); - } - } + AssertExtensions.Throws( + "value", + () => writer.WriteEnumeratedValue(null)); - [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)); - } + AssertExtensions.Throws( + "value", + () => writer.WriteEnumeratedValue( + null, + new Asn1Tag(TagClass.ContextSpecific, 1))); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_Object(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object(AsnEncodingRules 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); + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); - genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.SByteBacked.Fluff); + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.UIntBacked.Fluff); - genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.ULongBacked.Fluff); + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.SByteBacked.Fluff); - Verify(objWriter, genWriter.Encode().ByteArrayToHex()); - } + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)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) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object_WithTag(AsnEncodingRules ruleSet) { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.UIntBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.UIntBacked.Fluff); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); - tag = new Asn1Tag(TagClass.Private, 4); + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.UIntBacked.Fluff, tag); - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.SByteBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.SByteBacked.Fluff); + tag = new Asn1Tag(TagClass.Private, 4); - tag = new Asn1Tag(TagClass.Application, 75); + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.SByteBacked.Fluff, tag); - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.ULongBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.ULongBacked.Fluff); + tag = new Asn1Tag(TagClass.Application, 75); - Verify(objWriter, genWriter.Encode().ByteArrayToHex()); - } + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.ULongBacked.Fluff, tag); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteEnumeratedValue_ConstructedIgnored(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteEnumeratedValue_ConstructedIgnored(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteEnumeratedValue( - new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true), - ReadEnumerated.ULongBacked.Fluff); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteEnumeratedValue( + ReadEnumerated.ULongBacked.Fluff, + new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true)); - writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), - (object)ReadEnumerated.SByteBacked.Fluff); + writer.WriteEnumeratedValue( + (Enum)ReadEnumerated.SByteBacked.Fluff, + new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); - 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)); - } + Verify(writer, "0A0900FACEF00DCAFEBEEF" + "800153"); } } } diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs index c195061c5ca4fc..715f5edac31681 100644 --- a/src/libraries/System.Formats.Asn1/tests/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.Formats.Asn1/tests/Writer/WriteIA5String.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs index 607ef4c7fafa82..31165b91a51c12 100644 --- a/src/libraries/System.Formats.Asn1/tests/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 index 05c9b394867fa1..7ea14a99ccf2ce 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs @@ -3,281 +3,268 @@ // 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 +namespace System.Formats.Asn1.Tests.Writer { 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); - Verify(writer, expectedHex); - } + 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); - Verify(writer, expectedHex); - } + 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) + [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); - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); - Verify(writer, expectedHex); - } + 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.Application, 7), value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.Application, 7)); - Verify(writer, expectedHex); - } + 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) + [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) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 9), value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.ContextSpecific, 9)); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] - [InlineData(PublicEncodingRules.BER, "0", "D00100")] - [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "D0100102030405060708090A0B0C0D0E0F00")] - [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "D01100F0E0D0C0B0A090807060504030201000")] - [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "D010FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules 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); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.Private, 16)); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] @@ -294,12 +281,10 @@ 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteInteger(valueHex.HexToByteArray()); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] @@ -316,12 +301,50 @@ 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()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteInteger(valueHex.HexToByteArray(), new Asn1Tag(TagClass.ContextSpecific, 4)); - Verify(writer, expectedHex); - } + 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] @@ -333,12 +356,12 @@ public void VerifyWriteInteger_Context4_EncodedBytes(string valueHex) [InlineData("FFFF")] [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] [InlineData("FF80")] - public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valuHex) + public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valueHex) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.ThrowsAny(() => writer.WriteInteger(valuHex.HexToByteArray())); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + AssertExtensions.Throws( + "value", + () => writer.WriteInteger(valueHex.HexToByteArray())); } [Theory] @@ -350,144 +373,82 @@ public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valuHex) [InlineData("FFFF")] [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] [InlineData("FF80")] - public void VerifyWriteInteger_Application3_InvalidEncodedValue_Throws(string valuHex) + public void VerifyWriteInteger_Application3_InvalidEncodedValue_Throws(string valueHex) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); - Assert.ThrowsAny( - () => writer.WriteInteger(tag, valuHex.HexToByteArray())); - } + AssertExtensions.Throws( + "value", + () => writer.WriteInteger(valueHex.HexToByteArray(), tag)); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteInteger_EndOfContents(PublicEncodingRules ruleSet) + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + public void VerifyWriteIntegerUnsigned_InvalidEncodedValue_Throws(string valueHex) { - 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)); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + AssertExtensions.Throws( + "value", + () => writer.WriteIntegerUnsigned(valueHex.HexToByteArray())); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteInteger_ConstructedIgnored(PublicEncodingRules ruleSet) + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + public void VerifyWriteIntegerUnsigned_Application3_InvalidEncodedValue_Throws(string valueHex) { - 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"); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); + + AssertExtensions.Throws( + "value", + () => writer.WriteIntegerUnsigned(valueHex.HexToByteArray(), tag)); } + [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteInteger_Null(AsnEncodingRules ruleSet) { - 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 })); + 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)); + } - Assert.Throws( - () => writer.WriteInteger(tag, new byte[] { 0xFF, 0xFF })); - } + [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 index f4de4f56372131..d6501f6b4e5ec4 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs @@ -2,85 +2,85 @@ // 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.Collections; +using System.Formats.Asn1.Tests.Reader; +using System.Security.Cryptography.X509Certificates; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteNamedBitList : Asn1WriterTests { [Theory] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "030100", ReadNamedBitList.ULongFlags.None)] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, "030100", ReadNamedBitList.ULongFlags.None)] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, "030100", ReadNamedBitList.ULongFlags.None)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "0309000000000000000003", ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, "0309010000000080000002", ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, "030204B0", ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] public static void VerifyWriteNamedBitList( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string expectedHex, - object value) + Enum value) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNamedBitList(value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(value); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "C00100", ReadNamedBitList.ULongFlags.None)] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, "410100", ReadNamedBitList.ULongFlags.None)] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, "820100", ReadNamedBitList.ULongFlags.None)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "C009000000000000000003", ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] [InlineData( - PublicEncodingRules.CER, + AsnEncodingRules.CER, "4109010000000080000002", ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, "820204B0", ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] public static void VerifyWriteNamedBitList_WithTag( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string expectedHex, - object value) + Enum value) { int ruleSetVal = (int)ruleSet; TagClass tagClass = (TagClass)(byte)(ruleSetVal << 6); @@ -90,258 +90,294 @@ public static void VerifyWriteNamedBitList_WithTag( Asn1Tag tag = new Asn1Tag(tagClass, ruleSetVal); - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNamedBitList(tag, value); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(value, tag); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_Generic(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic(AsnEncodingRules 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; + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); - genWriter.WriteNamedBitList(flagsValue); - objWriter.WriteNamedBitList((object)flagsValue); + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; - Verify(genWriter, objWriter.Encode().ByteArrayToHex()); - } + genWriter.WriteNamedBitList(flagsValue); + objWriter.WriteNamedBitList((Enum)flagsValue); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_Generic_WithTag(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic_WithTag(AsnEncodingRules ruleSet) { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + 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; + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; - genWriter.WriteNamedBitList(tag, flagsValue); - objWriter.WriteNamedBitList(tag, (object)flagsValue); + genWriter.WriteNamedBitList(flagsValue, tag); + objWriter.WriteNamedBitList((Enum)flagsValue, tag); - Verify(genWriter, objWriter.Encode().ByteArrayToHex()); - } + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_NonNull(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_NonNull(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(null)); + AsnWriter writer = new AsnWriter(ruleSet); - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), null)); - } + 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(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_EnumRequired(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_FlagsEnumRequired(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteNamedBitList(3)); + 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))); + } - Assert.Throws( - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), 3)); + [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)); + } - Assert.Throws( - () => writer.WriteNamedBitList((object)3)); + [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); - Assert.Throws( - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), (object)3)); - } + writer.WriteNamedBitList(bits, new Asn1Tag(TagClass.Application, 4)); + + Verify(writer, "440406400100"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_FlagsEnumRequired(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_Empty(AsnEncodingRules 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)); - } + AsnWriter writer = new AsnWriter(ruleSet); + BitArray bits = new BitArray(0); + + writer.WriteNamedBitList(bits); + + Verify(writer, "030100"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_EndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_EveryPattern(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + byte[] allTheBytes = new byte[256]; + + for (int i = 0; i < allTheBytes.Length; i++) { - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList( - Asn1Tag.EndOfContents, - StringSplitOptions.RemoveEmptyEntries)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList( - Asn1Tag.EndOfContents, - (object)StringSplitOptions.RemoveEmptyEntries)); + 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(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_7993Bits(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) + 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) { - 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)); + 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 index f6d2cc0960922b..659965c59af5eb 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs @@ -2,80 +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 Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteNull : Asn1WriterTests { [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNull(); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNull(); - Verify(writer, "0500"); - } + Verify(writer, "0500"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull_EndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull_OctetString(AsnEncodingRules 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)); + AsnWriter writer = new AsnWriter(ruleSet); - Verify(writer, "87000500"); - } + AssertExtensions.Throws( + "tag", + () => writer.WriteNull(Asn1Tag.PrimitiveOctetString)); } [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull_ConstructedIgnored(AsnEncodingRules ruleSet) { - 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)); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNull(new Asn1Tag(TagClass.ContextSpecific, 7, true)); + writer.WriteNull(new Asn1Tag(UniversalTagNumber.Null, true)); - Assert.Throws( - () => writer.WriteNull(new Asn1Tag(TagClass.Private, 3))); - } + 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 index e488209ee9b5c0..7616d3d1fd6281 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs @@ -3,158 +3,99 @@ // 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 WriteObjectIdentifier : Asn1WriterTests { [Theory] [MemberData(nameof(ValidOidData))] public void VerifyWriteObjectIdentifier_String( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string oidValue, string expectedHex) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidValue); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier(oidValue); - Verify(writer, expectedHex); - } + 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, + AsnEncodingRules ruleSet, string oidValue, string expectedHex) { - Oid oidObj = new Oid(oidValue, "FriendlyName does not matter"); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier(oidValue.AsSpan()); - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidObj); - - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] [MemberData(nameof(InvalidOidData))] public void VerifyWriteOid_InvalidValue_String( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string nonOidValue) { _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) + AsnWriter writer = new AsnWriter(ruleSet); + + if (nonOidValue == null) { - if (nonOidValue == null) - { - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(nonOidValue)); - } - else - { - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidValue)); - } + AssertExtensions.Throws( + "oidValue", + () => 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)) + else { - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidValue.AsSpan())); + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue)); } } [Theory] [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_Oid( + public void VerifyWriteOid_InvalidValue_Span( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules 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"); + AsnWriter writer = new AsnWriter(ruleSet); - Verify(writer, "83052B0E03021A"); - } + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue.AsSpan())); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_Span(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_String(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), "1.3.14.3.2.26".AsSpan()); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier("1.3.14.3.2.26", new Asn1Tag(TagClass.ContextSpecific, 3)); - Verify(writer, "42052B0E03021A"); - } + Verify(writer, "83052B0E03021A"); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_Oid(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_Span(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier( - new Asn1Tag(TagClass.Private, 36), - Oid.FromFriendlyName("SHA1", OidGroup.HashAlgorithm)); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier("1.3.14.3.2.26".AsSpan(), new Asn1Tag(TagClass.Application, 2)); - Verify(writer, "DF24052B0E03021A"); - } + Verify(writer, "42052B0E03021A"); } [Theory] @@ -162,297 +103,58 @@ public void WriteObjectIdentifier_CustomTag_Oid(PublicEncodingRules ruleSet) [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); - } - }); - } - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteObjectIdentifier_NullOid(bool defaultTag) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - AssertExtensions.Throws( - "oid", - () => + AssertExtensions.Throws( + "oidValue", + () => + { + if (defaultTag) { - 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"))); - } + writer.WriteObjectIdentifier((string)null); + } + else + { + writer.WriteObjectIdentifier((string)null, new Asn1Tag(TagClass.ContextSpecific, 6)); + } + }); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteObjectIdentifier_ConstructedIgnored(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_Null(AsnEncodingRules 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); + AsnWriter writer = new AsnWriter(ruleSet); - 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)); + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier("1.1", Asn1Tag.Null)); - Verify(writer, "060129800129060129800129060129800129"); - } + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier("1.1".AsSpan(), Asn1Tag.Null)); } [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_ConstructedIgnored(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0")); + AsnWriter writer = new AsnWriter(ruleSet); - Assert.Throws( - () => writer.WriteObjectIdentifier("0")); + const string OidValue = "1.1"; + Asn1Tag constructedOid = new Asn1Tag(UniversalTagNumber.ObjectIdentifier, isConstructed: true); + Asn1Tag constructedContext0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0q")); + writer.WriteObjectIdentifier(OidValue, constructedOid); + writer.WriteObjectIdentifier(OidValue, constructedContext0); + writer.WriteObjectIdentifier(OidValue.AsSpan(), constructedOid); + writer.WriteObjectIdentifier(OidValue.AsSpan(), constructedContext0); - 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)); - } + Verify(writer, "060129800129060129800129"); } public static IEnumerable ValidOidData { get; } = @@ -460,37 +162,37 @@ public static void WriteAfterDispose(bool empty) { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "0.0", "060100", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1.0", "060128", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "2.0", "060150", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1.3.14.3.2.26", "06052B0E03021A", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "2.999.19427512891.25", "06088837C8AFE1A43B19", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1.2.840.113549.1.1.10", "06092A864886F70D01010A", }, @@ -500,7 +202,7 @@ public static void WriteAfterDispose(bool empty) // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 // this is // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } - PublicEncodingRules.DER, + AsnEncodingRules.DER, "2.25.329800735698586629295641978511506172918.3", "06156983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", }, @@ -509,25 +211,25 @@ public static void WriteAfterDispose(bool empty) 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" }, + 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 index f0c7f9854df1bb..465df77e16897d 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs @@ -2,53 +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 Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteOctetString : Asn1WriterTests { [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteEmpty(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteEmpty(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(ReadOnlySpan.Empty); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(ReadOnlySpan.Empty); - Verify(writer, "0400"); - } + 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) + [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]; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(data); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(data); - Verify(writer, expectedHex); - } + Verify(writer, expectedHex); } [Theory] @@ -67,44 +62,42 @@ public void WriteSegmentedCER(int length, string hexStart, string hexStart2) data[i] = 0x88; } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteOctetString(data); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteOctetString(data); - Verify(writer, expectedHex); - } + 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)] + [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( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, int payloadLength, bool expectConstructed) { @@ -122,12 +115,10 @@ public void VerifyWriteOctetString_PrimitiveOrConstructed( foreach (Asn1Tag toTry in tagsToTry) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(toTry, data); + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(data, toTry); - Assert.True(writer.TryEncode(answerBuf, out _)); - } + Assert.True(writer.TryEncode(answerBuf, out _)); Assert.True(Asn1Tag.TryDecode(answerBuf, out Asn1Tag writtenTag, out _)); if (expectConstructed) @@ -145,49 +136,20 @@ public void VerifyWriteOctetString_PrimitiveOrConstructed( } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteOctetString_EndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteOctetString_Null(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); + AsnWriter writer = new AsnWriter(ruleSet); - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.EndOfContents, new byte[1])); - } - } + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(ReadOnlySpan.Empty, Asn1Tag.Null)); - [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)); - } + 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 index 75e4281ef966ad..202622470dd00c 100644 --- a/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.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 WriteUtcTime : Asn1WriterTests { @@ -33,241 +32,170 @@ public class WriteUtcTime : Asn1WriterTests [MemberData(nameof(TestCases))] public void VerifyWriteUtcTime_BER(DateTimeOffset input, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteUtcTime(input); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteUtcTime(input); - Verify(writer, "17" + expectedHexPayload); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); + writer.WriteUtcTime(input, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteUtcTime(input); - Verify(writer, "17" + expectedHexPayload); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); + writer.WriteUtcTime(input, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteUtcTime(input); - Verify(writer, "17" + expectedHexPayload); - } + 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); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteUtcTime(input, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + Verify(writer, Stringify(tag) + expectedHexPayload); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteUtcTime_EndOfContents(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteUtcTime_Null(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.EndOfContents, DateTimeOffset.Now)); - } + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteUtcTime(DateTimeOffset.Now, Asn1Tag.Null)); } [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteUtcTime_IgnoresConstructed(PublicEncodingRules ruleSet) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteUtcTime_IgnoresConstructed(AsnEncodingRules 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); + 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"); - } + 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) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Assert.Equal(0, writer.GetEncodedLength()); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Assert.Equal(0, writer.GetEncodedLength()); - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(input, input.Year - 1)); + AssertExtensions.Throws( + "value", + () => writer.WriteUtcTime(input, input.Year - 1)); - Assert.Equal(0, writer.GetEncodedLength()); + Assert.Equal(0, writer.GetEncodedLength()); - writer.WriteUtcTime(input, input.Year); - Assert.Equal(15, writer.GetEncodedLength()); + writer.WriteUtcTime(input, input.Year); + Assert.Equal(15, writer.GetEncodedLength()); - writer.WriteUtcTime(input, input.Year + 99); - Assert.Equal(30, writer.GetEncodedLength()); + writer.WriteUtcTime(input, input.Year + 99); + Assert.Equal(30, writer.GetEncodedLength()); - writer.Reset(); + writer.Reset(); - _ = expectedHexPayload; - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(input, input.Year + 100)); + _ = expectedHexPayload; + AssertExtensions.Throws( + "value", + () => writer.WriteUtcTime(input, input.Year + 100)); - Assert.Equal(0, writer.GetEncodedLength()); - } + 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)); - } + 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.Formats.Asn1/tests/Writer/WriteUtf8String.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs index 9caa0d8adcf3fb..f60613708adabe 100644 --- a/src/libraries/System.Formats.Asn1/tests/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); } } From 3ff0d89ace9947ca6e8427045407c3b7201ddfbf Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 19 May 2020 09:06:11 -0700 Subject: [PATCH 4/4] Move AsnReader consumers to public API --- .../AsymmetricAlgorithmHelpers.Der.cs | 41 +- .../Interop.ASN1.GetIntegerBytes.cs | 18 +- .../Asn1/AlgorithmIdentifierAsn.xml.cs | 50 +- .../Cryptography/Asn1/AttributeAsn.manual.cs | 2 +- .../Cryptography/Asn1/AttributeAsn.xml.cs | 50 +- .../Cryptography/Asn1/CurveAsn.xml.cs | 38 +- .../Cryptography/Asn1/DigestInfoAsn.xml.cs | 32 +- .../Asn1/DirectoryStringAsn.xml.cs | 39 +- .../Cryptography/Asn1/DssParms.xml.cs | 30 +- .../Asn1/ECDomainParameters.xml.cs | 41 +- .../Cryptography/Asn1/ECPrivateKey.xml | 4 +- .../Cryptography/Asn1/ECPrivateKey.xml.cs | 40 +- .../Cryptography/Asn1/EdiPartyNameAsn.xml.cs | 30 +- .../Asn1/EncryptedPrivateKeyInfoAsn.xml.cs | 32 +- .../Security/Cryptography/Asn1/FieldID.xml.cs | 50 +- .../Cryptography/Asn1/GeneralNameAsn.xml.cs | 75 ++- .../Cryptography/Asn1/OaepParamsAsn.xml.cs | 81 +-- .../Cryptography/Asn1/OtherNameAsn.xml.cs | 50 +- .../Cryptography/Asn1/PBEParameter.xml.cs | 32 +- .../Cryptography/Asn1/PBES2Params.xml.cs | 30 +- .../Cryptography/Asn1/Pbkdf2Params.xml | 4 +- .../Cryptography/Asn1/Pbkdf2Params.xml.cs | 47 +- .../Cryptography/Asn1/Pbkdf2SaltChoice.xml.cs | 32 +- .../Asn1/Pkcs12/CertBagAsn.xml.cs | 50 +- .../Cryptography/Asn1/Pkcs12/MacData.xml.cs | 45 +- .../Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs | 2 +- .../Cryptography/Asn1/Pkcs12/PfxAsn.xml | 2 +- .../Cryptography/Asn1/Pkcs12/PfxAsn.xml.cs | 34 +- .../Asn1/Pkcs12/SafeBagAsn.xml.cs | 50 +- .../Asn1/Pkcs7/ContentInfoAsn.xml.cs | 50 +- .../Asn1/Pkcs7/EncryptedContentInfoAsn.xml.cs | 45 +- .../Asn1/Pkcs7/EncryptedDataAsn.xml.cs | 30 +- .../Cryptography/Asn1/PrivateKeyInfoAsn.xml | 4 +- .../Asn1/PrivateKeyInfoAsn.xml.cs | 36 +- .../Cryptography/Asn1/PssParamsAsn.xml.cs | 98 +-- .../Cryptography/Asn1/RSAPrivateKeyAsn.xml | 4 +- .../Cryptography/Asn1/RSAPrivateKeyAsn.xml.cs | 34 +- .../Cryptography/Asn1/RSAPublicKeyAsn.xml.cs | 30 +- .../Cryptography/Asn1/Rc2CbcParameters.xml.cs | 32 +- .../Cryptography/Asn1/SpecifiedECDomain.xml | 2 +- .../Asn1/SpecifiedECDomain.xml.cs | 47 +- .../Asn1/SubjectPublicKeyInfoAsn.xml.cs | 34 +- .../Asn1/X509ExtensionAsn.manual.cs | 3 +- .../Cryptography/Asn1/X509ExtensionAsn.xml.cs | 56 +- .../System/Security/Cryptography/Asn1/asn.xsd | 31 +- .../Security/Cryptography/Asn1/asn.xslt | 227 +++---- .../Cryptography/Asn1Reader/AsnValueReader.cs | 232 +++++++ ...y.Cryptography.Asn1Reader.Shared.projitems | 106 +--- .../System/Security/Cryptography/CngPkcs8.cs | 218 ++++--- .../Cryptography/DSAKeyFormatHelper.cs | 71 ++- .../Cryptography/DSASecurityTransforms.cs | 52 +- .../Cryptography/EccKeyFormatHelper.cs | 150 +++-- .../Cryptography/EccSecurityTransforms.cs | 37 +- .../Security/Cryptography/KeyBlobHelpers.cs | 2 +- .../Security/Cryptography/KeyFormatHelper.cs | 193 +++--- .../Cryptography/PasswordBasedEncryption.cs | 73 ++- .../Cryptography/RSAKeyFormatHelper.cs | 74 ++- .../Security/Cryptography/RSAOpenSsl.cs | 40 +- .../Cryptography/RSASecurityTransforms.cs | 46 +- .../System.Formats.Asn1/Directory.Build.props | 1 + ...em.Security.Cryptography.Algorithms.csproj | 1 + .../src/System/Security/Cryptography/DSA.cs | 36 +- .../Security/Cryptography/ECDiffieHellman.cs | 49 +- .../src/System/Security/Cryptography/ECDsa.cs | 49 +- .../src/System/Security/Cryptography/RSA.cs | 150 +++-- .../System.Security.Cryptography.Cng.csproj | 3 +- .../Internal/Cryptography/AsnFormatter.OSX.cs | 3 +- ...stem.Security.Cryptography.Encoding.csproj | 1 + ...ecurity.Cryptography.Encoding.Tests.csproj | 42 -- ...ystem.Security.Cryptography.OpenSsl.csproj | 1 + .../Cryptography/Pal/AnyOS/AsnHelpers.cs | 39 +- .../Cryptography/Pal/AnyOS/ManagedPal.Asn.cs | 2 +- .../Pal/AnyOS/ManagedPal.Decode.cs | 2 +- .../Pal/AnyOS/ManagedPal.Decrypt.cs | 59 +- .../Pal/AnyOS/ManagedPal.Encrypt.cs | 39 +- .../Pal/AnyOS/ManagedPal.KeyTrans.cs | 14 +- .../Cryptography/Pal/AnyOS/ManagedPal.cs | 58 +- .../Pal/Windows/DecryptorPalWindows.Decode.cs | 2 +- .../Pal/Windows/PkcsPalWindows.Encrypt.cs | 39 +- .../src/Internal/Cryptography/PkcsHelpers.cs | 257 ++++---- .../System.Security.Cryptography.Pkcs.csproj | 3 +- .../Pkcs/Asn1/CadesIssuerSerial.xml.cs | 30 +- .../Pkcs/Asn1/CertificateChoiceAsn.xml.cs | 39 +- .../Asn1/EncapsulatedContentInfoAsn.xml.cs | 50 +- .../Pkcs/Asn1/EnvelopedDataAsn.xml.cs | 30 +- .../Cryptography/Pkcs/Asn1/EssCertId.xml.cs | 32 +- .../Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs | 45 +- .../Pkcs/Asn1/IssuerAndSerialNumberAsn.xml.cs | 39 +- .../KeyAgreeRecipientIdentifierAsn.xml.cs | 30 +- .../Pkcs/Asn1/KeyAgreeRecipientInfoAsn.xml.cs | 32 +- .../Pkcs/Asn1/KeyTransRecipientInfoAsn.xml.cs | 32 +- .../Pkcs/Asn1/MessageImprint.xml.cs | 32 +- .../Asn1/OriginatorIdentifierOrKeyAsn.xml.cs | 34 +- .../Pkcs/Asn1/OriginatorInfoAsn.xml.cs | 39 +- .../Pkcs/Asn1/OriginatorPublicKeyAsn.xml.cs | 34 +- .../Pkcs/Asn1/OtherKeyAttributeAsn.xml.cs | 50 +- .../Pkcs/Asn1/PkiStatusInfo.xml.cs | 39 +- .../Pkcs/Asn1/PolicyInformation.xml.cs | 41 +- .../Pkcs/Asn1/PolicyQualifierInfo.xml.cs | 50 +- .../Pkcs/Asn1/RecipientEncryptedKeyAsn.xml.cs | 32 +- .../Pkcs/Asn1/RecipientIdentifierAsn.xml.cs | 34 +- .../Pkcs/Asn1/RecipientInfoAsn.xml.cs | 30 +- .../Pkcs/Asn1/RecipientKeyIdentifier.xml.cs | 34 +- .../Pkcs/Asn1/Rfc3161Accuracy.xml.cs | 38 +- .../Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs | 54 +- .../Pkcs/Asn1/Rfc3161TimeStampResp.xml.cs | 39 +- .../Pkcs/Asn1/Rfc3161TstInfo.xml.cs | 56 +- .../Pkcs/Asn1/SecretBagAsn.xml.cs | 50 +- .../Pkcs/Asn1/SignedAttributesSet.xml.cs | 30 +- .../Pkcs/Asn1/SignedDataAsn.xml.cs | 39 +- .../Pkcs/Asn1/SignerIdentifierAsn.xml.cs | 34 +- .../Pkcs/Asn1/SignerInfoAsn.xml.cs | 41 +- .../Pkcs/Asn1/SigningCertificateAsn.xml.cs | 30 +- .../Pkcs/Asn1/SigningCertificateV2Asn.xml.cs | 30 +- .../Cryptography/Pkcs/CmsSignature.RSA.cs | 14 +- .../Cryptography/Pkcs/CmsSignature.cs | 8 +- .../Security/Cryptography/Pkcs/CmsSigner.cs | 47 +- .../Cryptography/Pkcs/Pkcs12Builder.cs | 44 +- .../Cryptography/Pkcs/Pkcs12CertBag.cs | 24 +- .../Security/Cryptography/Pkcs/Pkcs12Info.cs | 69 +- .../Cryptography/Pkcs/Pkcs12SafeBag.cs | 45 +- .../Cryptography/Pkcs/Pkcs12SafeContents.cs | 235 ++++--- .../Pkcs/Pkcs12SafeContentsBag.cs | 8 +- .../Cryptography/Pkcs/Pkcs12SecretBag.cs | 10 +- .../Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs | 82 ++- .../Cryptography/Pkcs/Pkcs9LocalKeyId.cs | 10 +- .../Cryptography/Pkcs/Pkcs9MessageDigest.cs | 12 +- .../Pkcs/Rfc3161TimestampRequest.cs | 46 +- .../Pkcs/Rfc3161TimestampToken.cs | 6 +- .../Pkcs/Rfc3161TimestampTokenInfo.cs | 36 +- .../Security/Cryptography/Pkcs/SignedCms.cs | 62 +- .../Security/Cryptography/Pkcs/SignerInfo.cs | 59 +- .../src/Internal/Cryptography/Helpers.cs | 128 ++-- .../Pal.OSX/AppleCertificatePal.cs | 11 +- .../Cryptography/Pal.OSX/ApplePkcs12Reader.cs | 5 +- .../Cryptography/Pal.OSX/CertificateData.cs | 136 ++-- .../Internal/Cryptography/Pal.OSX/X509Pal.cs | 40 +- .../Pal.Unix/CertificatePolicy.cs | 99 +-- .../Cryptography/Pal.Unix/CrlCache.cs | 5 + .../Pal.Unix/ManagedCertificateFinder.cs | 19 +- .../Pal.Unix/ManagedX509ExtensionProcessor.cs | 101 +-- .../Pal.Unix/OpenSslPkcs12Reader.cs | 5 +- .../Pal.Unix/OpenSslX509ChainProcessor.cs | 5 + .../Pal.Unix/OpenSslX509Encoder.cs | 28 +- .../Pal.Unix/UnixExportProvider.cs | 48 +- .../Cryptography/Pal.Unix/UnixPkcs12Reader.cs | 120 ++-- .../Pal.Unix/X500NameEncoder.ManagedDecode.cs | 200 +++--- .../Cryptography/Pal.Unix/X500NameEncoder.cs | 57 +- ...urity.Cryptography.X509Certificates.csproj | 1 + .../Asn1/AccessDescriptionAsn.xml.cs | 41 +- .../Asn1/BasicConstraintsAsn.xml.cs | 43 +- .../Asn1/CertificateAsn.xml.cs | 34 +- .../Asn1/CertificatePolicyMappingAsn.xml.cs | 52 +- .../Asn1/CertificateTemplateAsn.xml.cs | 41 +- .../Asn1/CertificationRequestAsn.xml.cs | 34 +- .../Asn1/CertificationRequestInfoAsn.xml.cs | 39 +- .../Asn1/DistributionPointAsn.xml.cs | 32 +- .../Asn1/DistributionPointNameAsn.xml.cs | 39 +- .../Asn1/PolicyConstraintsAsn.xml.cs | 38 +- .../Asn1/PolicyInformationAsn.xml.cs | 50 +- .../Asn1/TbsCertificateAsn.xml.cs | 77 ++- .../X509Certificates/Asn1/TimeAsn.xml.cs | 38 +- .../X509Certificates/Asn1/ValidityAsn.xml.cs | 30 +- .../X509Certificates/CertificateRequest.cs | 30 +- .../ECDsaX509SignatureGenerator.cs | 23 +- .../Pkcs10CertificationRequestInfo.cs | 29 +- .../X509Certificates/Pkcs9ExtensionRequest.cs | 14 +- .../RSAPkcs1X509SignatureGenerator.cs | 17 +- .../RSAPssX509SignatureGenerator.cs | 40 +- .../SubjectAlternativeNameBuilder.cs | 35 +- .../RevocationTests/CertificateAuthority.cs | 597 ++++++++---------- .../RevocationTests/RevocationResponder.cs | 4 +- 172 files changed, 5275 insertions(+), 3114 deletions(-) create mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs 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/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/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 index ba1f965d83cae7..749d7fc1c6b56d 100644 --- a/src/libraries/System.Formats.Asn1/Directory.Build.props +++ b/src/libraries/System.Formats.Asn1/Directory.Build.props @@ -2,5 +2,6 @@ Open + true 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/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; }