From b772f618078abcb16ddeb6eb0173e32ffe225399 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 8 Jun 2020 21:33:48 -0700 Subject: [PATCH] Make System.Formats.Asn1 library public * 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. * Make System.Formats.Asn1 package * Apply changes from API review Some additional tests were written to increase code coverage after the changes * Move AsnReader consumers to public API --- .../Cryptography/AsymmetricAlgorithmHelpers.Der.cs | 41 +- .../Interop.ASN1.GetIntegerBytes.cs | 18 +- .../Asn1/AlgorithmIdentifierAsn.xml.cs | 50 +- .../Cryptography/Asn1/AttributeAsn.manual.cs | 2 +- .../Security/Cryptography/Asn1/AttributeAsn.xml.cs | 50 +- .../Security/Cryptography/Asn1/CurveAsn.xml.cs | 38 +- .../Cryptography/Asn1/DigestInfoAsn.xml.cs | 32 +- .../Cryptography/Asn1/DirectoryStringAsn.xml.cs | 39 +- .../Security/Cryptography/Asn1/DssParms.xml.cs | 30 +- .../Cryptography/Asn1/ECDomainParameters.xml.cs | 41 +- .../Security/Cryptography/Asn1/ECPrivateKey.xml | 4 +- .../Security/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 +- .../Security/Cryptography/Asn1/OtherNameAsn.xml.cs | 50 +- .../Security/Cryptography/Asn1/PBEParameter.xml.cs | 32 +- .../Security/Cryptography/Asn1/PBES2Params.xml.cs | 30 +- .../Security/Cryptography/Asn1/Pbkdf2Params.xml | 4 +- .../Security/Cryptography/Asn1/Pbkdf2Params.xml.cs | 47 +- .../Cryptography/Asn1/Pbkdf2SaltChoice.xml.cs | 32 +- .../Cryptography/Asn1/Pkcs12/CertBagAsn.xml.cs | 50 +- .../Cryptography/Asn1/Pkcs12/MacData.xml.cs | 45 +- .../Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs | 2 +- .../Security/Cryptography/Asn1/Pkcs12/PfxAsn.xml | 2 +- .../Cryptography/Asn1/Pkcs12/PfxAsn.xml.cs | 34 +- .../Cryptography/Asn1/Pkcs12/SafeBagAsn.xml.cs | 50 +- .../Cryptography/Asn1/Pkcs7/ContentInfoAsn.xml.cs | 50 +- .../Asn1/Pkcs7/EncryptedContentInfoAsn.xml.cs | 45 +- .../Asn1/Pkcs7/EncryptedDataAsn.xml.cs | 30 +- .../Cryptography/Asn1/PrivateKeyInfoAsn.xml | 4 +- .../Cryptography/Asn1/PrivateKeyInfoAsn.xml.cs | 36 +- .../Security/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 +- .../Cryptography/Asn1/SpecifiedECDomain.xml.cs | 47 +- .../Asn1/SubjectPublicKeyInfoAsn.xml.cs | 34 +- .../Cryptography/Asn1/X509ExtensionAsn.manual.cs | 3 +- .../Cryptography/Asn1/X509ExtensionAsn.xml.cs | 56 +- .../src/System/Security/Cryptography/Asn1/asn.xsd | 31 +- .../src/System/Security/Cryptography/Asn1/asn.xslt | 227 ++-- .../Cryptography/Asn1Reader/AsnReader.BitString.cs | 919 --------------- .../Cryptography/Asn1Reader/AsnReader.Boolean.cs | 124 -- .../Asn1Reader/AsnReader.Enumerated.cs | 401 ------- .../Cryptography/Asn1Reader/AsnReader.Integer.cs | 1217 -------------------- .../Asn1Reader/AsnReader.NamedBitList.cs | 427 ------- .../Cryptography/Asn1Reader/AsnReader.Null.cs | 84 -- .../Asn1Reader/AsnReader.OctetString.cs | 789 ------------- .../Cryptography/Asn1Reader/AsnReader.Sequence.cs | 142 --- .../Cryptography/Asn1Reader/AsnReader.SetOf.cs | 184 --- .../Cryptography/Asn1Reader/AsnReader.Text.cs | 1163 ------------------- .../Security/Cryptography/Asn1Reader/AsnReader.cs | 675 ----------- .../Cryptography/Asn1Reader/AsnValueReader.cs | 232 ++++ .../Cryptography/Asn1Reader/AsnWriter.BitString.cs | 387 ------- .../Asn1Reader/AsnWriter.Enumerated.cs | 134 --- .../Asn1Reader/AsnWriter.NamedBitList.cs | 168 --- .../Cryptography/Asn1Reader/AsnWriter.Sequence.cs | 93 -- .../Cryptography/Asn1Reader/AsnWriter.Text.cs | 203 ---- ...curity.Cryptography.Asn1Reader.Shared.projitems | 106 +- .../src/System/Security/Cryptography/CngPkcs8.cs | 218 ++-- .../Security/Cryptography/DSAKeyFormatHelper.cs | 71 +- .../Security/Cryptography/DSASecurityTransforms.cs | 52 +- .../Security/Cryptography/EccKeyFormatHelper.cs | 150 +-- .../Security/Cryptography/EccSecurityTransforms.cs | 37 +- .../System/Security/Cryptography/KeyBlobHelpers.cs | 2 +- .../Security/Cryptography/KeyFormatHelper.cs | 193 ++-- .../Cryptography/PasswordBasedEncryption.cs | 73 +- .../Security/Cryptography/RSAKeyFormatHelper.cs | 74 +- .../src/System/Security/Cryptography/RSAOpenSsl.cs | 40 +- .../Security/Cryptography/RSASecurityTransforms.cs | 46 +- .../System.Formats.Asn1/Directory.Build.props | 7 + .../System.Formats.Asn1/System.Formats.Asn1.sln | 53 + .../pkg/System.Formats.Asn1.pkgproj | 9 + .../System.Formats.Asn1/ref/System.Formats.Asn1.cs | 243 ++++ .../ref/System.Formats.Asn1.csproj | 12 + .../System.Formats.Asn1/src/Resources/Strings.resx | 156 +++ .../src/System.Formats.Asn1.csproj | 53 + .../System/Formats/Asn1}/Asn1Tag.Accelerators.cs | 6 +- .../src/System/Formats/Asn1}/Asn1Tag.cs | 176 ++- .../Formats/Asn1}/AsnCharacterStringEncodings.cs | 92 +- .../src/System/Formats/Asn1/AsnContentException.cs | 32 + .../System/Formats/Asn1/AsnDecoder.BitString.cs | 854 ++++++++++++++ .../src/System/Formats/Asn1/AsnDecoder.Boolean.cs | 120 ++ .../System/Formats/Asn1/AsnDecoder.Enumerated.cs | 412 +++++++ .../Formats/Asn1/AsnDecoder.GeneralizedTime.cs} | 179 ++- .../src/System/Formats/Asn1/AsnDecoder.Integer.cs | 747 ++++++++++++ .../System/Formats/Asn1/AsnDecoder.NamedBitList.cs | 593 ++++++++++ .../src/System/Formats/Asn1/AsnDecoder.Null.cs | 97 ++ .../System/Formats/Asn1/AsnDecoder.OctetString.cs | 686 +++++++++++ .../src/System/Formats/Asn1/AsnDecoder.Oid.cs} | 214 ++-- .../src/System/Formats/Asn1/AsnDecoder.Sequence.cs | 149 +++ .../src/System/Formats/Asn1/AsnDecoder.SetOf.cs | 227 ++++ .../src/System/Formats/Asn1/AsnDecoder.Text.cs | 784 +++++++++++++ .../src/System/Formats/Asn1/AsnDecoder.UtcTime.cs} | 196 ++-- .../src/System/Formats/Asn1/AsnDecoder.cs | 803 +++++++++++++ .../src/System/Formats/Asn1}/AsnEncodingRules.cs | 4 +- .../src/System/Formats/Asn1/AsnReaderOptions.cs | 53 + .../src/System/Formats/Asn1/AsnWriter.BitString.cs | 197 ++++ .../src/System/Formats/Asn1}/AsnWriter.Boolean.cs | 20 +- .../System/Formats/Asn1/AsnWriter.Enumerated.cs | 100 ++ .../Formats/Asn1}/AsnWriter.GeneralizedTime.cs | 47 +- .../src/System/Formats/Asn1}/AsnWriter.Integer.cs | 130 +-- .../System/Formats/Asn1/AsnWriter.NamedBitList.cs | 199 ++++ .../src/System/Formats/Asn1}/AsnWriter.Null.cs | 22 +- .../System/Formats/Asn1}/AsnWriter.OctetString.cs | 68 +- .../src/System/Formats/Asn1}/AsnWriter.Oid.cs | 149 +-- .../src/System/Formats/Asn1/AsnWriter.Sequence.cs | 71 ++ .../src/System/Formats/Asn1}/AsnWriter.SetOf.cs | 74 +- .../src/System/Formats/Asn1/AsnWriter.Text.cs | 133 +++ .../src/System/Formats/Asn1}/AsnWriter.UtcTime.cs | 70 +- .../src/System/Formats/Asn1}/AsnWriter.cs | 400 +++++-- .../src/System/Formats/Asn1}/SetOfValueComparer.cs | 9 +- .../src/System/Formats/Asn1}/TagClass.cs | 4 +- .../src/System/Formats/Asn1}/UniversalTagNumber.cs | 4 +- .../System.Formats.Asn1/tests/Asn1TagTests.cs | 245 ++++ .../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 | 226 ++++ .../tests/Reader/ReadEnumerated.cs | 802 +++++++++++++ .../tests}/Reader/ReadGeneralizedTime.cs | 213 ++-- .../tests}/Reader/ReadIA5String.cs | 396 ++++--- .../tests/Reader/ReadInteger.cs | 448 +++++++ .../System.Formats.Asn1/tests/Reader/ReadLength.cs | 180 +++ .../tests/Reader/ReadNamedBitList.cs | 518 +++++++++ .../System.Formats.Asn1/tests/Reader/ReadNull.cs | 130 +++ .../tests}/Reader/ReadObjectIdentifier.cs | 196 ++-- .../tests}/Reader/ReadOctetString.cs | 353 +++--- .../tests/Reader/ReadSequence.cs | 410 +++++++ .../System.Formats.Asn1/tests/Reader/ReadSetOf.cs | 395 +++++++ .../tests}/Reader/ReadT61String.cs | 37 +- .../tests}/Reader/ReadUTF8String.cs | 387 ++++--- .../tests/Reader/ReadUtcTime.cs | 299 +++++ .../tests}/Reader/ReaderStateTests.cs | 5 +- .../tests/System.Formats.Asn1.Tests.csproj | 55 + .../tests}/Writer/Asn1WriterTests.cs | 13 +- .../tests/Writer/ComprehensiveWriteTest.cs | 322 ++++++ .../tests/Writer/PushPopOctetString.cs | 230 ++++ .../tests/Writer/PushPopSequence.cs | 602 ++++++++++ .../tests/Writer/PushPopSetOf.cs | 585 ++++++++++ .../tests/Writer/SimpleWriterTests.cs | 143 +++ .../tests}/Writer/WriteBMPString.cs | 55 +- .../tests/Writer/WriteBitString.cs | 315 +++++ .../tests/Writer/WriteBoolean.cs | 80 ++ .../tests/Writer/WriteCharacterString.cs | 421 +++++++ .../tests/Writer/WriteEncodedValue.cs | 102 ++ .../tests/Writer/WriteEnumerated.cs | 373 ++++++ .../tests}/Writer/WriteGeneralizedTime.cs | 140 +-- .../tests}/Writer/WriteIA5String.cs | 55 +- .../tests/Writer/WriteInteger.cs | 454 ++++++++ .../tests/Writer/WriteNamedBitList.cs | 383 ++++++ .../System.Formats.Asn1/tests/Writer/WriteNull.cs | 49 + .../tests/Writer/WriteObjectIdentifier.cs | 235 ++++ .../tests/Writer/WriteOctetString.cs | 155 +++ .../tests/Writer/WriteUtcTime.cs | 201 ++++ .../tests}/Writer/WriteUtf8String.cs | 55 +- .../System.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 +-- .../src/System.Security.Cryptography.Cng.csproj | 3 +- .../src/Internal/Cryptography/AsnFormatter.OSX.cs | 3 +- .../System.Security.Cryptography.Encoding.csproj | 1 + .../tests/Asn1/Reader/Asn1ReaderTests.cs | 26 - .../tests/Asn1/Reader/ReadBoolean.cs | 221 ---- .../tests/Asn1/Reader/ReadEnumerated.cs | 760 ------------ .../tests/Asn1/Reader/ReadInteger.cs | 606 ---------- .../tests/Asn1/Reader/ReadLength.cs | 176 --- .../tests/Asn1/Reader/ReadNamedBitList.cs | 315 ----- .../tests/Asn1/Reader/ReadNull.cs | 131 --- .../tests/Asn1/Reader/ReadSequence.cs | 339 ------ .../tests/Asn1/Reader/ReadSetOf.cs | 305 ----- .../tests/Asn1/Reader/ReadUtcTime.cs | 239 ---- .../tests/Asn1/Writer/ComprehensiveWriteTest.cs | 290 ----- .../tests/Asn1/Writer/PushPopSequence.cs | 639 ---------- .../tests/Asn1/Writer/PushPopSetOf.cs | 622 ---------- .../tests/Asn1/Writer/WriteBitString.cs | 366 ------ .../tests/Asn1/Writer/WriteBoolean.cs | 114 -- .../tests/Asn1/Writer/WriteCharacterString.cs | 548 --------- .../tests/Asn1/Writer/WriteEnumerated.cs | 526 --------- .../tests/Asn1/Writer/WriteInteger.cs | 493 -------- .../tests/Asn1/Writer/WriteNamedBitList.cs | 347 ------ .../tests/Asn1/Writer/WriteNull.cs | 81 -- .../tests/Asn1/Writer/WriteObjectIdentifier.cs | 533 --------- .../tests/Asn1/Writer/WriteOctetString.cs | 193 ---- .../tests/Asn1/Writer/WriteUtcTime.cs | 273 ----- ...tem.Security.Cryptography.Encoding.Tests.csproj | 42 - .../System.Security.Cryptography.OpenSsl.csproj | 1 + .../Internal/Cryptography/Pal/AnyOS/AsnHelpers.cs | 39 +- .../Cryptography/Pal/AnyOS/ManagedPal.Asn.cs | 2 +- .../Cryptography/Pal/AnyOS/ManagedPal.Decode.cs | 2 +- .../Cryptography/Pal/AnyOS/ManagedPal.Decrypt.cs | 59 +- .../Cryptography/Pal/AnyOS/ManagedPal.Encrypt.cs | 39 +- .../Cryptography/Pal/AnyOS/ManagedPal.KeyTrans.cs | 14 +- .../Internal/Cryptography/Pal/AnyOS/ManagedPal.cs | 58 +- .../Pal/Windows/DecryptorPalWindows.Decode.cs | 2 +- .../Pal/Windows/PkcsPalWindows.Encrypt.cs | 39 +- .../src/Internal/Cryptography/PkcsHelpers.cs | 257 +++-- .../src/System.Security.Cryptography.Pkcs.csproj | 3 +- .../Pkcs/Asn1/CadesIssuerSerial.xml.cs | 30 +- .../Pkcs/Asn1/CertificateChoiceAsn.xml.cs | 39 +- .../Pkcs/Asn1/EncapsulatedContentInfoAsn.xml.cs | 50 +- .../Cryptography/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 +- .../Asn1/KeyAgreeRecipientIdentifierAsn.xml.cs | 30 +- .../Pkcs/Asn1/KeyAgreeRecipientInfoAsn.xml.cs | 32 +- .../Pkcs/Asn1/KeyTransRecipientInfoAsn.xml.cs | 32 +- .../Cryptography/Pkcs/Asn1/MessageImprint.xml.cs | 32 +- .../Pkcs/Asn1/OriginatorIdentifierOrKeyAsn.xml.cs | 34 +- .../Pkcs/Asn1/OriginatorInfoAsn.xml.cs | 39 +- .../Pkcs/Asn1/OriginatorPublicKeyAsn.xml.cs | 34 +- .../Pkcs/Asn1/OtherKeyAttributeAsn.xml.cs | 50 +- .../Cryptography/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 +- .../Cryptography/Pkcs/Asn1/RecipientInfoAsn.xml.cs | 30 +- .../Pkcs/Asn1/RecipientKeyIdentifier.xml.cs | 34 +- .../Cryptography/Pkcs/Asn1/Rfc3161Accuracy.xml.cs | 38 +- .../Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs | 54 +- .../Pkcs/Asn1/Rfc3161TimeStampResp.xml.cs | 39 +- .../Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs | 56 +- .../Cryptography/Pkcs/Asn1/SecretBagAsn.xml.cs | 50 +- .../Pkcs/Asn1/SignedAttributesSet.xml.cs | 30 +- .../Cryptography/Pkcs/Asn1/SignedDataAsn.xml.cs | 39 +- .../Pkcs/Asn1/SignerIdentifierAsn.xml.cs | 34 +- .../Cryptography/Pkcs/Asn1/SignerInfoAsn.xml.cs | 41 +- .../Pkcs/Asn1/SigningCertificateAsn.xml.cs | 30 +- .../Pkcs/Asn1/SigningCertificateV2Asn.xml.cs | 30 +- .../Security/Cryptography/Pkcs/CmsSignature.RSA.cs | 14 +- .../Security/Cryptography/Pkcs/CmsSignature.cs | 8 +- .../System/Security/Cryptography/Pkcs/CmsSigner.cs | 47 +- .../Security/Cryptography/Pkcs/Pkcs12Builder.cs | 44 +- .../Security/Cryptography/Pkcs/Pkcs12CertBag.cs | 24 +- .../Security/Cryptography/Pkcs/Pkcs12Info.cs | 69 +- .../Security/Cryptography/Pkcs/Pkcs12SafeBag.cs | 45 +- .../Cryptography/Pkcs/Pkcs12SafeContents.cs | 235 ++-- .../Cryptography/Pkcs/Pkcs12SafeContentsBag.cs | 8 +- .../Security/Cryptography/Pkcs/Pkcs12SecretBag.cs | 10 +- .../Cryptography/Pkcs/Pkcs8PrivateKeyInfo.cs | 82 +- .../Security/Cryptography/Pkcs/Pkcs9LocalKeyId.cs | 10 +- .../Cryptography/Pkcs/Pkcs9MessageDigest.cs | 12 +- .../Cryptography/Pkcs/Rfc3161TimestampRequest.cs | 46 +- .../Cryptography/Pkcs/Rfc3161TimestampToken.cs | 6 +- .../Cryptography/Pkcs/Rfc3161TimestampTokenInfo.cs | 36 +- .../System/Security/Cryptography/Pkcs/SignedCms.cs | 62 +- .../Security/Cryptography/Pkcs/SignerInfo.cs | 59 +- .../src/Internal/Cryptography/Helpers.cs | 128 +- .../Cryptography/Pal.OSX/AppleCertificatePal.cs | 11 +- .../Cryptography/Pal.OSX/ApplePkcs12Reader.cs | 5 +- .../Cryptography/Pal.OSX/CertificateData.cs | 136 ++- .../src/Internal/Cryptography/Pal.OSX/X509Pal.cs | 40 +- .../Cryptography/Pal.Unix/CertificatePolicy.cs | 99 +- .../src/Internal/Cryptography/Pal.Unix/CrlCache.cs | 5 + .../Pal.Unix/ManagedCertificateFinder.cs | 19 +- .../Pal.Unix/ManagedX509ExtensionProcessor.cs | 101 +- .../Cryptography/Pal.Unix/OpenSslPkcs12Reader.cs | 5 +- .../Pal.Unix/OpenSslX509ChainProcessor.cs | 5 + .../Cryptography/Pal.Unix/OpenSslX509Encoder.cs | 28 +- .../Cryptography/Pal.Unix/UnixExportProvider.cs | 48 +- .../Cryptography/Pal.Unix/UnixPkcs12Reader.cs | 120 +- .../Pal.Unix/X500NameEncoder.ManagedDecode.cs | 200 ++-- .../Cryptography/Pal.Unix/X500NameEncoder.cs | 57 +- ...m.Security.Cryptography.X509Certificates.csproj | 1 + .../Asn1/AccessDescriptionAsn.xml.cs | 41 +- .../Asn1/BasicConstraintsAsn.xml.cs | 43 +- .../X509Certificates/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 +- .../X509Certificates/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 +- .../tests/RevocationTests/CertificateAuthority.cs | 597 +++++----- .../tests/RevocationTests/RevocationResponder.cs | 4 +- src/libraries/pkg/baseline/packageIndex.json | 6 + src/libraries/pkg/descriptions.json | 8 + 301 files changed, 22996 insertions(+), 20798 deletions(-) delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs create mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs 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/Resources/Strings.resx create mode 100644 src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/Asn1Tag.Accelerators.cs (94%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/Asn1Tag.cs (72%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnCharacterStringEncodings.cs (82%) 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/AsnDecoder.BitString.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs} (67%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs} (56%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs => System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs} (60%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnEncodingRules.cs (89%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Boolean.cs (65%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.GeneralizedTime.cs (75%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Integer.cs (65%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Null.cs (58%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.OctetString.cs (58%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.Oid.cs (55%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.SetOf.cs (51%) create mode 100644 src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.UtcTime.cs (59%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/AsnWriter.cs (55%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/SetOfValueComparer.cs (91%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/TagClass.cs (91%) rename src/libraries/{Common/src/System/Security/Cryptography/Asn1Reader => System.Formats.Asn1/src/System/Formats/Asn1}/UniversalTagNumber.cs (98%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ComprehensiveReadTests.cs (93%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/OverlappedReads.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ParseTag.cs (58%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/PeekTests.cs (84%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadBMPString.cs (57%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadBitString.cs (50%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadGeneralizedTime.cs (65%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadIA5String.cs (57%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadObjectIdentifier.cs (51%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadOctetString.cs (50%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadT61String.cs (79%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReadUTF8String.cs (55%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Reader/ReaderStateTests.cs (85%) create mode 100644 src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/Asn1WriterTests.cs (81%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteBMPString.cs (88%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteGeneralizedTime.cs (58%) rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteIA5String.cs (87%) create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs create mode 100644 src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs rename src/libraries/{System.Security.Cryptography.Encoding/tests/Asn1 => System.Formats.Asn1/tests}/Writer/WriteUtf8String.cs (87%) delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs delete mode 100644 src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs diff --git a/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Der.cs b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Der.cs index 40f035a..aef49d2 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 @@ namespace Internal.Cryptography /// 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 @@ namespace Internal.Cryptography 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 @@ namespace Internal.Cryptography 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 9026991..d7606b1 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 partial class Interop (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 ccea20e..6d907ad 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -56,6 +76,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 65eb4e6..2c36126 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 @@ namespace System.Security.Cryptography.Asn1 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 47e0d86..4e4ab39 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -59,6 +79,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader collectionReader; 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 83f2507..eb2983a 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 @@ namespace System.Security.Cryptography.Asn1 if (Seed.HasValue) { - writer.WriteBitString(Seed.Value.Span); + writer.WriteBitString(Seed.Value.Span, 0); } writer.PopSequence(tag); @@ -44,11 +43,18 @@ namespace System.Security.Cryptography.Asn1 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) @@ -58,6 +64,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -65,7 +83,7 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } - 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 @@ namespace System.Security.Cryptography.Asn1 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 64c0b1b..e242ec2 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -59,7 +77,7 @@ namespace System.Security.Cryptography.Asn1 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 eda1cfd..abc27ba 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 @@ namespace System.Security.Cryptography.Asn1 } } - 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,15 +113,34 @@ namespace System.Security.Cryptography.Asn1 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(); ReadOnlySpan rebindSpan = rebind.Span; 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 a45f9f5..8839e64 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -53,6 +59,18 @@ namespace System.Security.Cryptography.Asn1 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 727797d..5d481c4 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 @@ namespace System.Security.Cryptography.Asn1 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,15 +71,34 @@ namespace System.Security.Cryptography.Asn1 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 c881bb0..c5fcf18 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 bb43eed..c416a84 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -69,6 +75,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader explicitReader; @@ -77,13 +95,13 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 { 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 b1b5c40..3d70d7a 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -60,6 +66,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader explicitReader; 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 5e1dbe8..598aafc 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -59,7 +77,7 @@ namespace System.Security.Cryptography.Asn1 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 b95903d..6344dc6 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,13 +71,25 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 5525fc3..1a26d9f 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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,15 +173,34 @@ namespace System.Security.Cryptography.Asn1 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(); AsnValueReader explicitReader; @@ -178,11 +217,11 @@ namespace System.Security.Cryptography.Asn1 } 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 @@ namespace System.Security.Cryptography.Asn1 } 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 @@ namespace System.Security.Cryptography.Asn1 } 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 06fb623..b81ec3a 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -128,6 +125,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader explicitReader; 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 18bf247..42a4147 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -53,6 +73,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader explicitReader; @@ -60,7 +92,7 @@ namespace System.Security.Cryptography.Asn1 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 2544f2b..0d55b3d 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -58,7 +76,7 @@ namespace System.Security.Cryptography.Asn1 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 2d45e58..a7b6f9e 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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 271a95a..3f9723b 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 7873090..ca7f36c 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -89,6 +92,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader defaultReader; @@ -104,7 +119,7 @@ namespace System.Security.Cryptography.Asn1 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 9a1c275..7c2f026 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,15 +64,34 @@ namespace System.Security.Cryptography.Asn1 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(); ReadOnlySpan rebindSpan = rebind.Span; @@ -83,7 +101,7 @@ namespace System.Security.Cryptography.Asn1 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 fe4ac88..38fef15 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 { 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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) @@ -53,6 +73,18 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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); AsnValueReader explicitReader; @@ -60,7 +92,7 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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 da40456..c7b0ae0 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 // 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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) @@ -86,6 +89,18 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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); AsnValueReader defaultReader; @@ -95,7 +110,7 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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 4c7dd5a..e9004e5 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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 a46703b..33dd50b 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 5364bdf..56dfa3c 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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) @@ -58,11 +64,23 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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 c34015f..dd548c6 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 { 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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) @@ -68,6 +88,18 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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); AsnValueReader explicitReader; @@ -76,7 +108,7 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 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 d7ef164..ed94ab2 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 { 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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) @@ -53,6 +73,18 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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); AsnValueReader explicitReader; @@ -60,7 +92,7 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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 b915bea..2b9466a 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 { 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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) @@ -58,19 +71,31 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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); ReadOnlySpan rebindSpan = rebind.Span; 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 fffd2c0..2342c2b 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 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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) @@ -66,6 +72,18 @@ namespace System.Security.Cryptography.Asn1.Pkcs7 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); AsnValueReader collectionReader; 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 830e897..bfd83bb 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 648489e..f8cece5 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -68,6 +74,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader collectionReader; @@ -76,14 +94,14 @@ namespace System.Security.Cryptography.Asn1 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 6c42acd..3aa8100 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -162,6 +156,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader explicitReader; 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 7c4f2a4..0947908 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 a5ffdaf..476b201 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -65,11 +71,23 @@ namespace System.Security.Cryptography.Asn1 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 ee46993..5fb986a 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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 580692b..e31fc76 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -64,7 +82,7 @@ namespace System.Security.Cryptography.Asn1 } - 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 ee3b485..7505619 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 dbe070a..ae447d0 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -71,6 +84,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -78,7 +103,7 @@ namespace System.Security.Cryptography.Asn1 ReadOnlySpan tmpSpan; - if (!sequenceReader.TryReadUInt8(out decoded.Version)) + if (!sequenceReader.TryReadInt32(out decoded.Version)) { sequenceReader.ThrowIfNotEmpty(); } @@ -86,7 +111,7 @@ namespace System.Security.Cryptography.Asn1 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 24125bc..bb2fbdd 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 @@ namespace System.Security.Cryptography.Asn1 writer.PushSequence(tag); Algorithm.Encode(writer); - writer.WriteBitString(SubjectPublicKey.Span); + writer.WriteBitString(SubjectPublicKey.Span, 0); writer.PopSequence(tag); } @@ -37,11 +36,18 @@ namespace System.Security.Cryptography.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -59,7 +77,7 @@ namespace System.Security.Cryptography.Asn1 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 3c226d8..6b9ab9f 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 @@ namespace System.Security.Cryptography.Asn1 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 45ead32..6652878 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 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) @@ -81,6 +91,18 @@ namespace System.Security.Cryptography.Asn1 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); AsnValueReader defaultReader; @@ -101,7 +123,7 @@ namespace System.Security.Cryptography.Asn1 } - 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 d8560ad..90a5235 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 3433cd1..12f7ebf 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) @@ -107,6 +113,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); AsnValueReader explicitReader; @@ -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,15 +186,34 @@ 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(); AsnValueReader explicitReader; @@ -208,7 +244,7 @@ namespace - Error: neiher optional or defaultDerInit may be specified for fields in a Choice type () + Error: neither optional or defaultDerInit may be specified for fields in a Choice type () @@ -230,22 +266,20 @@ namespace + // DEFAULT value handler for . { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); - + - ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan(); - if (!encoded.SequenceEqual()) - { - writer.WriteEncodedValue(encoded); - } + if (!tmp.EncodedValueEquals()) + { + tmp.CopyTo(writer); } } @@ -304,19 +338,16 @@ namespace // DEFAULT value handler for . { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - - - - ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual()) - { - writer.PushSequence(); - writer.WriteEncodedValue(encoded); - writer.PopSequence(); - } + AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); + + + + + if (!tmp.EncodedValueEquals()) + { + writer.PushSequence(); + tmp.CopyTo(writer); + writer.PopSequence(); } } @@ -414,11 +445,11 @@ namespace tmp; - .Decode(ref , rebind, out tmp); + .Decode(ref , rebind, out tmp); = tmp; - .Decode(ref , rebind, out ); + .Decode(ref , rebind, out ); @@ -445,7 +476,14 @@ namespace } - .WriteEncodedValue(.Value.Span); + try + { + .WriteEncodedValue(.Value.Span); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } @@ -482,7 +520,7 @@ namespace - .WriteBoolean(.Value); + .WriteBoolean(.Value); @@ -501,15 +539,11 @@ namespace internal ReadOnlyMemory<byte>? ; - - internal byte? ; - internal int? ; System.Numerics.BigInteger ReadOnlyMemory<byte> - byte int @@ -518,7 +552,7 @@ namespace - .WriteInteger(.Value); + .WriteInteger(.Value); @@ -527,7 +561,7 @@ namespace - .WriteInteger(.Value.Span); + .WriteInteger(.Value.Span); @@ -546,32 +580,6 @@ namespace tmpSpan = .ReadIntegerBytes(); - - - - - - - - if (.TryReadUInt8(out byte tmp)) - { - = tmp; - } - else - { - .ThrowIfNotEmpty(); - } - - - - if (!.TryReadUInt8(out )) - { - .ThrowIfNotEmpty(); - } - - - - @@ -579,7 +587,7 @@ namespace - if (.TryReadInt32(out int tmp)) + if (.TryReadInt32(out int tmp)) { = tmp; } @@ -590,7 +598,7 @@ namespace - if (!.TryReadInt32(out )) + if (!.TryReadInt32(out )) { .ThrowIfNotEmpty(); } @@ -611,7 +619,7 @@ namespace - .WriteBitString(.Value.Span); + .WriteBitString(.Value.Span, 0); @@ -620,12 +628,12 @@ namespace - if (.TryReadPrimitiveBitStringValue(out _, out tmpSpan)) + if (.TryReadPrimitiveBitString(out _, out tmpSpan)) { } else { - = .ReadBitString(out _); + = .ReadBitString(out _); } @@ -643,7 +651,7 @@ namespace - .WriteNamedBitList(.Value); + .WriteNamedBitList(.Value); @@ -667,7 +675,7 @@ namespace - .WriteOctetString(.Value.Span); + .WriteOctetString(.Value.Span); @@ -676,7 +684,7 @@ namespace - if (.TryReadPrimitiveOctetStringBytes(out tmpSpan)) + if (.TryReadPrimitiveOctetString(out tmpSpan)) { } else @@ -688,13 +696,10 @@ namespace Asn1Tag.PrimitiveOctetString - - internal Oid? ; - + internal string? ; - Oid - string + string @@ -704,11 +709,18 @@ namespace - .WriteObjectIdentifier(); + try + { + .WriteObjectIdentifier(); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } - + @@ -720,18 +732,6 @@ namespace - - - - - - - - - = .ReadObjectIdentifierAsString(); - - - Asn1Tag.ObjectIdentifier @@ -745,7 +745,7 @@ namespace - .WriteEnumeratedValue(.Value); + .WriteEnumeratedValue(.Value); @@ -769,7 +769,7 @@ namespace - .WriteCharacterString(UniversalTagNumber., ); + .WriteCharacterString(UniversalTagNumber., ); @@ -777,7 +777,7 @@ namespace - = .ReadCharacterString(UniversalTagNumber.); + = .ReadCharacterString(UniversalTagNumber.); new Asn1Tag(UniversalTagNumber.) @@ -846,7 +846,7 @@ namespace - .WriteUtcTime(.Value); + .WriteUtcTime(.Value); @@ -855,7 +855,7 @@ namespace - = .ReadUtcTime(); + = .ReadUtcTime(); = .ReadUtcTime(); @@ -870,9 +870,9 @@ namespace - .WriteGeneralizedTime(.Value, omitFractionalSeconds: ); + .WriteGeneralizedTime(.Value, omitFractionalSeconds: ); - .WriteGeneralizedTime(.Value); + .WriteGeneralizedTime(.Value, false); @@ -880,12 +880,16 @@ namespace - - - = .ReadGeneralizedTime(disallowFractions: ); - - = .ReadGeneralizedTime(); - + + + = .ReadGeneralizedTime(); + + + if (!.Value.Ticks % TimeSpan.TicksPerSecond != 0) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + Asn1Tag.GeneralizedTime @@ -893,9 +897,6 @@ namespace - - , - , diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs deleted file mode 100644 index e777cc1..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.BitString.cs +++ /dev/null @@ -1,919 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlySpan value) - => TryReadPrimitiveBitStringValue(Asn1Tag.PrimitiveBitString, out unusedBitCount, out value); - - /// - /// Reads the next value as a BIT STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, - out int unusedBitCount, - out ReadOnlySpan value) - { - bool isPrimitive = TryReadPrimitiveBitStringValue( - expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out unusedBitCount, - out value, - out byte normalizedLastByte); - - if (isPrimitive) - { - // A BER reader which encountered a situation where an "unused" bit was not - // set to 0. - if (value.Length != 0 && normalizedLastByte != value[value.Length - 1]) - { - unusedBitCount = 0; - value = default; - return false; - } - - // Skip the tag+length (header) and the unused bit count byte (1) and the contents. - _data = _data.Slice(headerLength + value.Length + 1); - } - - return isPrimitive; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyBitStringBytes( - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - Asn1Tag.PrimitiveBitString, - destination, - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - if (TryReadPrimitiveBitStringValue( - expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out unusedBitCount, - out ReadOnlySpan value, - out byte normalizedLastByte)) - { - if (value.Length > destination.Length) - { - bytesWritten = 0; - unusedBitCount = 0; - return false; - } - - CopyBitStringValue(value, normalizedLastByte, destination); - - bytesWritten = value.Length; - // contents doesn't include the unusedBitCount value, so add one byte for that. - _data = _data.Slice(headerLength + value.Length + 1); - return true; - } - - Debug.Assert(actualTag.IsConstructed); - - bool read = TryCopyConstructedBitStringValue( - Slice(_data, headerLength, contentsLength), - destination, - contentsLength == null, - out unusedBitCount, - out int bytesRead, - out bytesWritten); - - if (read) - { - _data = _data.Slice(headerLength + bytesRead); - } - - return read; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public byte[] ReadBitString(out int unusedBitCount) - { - return ReadBitString(Asn1Tag.PrimitiveBitString, out unusedBitCount); - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public byte[] ReadBitString(Asn1Tag expectedTag, out int unusedBitCount) - { - ReadOnlySpan span; - - if (TryReadPrimitiveBitStringValue(expectedTag, out unusedBitCount, out span)) - { - return span.ToArray(); - } - - span = PeekEncodedValue(); - - // Guaranteed long enough - byte[] rented = CryptoPool.Rent(span.Length); - int dataLength = 0; - - try - { - if (!TryCopyBitStringBytes(expectedTag, rented, out unusedBitCount, out dataLength)) - { - Debug.Fail("TryCopyBitStringBytes failed with a pre-allocated buffer"); - throw new CryptographicException(); - } - - byte[] alloc = new byte[dataLength]; - rented.AsSpan(0, dataLength).CopyTo(alloc); - return alloc; - } - finally - { - CryptoPool.Return(rented, dataLength); - } - } - - private void ParsePrimitiveBitStringContents( - ReadOnlySpan source, - out int unusedBitCount, - out ReadOnlySpan value, - out byte normalizedLastByte) - { - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER && source.Length > MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - // T-REC-X.690-201508 sec 8.6.2.3 - if (source.Length == 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - unusedBitCount = source[0]; - - // T-REC-X.690-201508 sec 8.6.2.2 - if (unusedBitCount > 7) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (source.Length == 1) - { - // T-REC-X.690-201508 sec 8.6.2.4 - if (unusedBitCount > 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(unusedBitCount == 0); - value = ReadOnlySpan.Empty; - normalizedLastByte = 0; - return; - } - - // Build a mask for the bits that are used so the normalized value can be computed - // - // If 3 bits are "unused" then build a mask for them to check for 0. - // -1 << 3 => 0b1111_1111 << 3 => 0b1111_1000 - int mask = -1 << unusedBitCount; - byte lastByte = source[source.Length - 1]; - byte maskedByte = (byte)(lastByte & mask); - - if (maskedByte != lastByte) - { - // T-REC-X.690-201508 sec 11.2.1 - if (RuleSet == AsnEncodingRules.DER || RuleSet == AsnEncodingRules.CER) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - normalizedLastByte = maskedByte; - value = source.Slice(1); - } - - private delegate void BitStringCopyAction( - ReadOnlySpan value, - byte normalizedLastByte, - Span destination); - - private static void CopyBitStringValue( - ReadOnlySpan value, - byte normalizedLastByte, - Span destination) - { - if (value.Length == 0) - { - return; - } - - value.CopyTo(destination); - // Replace the last byte with the normalized answer. - destination[value.Length - 1] = normalizedLastByte; - } - - private int CountConstructedBitString(ReadOnlySpan source, bool isIndefinite) - { - Span destination = Span.Empty; - - return ProcessConstructedBitString( - source, - destination, - null, - isIndefinite, - out _, - out _); - } - - private void CopyConstructedBitString( - ReadOnlySpan source, - Span destination, - bool isIndefinite, - out int unusedBitCount, - out int bytesRead, - out int bytesWritten) - { - Span tmpDest = destination; - - bytesWritten = ProcessConstructedBitString( - source, - tmpDest, - (value, lastByte, dest) => CopyBitStringValue(value, lastByte, dest), - isIndefinite, - out unusedBitCount, - out bytesRead); - } - - private int ProcessConstructedBitString( - ReadOnlySpan source, - Span destination, - BitStringCopyAction? copyAction, - bool isIndefinite, - out int lastUnusedBitCount, - out int bytesRead) - { - lastUnusedBitCount = 0; - bytesRead = 0; - int lastSegmentLength = MaxCERSegmentSize; - - ReadOnlySpan originalSpan = _data; - AsnValueReader tmpReader = OpenUnchecked(source, RuleSet); - Stack<(int Offset, int Length, bool Indefinite, int BytesRead)>? readerStack = null; - int totalLength = 0; - Asn1Tag tag = Asn1Tag.ConstructedBitString; - Span curDest = destination; - - while (true) - { - while (tmpReader.HasData) - { - tag = tmpReader.ReadTagAndLength(out int? length, out int headerLength); - - if (tag == Asn1Tag.PrimitiveBitString) - { - if (lastUnusedBitCount != 0) - { - // T-REC-X.690-201508 sec 8.6.4, only the last segment may have - // a number of bits not a multiple of 8. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(length != null); - ReadOnlySpan encodedValue = Slice(tmpReader._data, headerLength, length.Value); - - ParsePrimitiveBitStringContents( - encodedValue, - out lastUnusedBitCount, - out ReadOnlySpan contents, - out byte normalizedLastByte); - - int localLen = headerLength + encodedValue.Length; - tmpReader._data = tmpReader._data.Slice(localLen); - - bytesRead += localLen; - totalLength += contents.Length; - lastSegmentLength = encodedValue.Length; - - if (copyAction != null) - { - copyAction(contents, normalizedLastByte, curDest); - curDest = curDest.Slice(contents.Length); - } - } - else if (tag == Asn1Tag.EndOfContents && isIndefinite) - { - ValidateEndOfContents(tag, length, headerLength); - - bytesRead += headerLength; - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); - tmpReader._data = topSpan.Slice(bytesRead); - - bytesRead += pushedBytesRead; - isIndefinite = wasIndefinite; - } - else - { - // We have matched the EndOfContents that brought us here. - break; - } - } - else if (tag == Asn1Tag.ConstructedBitString) - { - if (RuleSet == AsnEncodingRules.CER) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack == null) - { - readerStack = new Stack<(int, int, bool, int)>(); - } - - if (!originalSpan.Overlaps(tmpReader._data, out int curOffset)) - { - Debug.Fail("Non-overlapping data encountered..."); - throw new CryptographicException(); - } - - readerStack.Push((curOffset, tmpReader._data.Length, isIndefinite, bytesRead)); - - tmpReader._data = Slice(tmpReader._data, headerLength, length); - bytesRead = headerLength; - isIndefinite = (length == null); - } - else - { - // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - if (isIndefinite && tag != Asn1Tag.EndOfContents) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - - ReadOnlySpan tmpSpan = originalSpan.Slice(topOffset, topLength); - tmpReader._data = tmpSpan.Slice(bytesRead); - - isIndefinite = wasIndefinite; - bytesRead += pushedBytesRead; - } - else - { - return totalLength; - } - } - } - - private bool TryCopyConstructedBitStringValue( - ReadOnlySpan source, - Span dest, - bool isIndefinite, - out int unusedBitCount, - out int bytesRead, - out int bytesWritten) - { - // Call CountConstructedBitString to get the required byte and to verify that the - // data is well-formed before copying into dest. - int contentLength = CountConstructedBitString(source, isIndefinite); - - // Since the unused bits byte from the segments don't count, only one segment - // returns 999 (or less), the second segment bumps the count to 1000, and is legal. - // - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER && contentLength < MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (dest.Length < contentLength) - { - unusedBitCount = 0; - bytesRead = 0; - bytesWritten = 0; - return false; - } - - CopyConstructedBitString( - source, - dest, - isIndefinite, - out unusedBitCount, - out bytesRead, - out bytesWritten); - - Debug.Assert(bytesWritten == contentLength); - return true; - } - - private bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, - out Asn1Tag actualTag, - out int? contentsLength, - out int headerLength, - out int unusedBitCount, - out ReadOnlySpan value, - out byte normalizedLastByte) - { - actualTag = ReadTagAndLength(out contentsLength, out headerLength); - CheckExpectedTag(actualTag, expectedTag, UniversalTagNumber.BitString); - - if (actualTag.IsConstructed) - { - if (RuleSet == AsnEncodingRules.DER) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - unusedBitCount = 0; - value = default; - normalizedLastByte = 0; - return false; - } - - Debug.Assert(contentsLength.HasValue); - ReadOnlySpan encodedValue = Slice(_data, headerLength, contentsLength.Value); - - ParsePrimitiveBitStringContents( - encodedValue, - out unusedBitCount, - out value, - out normalizedLastByte); - - return true; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveBitStringValue(out int unusedBitCount, out ReadOnlyMemory value) - => TryReadPrimitiveBitStringValue(Asn1Tag.PrimitiveBitString, out unusedBitCount, out value); - - /// - /// Reads the next value as a BIT STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the BIT STRING. - /// - /// - /// true and advances the reader if the BIT STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveBitStringValue( - Asn1Tag expectedTag, - out int unusedBitCount, - out ReadOnlyMemory value) - { - AsnValueReader reader = OpenValueReader(); - - if (reader.TryReadPrimitiveBitStringValue(expectedTag, out unusedBitCount, out ReadOnlySpan span)) - { - value = AsnValueReader.Slice(_data, span); - reader.MatchSlice(ref _data); - return true; - } - - value = default; - return false; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyBitStringBytes( - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - Asn1Tag.PrimitiveBitString, - destination, - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - Span destination, - out int unusedBitCount, - out int bytesWritten) - { - AsnValueReader reader = OpenValueReader(); - - if (reader.TryCopyBitStringBytes(expectedTag, destination, out unusedBitCount, out bytesWritten)) - { - reader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyBitStringBytes( - ArraySegment destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - Asn1Tag.PrimitiveBitString, - destination.AsSpan(), - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyBitStringBytes( - Asn1Tag expectedTag, - ArraySegment destination, - out int unusedBitCount, - out int bytesWritten) - { - return TryCopyBitStringBytes( - expectedTag, - destination.AsSpan(), - out unusedBitCount, - out bytesWritten); - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public byte[] ReadBitString(out int unusedBitCount) - { - return ReadBitString(Asn1Tag.PrimitiveBitString, out unusedBitCount); - } - - /// - /// Reads the next value as a BIT STRING with tag UNIVERSAL 3, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// On success, receives the number of bits in the last byte which were reported as - /// "unused" by the writer. - /// - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public byte[] ReadBitString(Asn1Tag expectedTag, out int unusedBitCount) - { - AsnValueReader valueReader = OpenValueReader(); - byte[] ret = valueReader.ReadBitString(expectedTag, out unusedBitCount); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs deleted file mode 100644 index 8743adf..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Boolean.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a Boolean with tag UNIVERSAL 1. - /// - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool ReadBoolean() => ReadBoolean(Asn1Tag.Boolean); - - /// - /// Reads the next value as a Boolean with a specified tag. - /// - /// The tag to check for before reading. - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool ReadBoolean(Asn1Tag expectedTag) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Boolean); - - // T-REC-X.690-201508 sec 8.2.1 - if (tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(length.HasValue); - bool value = ReadBooleanValue( - Slice(_data, headerLength, length.Value), - RuleSet); - - _data = _data.Slice(headerLength + length.Value); - return value; - } - - private static bool ReadBooleanValue( - ReadOnlySpan source, - AsnEncodingRules ruleSet) - { - // T-REC-X.690-201508 sec 8.2.1 - if (source.Length != 1) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - byte val = source[0]; - - // T-REC-X.690-201508 sec 8.2.2 - if (val == 0) - { - return false; - } - - // T-REC-X.690-201508 sec 11.1 - if (val != 0xFF && (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return true; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a Boolean with tag UNIVERSAL 1. - /// - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool ReadBoolean() => ReadBoolean(Asn1Tag.Boolean); - - /// - /// Reads the next value as a Boolean with a specified tag. - /// - /// The tag to check for before reading. - /// The next value as a Boolean. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool ReadBoolean(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - bool ret = valueReader.ReadBoolean(expectedTag); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs deleted file mode 100644 index 296e596..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Enumerated.cs +++ /dev/null @@ -1,401 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, - /// returning the contents as a over the original data. - /// - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public ReadOnlySpan ReadEnumeratedBytes() => - ReadEnumeratedBytes(Asn1Tag.Enumerated); - - /// - /// Reads the next value as a Enumerated with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public ReadOnlySpan ReadEnumeratedBytes(Asn1Tag expectedTag) - { - // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Enumerated, out int headerLength); - - _data = _data.Slice(headerLength + contents.Length); - return contents; - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public TEnum ReadEnumeratedValue() where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(tEnum)); - } - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public TEnum ReadEnumeratedValue(Asn1Tag expectedTag) where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum)); - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public Enum ReadEnumeratedValue(Type tEnum) => - ReadEnumeratedValue(Asn1Tag.Enumerated, tEnum); - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) - { - const UniversalTagNumber tagNumber = UniversalTagNumber.Enumerated; - - // This will throw an ArgumentException if TEnum isn't an enum type, - // so we don't need to validate it. - Type backingType = tEnum.GetEnumUnderlyingType(); - - if (tEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, - nameof(tEnum)); - } - - // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. - int sizeLimit = Marshal.SizeOf(backingType); - - if (backingType == typeof(int) || - backingType == typeof(long) || - backingType == typeof(short) || - backingType == typeof(sbyte)) - { - if (!TryReadSignedInteger(sizeLimit, expectedTag, tagNumber, out long value)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return (Enum)Enum.ToObject(tEnum, value); - } - - if (backingType == typeof(uint) || - backingType == typeof(ulong) || - backingType == typeof(ushort) || - backingType == typeof(byte)) - { - if (!TryReadUnsignedInteger(sizeLimit, expectedTag, tagNumber, out ulong value)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return (Enum)Enum.ToObject(tEnum, value); - } - - Debug.Fail($"No handler for type {backingType.Name}"); - throw new CryptographicException(); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, - /// returning the contents as a over the original data. - /// - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public ReadOnlyMemory ReadEnumeratedBytes() => - ReadEnumeratedBytes(Asn1Tag.Enumerated); - - /// - /// Reads the next value as a Enumerated with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Enumerated value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public ReadOnlyMemory ReadEnumeratedBytes(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan bytes = valueReader.ReadEnumeratedBytes(expectedTag); - ReadOnlyMemory memory = AsnValueReader.Slice(_data, bytes); - - valueReader.MatchSlice(ref _data); - return memory; - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public TEnum ReadEnumeratedValue() where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(tEnum)); - } - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public TEnum ReadEnumeratedValue(Asn1Tag expectedTag) where TEnum : struct - { - Type tEnum = typeof(TEnum); - - return (TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum)); - } - - /// - /// Reads the next value as an Enumerated value with tag UNIVERSAL 10, converting it to - /// the non-[] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// - /// - public Enum ReadEnumeratedValue(Type tEnum) => - ReadEnumeratedValue(Asn1Tag.Enumerated, tEnum); - - /// - /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the - /// non-[] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the Enumerated value converted to a . - /// - /// - /// This method does not validate that the return value is defined within - /// . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Enum ReadEnumeratedValue(Asn1Tag expectedTag, Type tEnum) - { - AsnValueReader valueReader = OpenValueReader(); - Enum ret = valueReader.ReadEnumeratedValue(expectedTag, tEnum); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs deleted file mode 100644 index 3939428..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Integer.cs +++ /dev/null @@ -1,1217 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Numerics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a over the original data. - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public ReadOnlySpan ReadIntegerBytes() => - ReadIntegerBytes(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public ReadOnlySpan ReadIntegerBytes(Asn1Tag expectedTag) - { - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength); - - _data = _data.Slice(headerLength + contents.Length); - return contents; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a . - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public BigInteger ReadInteger() => ReadInteger(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public BigInteger ReadInteger(Asn1Tag expectedTag) - { - ReadOnlySpan contents = - GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength); - - // TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing - byte[] tmp = CryptoPool.Rent(contents.Length); - BigInteger value; - - try - { - byte fill = (contents[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF; - // Fill the unused portions of tmp with positive or negative padding. - new Span(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill); - contents.CopyTo(tmp); - // Convert to Little-Endian. - AsnWriter.Reverse(new Span(tmp, 0, contents.Length)); - value = new BigInteger(tmp); - } - finally - { - // Let CryptoPool.Return clear the whole tmp so that not even the sign bit - // is returned to the array pool. - CryptoPool.Return(tmp); - } - - _data = _data.Slice(headerLength + contents.Length); - return value; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt32(out int value) => - TryReadInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt32(Asn1Tag expectedTag, out int value) - { - if (TryReadSignedInteger(sizeof(int), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (int)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt32(out uint value) => - TryReadUInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt32(Asn1Tag expectedTag, out uint value) - { - if (TryReadUnsignedInteger(sizeof(uint), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (uint)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt64(out long value) => - TryReadInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt64(Asn1Tag expectedTag, out long value) - { - return TryReadSignedInteger(sizeof(long), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt64(out ulong value) => - TryReadUInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt64(Asn1Tag expectedTag, out ulong value) - { - return TryReadUnsignedInteger(sizeof(ulong), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt16(out short value) => - TryReadInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt16(Asn1Tag expectedTag, out short value) - { - if (TryReadSignedInteger(sizeof(short), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (short)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt16(out ushort value) => - TryReadUInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt16(Asn1Tag expectedTag, out ushort value) - { - if (TryReadUnsignedInteger(sizeof(ushort), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (ushort)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt8(out sbyte value) => - TryReadInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the Integer value is not valid - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt8(Asn1Tag expectedTag, out sbyte value) - { - if (TryReadSignedInteger(sizeof(sbyte), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (sbyte)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt8(out byte value) => - TryReadUInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt8(Asn1Tag expectedTag, out byte value) - { - if (TryReadUnsignedInteger(sizeof(byte), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (byte)ulongValue; - return true; - } - - value = 0; - return false; - } - - private ReadOnlySpan GetIntegerContents( - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out int headerLength) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out headerLength); - CheckExpectedTag(tag, expectedTag, tagNumber); - - // T-REC-X.690-201508 sec 8.3.1 - if (tag.IsConstructed || length < 1) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - // Slice first so that an out of bounds value triggers a CryptographicException. - ReadOnlySpan contents = Slice(_data, headerLength, length!.Value); - - // T-REC-X.690-201508 sec 8.3.2 - if (contents.Length > 1) - { - ushort bigEndianValue = (ushort)(contents[0] << 8 | contents[1]); - const ushort RedundancyMask = 0b1111_1111_1000_0000; - ushort masked = (ushort)(bigEndianValue & RedundancyMask); - - // If the first 9 bits are all 0 or are all 1, the value is invalid. - if (masked == 0 || masked == RedundancyMask) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - return contents; - } - - internal bool TryReadSignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out long value) - { - Debug.Assert(sizeLimit <= sizeof(long)); - - ReadOnlySpan contents = GetIntegerContents(expectedTag, tagNumber, out int headerLength); - - if (contents.Length > sizeLimit) - { - value = 0; - return false; - } - - bool isNegative = (contents[0] & 0x80) != 0; - long accum = isNegative ? -1 : 0; - - for (int i = 0; i < contents.Length; i++) - { - accum <<= 8; - accum |= contents[i]; - } - - _data = _data.Slice(headerLength + contents.Length); - value = accum; - return true; - } - - internal bool TryReadUnsignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out ulong value) - { - Debug.Assert(sizeLimit <= sizeof(ulong)); - - ReadOnlySpan contents = GetIntegerContents(expectedTag, tagNumber, out int headerLength); - int contentLength = contents.Length; - - bool isNegative = (contents[0] & 0x80) != 0; - - if (isNegative) - { - value = 0; - return false; - } - - // Ignore any padding zeros. - if (contents.Length > 1 && contents[0] == 0) - { - contents = contents.Slice(1); - } - - if (contents.Length > sizeLimit) - { - value = 0; - return false; - } - - ulong accum = 0; - - for (int i = 0; i < contents.Length; i++) - { - accum <<= 8; - accum |= contents[i]; - } - - _data = _data.Slice(headerLength + contentLength); - value = accum; - return true; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a over the original data. - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public ReadOnlyMemory ReadIntegerBytes() => - ReadIntegerBytes(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public ReadOnlyMemory ReadIntegerBytes(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan bytes = valueReader.ReadIntegerBytes(expectedTag); - ReadOnlyMemory memory = AsnValueReader.Slice(_data, bytes); - - valueReader.MatchSlice(ref _data); - return memory; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, returning the contents - /// as a . - /// - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public BigInteger ReadInteger() => ReadInteger(Asn1Tag.Integer); - - /// - /// Reads the next value as a Integer with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// The bytes of the Integer value, in signed big-endian form. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public BigInteger ReadInteger(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - BigInteger ret = valueReader.ReadInteger(expectedTag); - valueReader.MatchSlice(ref _data); - return ret; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt32(out int value) => - TryReadInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt32(Asn1Tag expectedTag, out int value) - { - if (TryReadSignedInteger(sizeof(int), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (int)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt32(out uint value) => - TryReadUInt32(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt32(Asn1Tag expectedTag, out uint value) - { - if (TryReadUnsignedInteger(sizeof(uint), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (uint)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt64(out long value) => - TryReadInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt64(Asn1Tag expectedTag, out long value) - { - return TryReadSignedInteger(sizeof(long), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt64(out ulong value) => - TryReadUInt64(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt64(Asn1Tag expectedTag, out ulong value) - { - return TryReadUnsignedInteger(sizeof(ulong), expectedTag, UniversalTagNumber.Integer, out value); - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt16(out short value) => - TryReadInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt16(Asn1Tag expectedTag, out short value) - { - if (TryReadSignedInteger(sizeof(short), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (short)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt16(out ushort value) => - TryReadUInt16(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt16(Asn1Tag expectedTag, out ushort value) - { - if (TryReadUnsignedInteger(sizeof(ushort), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (ushort)ulongValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as an . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadInt8(out sbyte value) => - TryReadInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as an . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the Integer value is not valid - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadInt8(Asn1Tag expectedTag, out sbyte value) - { - if (TryReadSignedInteger(sizeof(sbyte), expectedTag, UniversalTagNumber.Integer, out long longValue)) - { - value = (sbyte)longValue; - return true; - } - - value = 0; - return false; - } - - /// - /// Reads the next value as an Integer with tag UNIVERSAL 2, interpreting the contents - /// as a . - /// - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public bool TryReadUInt8(out byte value) => - TryReadUInt8(Asn1Tag.Integer, out value); - - /// - /// Reads the next value as a Integer with a specified tag, interpreting the contents - /// as a . - /// - /// The tag to check for before reading. - /// - /// On success, receives the value represented - /// - /// - /// false and does not advance the reader if the value is not between - /// and , inclusive; otherwise - /// true is returned and the reader advances. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public bool TryReadUInt8(Asn1Tag expectedTag, out byte value) - { - if (TryReadUnsignedInteger(sizeof(byte), expectedTag, UniversalTagNumber.Integer, out ulong ulongValue)) - { - value = (byte)ulongValue; - return true; - } - - value = 0; - return false; - } - - private bool TryReadSignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out long value) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryReadSignedInteger(sizeLimit, expectedTag, tagNumber, out value)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - private bool TryReadUnsignedInteger( - int sizeLimit, - Asn1Tag expectedTag, - UniversalTagNumber tagNumber, - out ulong value) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryReadUnsignedInteger(sizeLimit, expectedTag, tagNumber, out value)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs deleted file mode 100644 index d972df2..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.NamedBitList.cs +++ /dev/null @@ -1,427 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public TFlagsEnum ReadNamedBitListValue() where TFlagsEnum : struct => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// The bit alignment performed by this method is to interpret the most significant bit - /// in the first byte of the value as the least significant bit in , - /// with bits increasing in value until the least significant bit of the first byte, proceeding - /// with the most significant bit of the second byte, and so on. Under this scheme, the following - /// ASN.1 type declaration and C# enumeration can be used together: - /// - /// - /// KeyUsage ::= BIT STRING { - /// digitalSignature (0), - /// nonRepudiation (1), - /// keyEncipherment (2), - /// dataEncipherment (3), - /// keyAgreement (4), - /// keyCertSign (5), - /// cRLSign (6), - /// encipherOnly (7), - /// decipherOnly (8) } - /// - /// - /// - /// [Flags] - /// enum KeyUsage - /// { - /// None = 0, - /// DigitalSignature = 1 << (0), - /// NonRepudiation = 1 << (1), - /// KeyEncipherment = 1 << (2), - /// DataEncipherment = 1 << (3), - /// KeyAgreement = 1 << (4), - /// KeyCertSign = 1 << (5), - /// CrlSign = 1 << (6), - /// EncipherOnly = 1 << (7), - /// DecipherOnly = 1 << (8), - /// } - /// - /// - /// Note that while the example here uses the KeyUsage NamedBitList from - /// RFC 3280 (4.2.1.3), - /// the example enum uses values thar are different from - /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. - /// - public TFlagsEnum ReadNamedBitListValue(Asn1Tag expectedTag) where TFlagsEnum : struct - { - Type tFlagsEnum = typeof(TFlagsEnum); - - return (TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum)); - } - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public Enum ReadNamedBitListValue(Type tFlagsEnum) => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString, tFlagsEnum); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR--- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) - { - // This will throw an ArgumentException if TEnum isn't an enum type, - // so we don't need to validate it. - Type backingType = tFlagsEnum.GetEnumUnderlyingType(); - - if (!tFlagsEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, - nameof(tFlagsEnum)); - } - - int sizeLimit = Marshal.SizeOf(backingType); - ReadOnlySpan saveData = _data; - Span stackSpan; - - unsafe - { - byte* stackPtr = stackalloc byte[sizeLimit]; - stackSpan = new Span(stackPtr, sizeLimit); - } - - // If TryCopyBitStringBytes succeeds but anything else fails _data will have moved, - // so if anything throws here just move _data back to what it was. - try - { - if (!TryCopyBitStringBytes(expectedTag, stackSpan, out int unusedBitCount, out int bytesWritten)) - { - throw new CryptographicException( - SR.Format(SR.Cryptography_Asn_NamedBitListValueTooBig, tFlagsEnum.Name)); - } - - if (bytesWritten == 0) - { - // The mode isn't relevant, zero is always zero. - return (Enum)Enum.ToObject(tFlagsEnum, 0); - } - - ReadOnlySpan valueSpan = stackSpan.Slice(0, bytesWritten); - - // Now that the 0-bounds check is out of the way: - // - // T-REC-X.690-201508 sec 11.2.2 - if (RuleSet == AsnEncodingRules.DER || - RuleSet == AsnEncodingRules.CER) - { - byte lastByte = valueSpan[bytesWritten - 1]; - - // No unused bits tests 0x01, 1 is 0x02, 2 is 0x04, etc. - // We already know that TryCopyBitStringBytes checked that the - // declared unused bits were 0, this checks that the last "used" bit - // isn't also zero. - byte testBit = (byte)(1 << unusedBitCount); - - if ((lastByte & testBit) == 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - // Consider a NamedBitList defined as - // - // SomeList ::= BIT STRING { - // a(0), b(1), c(2), d(3), e(4), f(5), g(6), h(7), i(8), j(9), k(10) - // } - // - // The BIT STRING encoding of (a | j) is - // unusedBitCount = 6, - // contents: 0x80 0x40 (0b10000000_01000000) - // - // A the C# exposure of this structure we adhere to is - // - // [Flags] - // enum SomeList - // { - // A = 1, - // B = 1 << 1, - // C = 1 << 2, - // ... - // } - // - // Which happens to be exactly backwards from how the bits are encoded, but the complexity - // only needs to live here. - return (Enum)Enum.ToObject(tFlagsEnum, InterpretNamedBitListReversed(valueSpan)); - } - catch - { - _data = saveData; - throw; - } - } - - private static long InterpretNamedBitListReversed(ReadOnlySpan valueSpan) - { - Debug.Assert(valueSpan.Length <= sizeof(long)); - - long accum = 0; - long currentBitValue = 1; - - for (int byteIdx = 0; byteIdx < valueSpan.Length; byteIdx++) - { - byte byteVal = valueSpan[byteIdx]; - - for (int bitIndex = 7; bitIndex >= 0; bitIndex--) - { - int test = 1 << bitIndex; - - if ((byteVal & test) != 0) - { - accum |= currentBitValue; - } - - currentBitValue <<= 1; - } - } - - return accum; - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public TFlagsEnum ReadNamedBitListValue() where TFlagsEnum : struct => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Destination enum type - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// The bit alignment performed by this method is to interpret the most significant bit - /// in the first byte of the value as the least significant bit in , - /// with bits increasing in value until the least significant bit of the first byte, proceeding - /// with the most significant bit of the second byte, and so on. Under this scheme, the following - /// ASN.1 type declaration and C# enumeration can be used together: - /// - /// - /// KeyUsage ::= BIT STRING { - /// digitalSignature (0), - /// nonRepudiation (1), - /// keyEncipherment (2), - /// dataEncipherment (3), - /// keyAgreement (4), - /// keyCertSign (5), - /// cRLSign (6), - /// encipherOnly (7), - /// decipherOnly (8) } - /// - /// - /// - /// [Flags] - /// enum KeyUsage - /// { - /// None = 0, - /// DigitalSignature = 1 << (0), - /// NonRepudiation = 1 << (1), - /// KeyEncipherment = 1 << (2), - /// DataEncipherment = 1 << (3), - /// KeyAgreement = 1 << (4), - /// KeyCertSign = 1 << (5), - /// CrlSign = 1 << (6), - /// EncipherOnly = 1 << (7), - /// DecipherOnly = 1 << (8), - /// } - /// - /// - /// Note that while the example here uses the KeyUsage NamedBitList from - /// RFC 3280 (4.2.1.3), - /// the example enum uses values thar are different from - /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. - /// - public TFlagsEnum ReadNamedBitListValue(Asn1Tag expectedTag) where TFlagsEnum : struct - { - Type tFlagsEnum = typeof(TFlagsEnum); - - return (TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum)); - } - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// - /// - public Enum ReadNamedBitListValue(Type tFlagsEnum) => - ReadNamedBitListValue(Asn1Tag.PrimitiveBitString, tFlagsEnum); - - /// - /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the - /// [] enum specfied by . - /// - /// The tag to check for before reading. - /// Type object representing the destination type. - /// - /// the NamedBitList value converted to a . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR--- - /// the encoded value is too big to fit in a value - /// - /// - /// is not an enum type --OR-- - /// was not declared with - /// --OR-- - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) - { - AsnValueReader valueReader = OpenValueReader(); - Enum ret = valueReader.ReadNamedBitListValue(expectedTag, tFlagsEnum); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs deleted file mode 100644 index c7f781d..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Null.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a NULL with tag UNIVERSAL 5. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public void ReadNull() => ReadNull(Asn1Tag.Null); - - /// - /// Reads the next value as a NULL with a specified tag. - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public void ReadNull(Asn1Tag expectedTag) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Null); - - // T-REC-X.690-201508 sec 8.8.1 - // T-REC-X.690-201508 sec 8.8.2 - if (tag.IsConstructed || length != 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - _data = _data.Slice(headerLength); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a NULL with tag UNIVERSAL 5. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public void ReadNull() => ReadNull(Asn1Tag.Null); - - /// - /// Reads the next value as a NULL with a specified tag. - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public void ReadNull(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - valueReader.ReadNull(expectedTag); - valueReader.MatchSlice(ref _data); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs deleted file mode 100644 index 4b3756c..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.OctetString.cs +++ /dev/null @@ -1,789 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyOctetStringBytes( - Span destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - Asn1Tag.PrimitiveOctetString, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, - Span destination, - out int bytesWritten) - { - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contents)) - { - if (contents.Length > destination.Length) - { - bytesWritten = 0; - return false; - } - - contents.CopyTo(destination); - bytesWritten = contents.Length; - _data = _data.Slice(headerLength + contents.Length); - return true; - } - - Debug.Assert(actualTag.IsConstructed); - - bool copied = TryCopyConstructedOctetStringContents( - Slice(_data, headerLength, contentLength), - destination, - contentLength == null, - out int bytesRead, - out bytesWritten); - - if (copied) - { - _data = _data.Slice(headerLength + bytesRead); - } - - return copied; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// - /// a copy of the contents in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public byte[] ReadOctetString() - { - return ReadOctetString(Asn1Tag.PrimitiveOctetString); - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - /// - public byte[] ReadOctetString(Asn1Tag expectedTag) - { - ReadOnlySpan span; - - if (TryReadPrimitiveOctetStringBytes(expectedTag, out span)) - { - return span.ToArray(); - } - - span = PeekEncodedValue(); - - // Guaranteed long enough - byte[] rented = CryptoPool.Rent(span.Length); - int dataLength = 0; - - try - { - if (!TryCopyOctetStringBytes(expectedTag, rented, out dataLength)) - { - Debug.Fail("TryCopyOctetStringBytes failed with a pre-allocated buffer"); - throw new CryptographicException(); - } - - byte[] alloc = new byte[dataLength]; - rented.AsSpan(0, dataLength).CopyTo(alloc); - return alloc; - } - finally - { - CryptoPool.Return(rented, dataLength); - } - } - - private bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contents, - UniversalTagNumber universalTagNumber = UniversalTagNumber.OctetString) - { - actualTag = ReadTagAndLength(out contentLength, out headerLength); - CheckExpectedTag(actualTag, expectedTag, universalTagNumber); - - if (actualTag.IsConstructed) - { - if (RuleSet == AsnEncodingRules.DER) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - contents = default; - return false; - } - - Debug.Assert(contentLength.HasValue); - ReadOnlySpan encodedValue = Slice(_data, headerLength, contentLength.Value); - - if (RuleSet == AsnEncodingRules.CER && encodedValue.Length > MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - contents = encodedValue; - return true; - } - - internal bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out ReadOnlySpan contents) - { - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out _, - out _, - out int headerLength, - out contents, - universalTagNumber)) - { - _data = _data.Slice(headerLength + contents.Length); - return true; - } - - return false; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveOctetStringBytes(out ReadOnlySpan contents) => - TryReadPrimitiveOctetStringBytes(Asn1Tag.PrimitiveOctetString, out contents); - - /// - /// Reads the next value as an OCTET STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveOctetStringBytes(Asn1Tag expectedTag, out ReadOnlySpan contents) - { - return TryReadPrimitiveOctetStringBytes(expectedTag, UniversalTagNumber.OctetString, out contents); - } - - private int CountConstructedOctetString(ReadOnlySpan source, bool isIndefinite) - { - int contentLength = CopyConstructedOctetString( - source, - Span.Empty, - false, - isIndefinite, - out _); - - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER && contentLength <= MaxCERSegmentSize) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return contentLength; - } - - private void CopyConstructedOctetString( - ReadOnlySpan source, - Span destination, - bool isIndefinite, - out int bytesRead, - out int bytesWritten) - { - bytesWritten = CopyConstructedOctetString( - source, - destination, - true, - isIndefinite, - out bytesRead); - } - - private int CopyConstructedOctetString( - ReadOnlySpan source, - Span destination, - bool write, - bool isIndefinite, - out int bytesRead) - { - bytesRead = 0; - int lastSegmentLength = MaxCERSegmentSize; - - ReadOnlySpan originalSpan = _data; - AsnValueReader tmpReader = OpenUnchecked(source, RuleSet); - Stack<(int Offset, int Length, bool IsIndefinite, int BytesRead)>? readerStack = null; - int totalLength = 0; - Asn1Tag tag = Asn1Tag.ConstructedBitString; - Span curDest = destination; - - while (true) - { - while (tmpReader.HasData) - { - tag = tmpReader.ReadTagAndLength(out int? length, out int headerLength); - - if (tag == Asn1Tag.PrimitiveOctetString) - { - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Debug.Assert(length != null); - - // The call to Slice here sanity checks the data bounds, length.Value is not - // reliable unless this call has succeeded. - ReadOnlySpan contents = Slice(tmpReader._data, headerLength, length.Value); - - int localLen = headerLength + contents.Length; - tmpReader._data = tmpReader._data.Slice(localLen); - - bytesRead += localLen; - totalLength += contents.Length; - lastSegmentLength = contents.Length; - - if (RuleSet == AsnEncodingRules.CER && lastSegmentLength > MaxCERSegmentSize) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (write) - { - contents.CopyTo(curDest); - curDest = curDest.Slice(contents.Length); - } - } - else if (tag == Asn1Tag.EndOfContents && isIndefinite) - { - ValidateEndOfContents(tag, length, headerLength); - - bytesRead += headerLength; - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); - tmpReader._data = topSpan.Slice(bytesRead); - - bytesRead += pushedBytesRead; - isIndefinite = wasIndefinite; - } - else - { - // We have matched the EndOfContents that brought us here. - break; - } - } - else if (tag == Asn1Tag.ConstructedOctetString) - { - if (RuleSet == AsnEncodingRules.CER) - { - // T-REC-X.690-201508 sec 9.2 - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack == null) - { - readerStack = new Stack<(int, int, bool, int)>(); - } - - if (!originalSpan.Overlaps(tmpReader._data, out int curOffset)) - { - Debug.Fail("Non-overlapping data encountered..."); - throw new CryptographicException(); - } - - readerStack.Push((curOffset, tmpReader._data.Length, isIndefinite, bytesRead)); - - tmpReader._data = Slice(tmpReader._data, headerLength, length); - bytesRead = headerLength; - isIndefinite = (length == null); - } - else - { - // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - if (isIndefinite && tag != Asn1Tag.EndOfContents) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (readerStack?.Count > 0) - { - (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); - ReadOnlySpan topSpan = originalSpan.Slice(topOffset, topLength); - - tmpReader._data = topSpan.Slice(bytesRead); - - isIndefinite = wasIndefinite; - bytesRead += pushedBytesRead; - } - else - { - return totalLength; - } - } - } - - private bool TryCopyConstructedOctetStringContents( - ReadOnlySpan source, - Span dest, - bool isIndefinite, - out int bytesRead, - out int bytesWritten) - { - bytesRead = 0; - - int contentLength = CountConstructedOctetString(source, isIndefinite); - - if (dest.Length < contentLength) - { - bytesWritten = 0; - return false; - } - - CopyConstructedOctetString(source, dest, isIndefinite, out bytesRead, out bytesWritten); - - Debug.Assert(bytesWritten == contentLength); - return true; - } - - private ReadOnlySpan GetOctetStringContents( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out int bytesRead, - ref byte[]? rented, - Span tmpSpace = default) - { - Debug.Assert(rented == null); - - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contentsOctets, - universalTagNumber)) - { - bytesRead = headerLength + contentsOctets.Length; - return contentsOctets; - } - - Debug.Assert(actualTag.IsConstructed); - - ReadOnlySpan source = Slice(_data, headerLength, contentLength); - bool isIndefinite = contentLength == null; - int octetStringLength = CountConstructedOctetString(source, isIndefinite); - - if (tmpSpace.Length < octetStringLength) - { - rented = CryptoPool.Rent(octetStringLength); - tmpSpace = rented; - } - - CopyConstructedOctetString( - source, - tmpSpace, - isIndefinite, - out int localBytesRead, - out int bytesWritten); - - Debug.Assert(bytesWritten == octetStringLength); - - bytesRead = headerLength + localBytesRead; - return tmpSpace.Slice(0, bytesWritten); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyOctetStringBytes( - Span destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - Asn1Tag.PrimitiveOctetString, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, - Span destination, - out int bytesWritten) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryCopyOctetStringBytes(expectedTag, destination, out bytesWritten)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, copying the value - /// into a provided destination buffer. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public bool TryCopyOctetStringBytes( - ArraySegment destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - Asn1Tag.PrimitiveOctetString, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with a specified tag, copying the value - /// into a provided destination buffer. - /// - /// The tag to check for before reading. - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - public bool TryCopyOctetStringBytes( - Asn1Tag expectedTag, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyOctetStringBytes( - expectedTag, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// - /// a copy of the contents in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public byte[] ReadOctetString() - { - return ReadOctetString(Asn1Tag.PrimitiveOctetString); - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value - /// in a byte array. - /// - /// The tag to check for before reading. - /// - /// a copy of the value in a newly allocated, precisely sized, array. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// - /// - public byte[] ReadOctetString(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - byte[] ret = valueReader.ReadOctetString(expectedTag); - valueReader.MatchSlice(ref _data); - return ret; - } - - /// - /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the contents - /// as a over the original data. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents) => - TryReadPrimitiveOctetStringBytes(Asn1Tag.PrimitiveOctetString, out contents); - - /// - /// Reads the next value as an OCTET STRING with a specified tag, returning the contents - /// as a over the original data. - /// - /// The tag to check for before reading. - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - public bool TryReadPrimitiveOctetStringBytes(Asn1Tag expectedTag, out ReadOnlyMemory contents) - { - return TryReadPrimitiveOctetStringBytes(expectedTag, UniversalTagNumber.OctetString, out contents); - } - - private bool TryReadPrimitiveOctetStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - out ReadOnlyMemory contents) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan span; - - if (valueReader.TryReadPrimitiveOctetStringBytes(expectedTag, universalTagNumber, out span)) - { - contents = AsnValueReader.Slice(_data, span); - valueReader.MatchSlice(ref _data); - return true; - } - - contents = default; - return false; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs deleted file mode 100644 index 6f89866..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Sequence.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with tag UNIVERSAL 16 - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public AsnValueReader ReadSequence() => ReadSequence(Asn1Tag.Sequence); - - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// The tag to check for before reading. - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnValueReader ReadSequence(Asn1Tag expectedTag) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.Sequence); - - // T-REC-X.690-201508 sec 8.9.1 - // T-REC-X.690-201508 sec 8.10.1 - if (!tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - int suffix = 0; - - if (length == null) - { - length = SeekEndOfContents(_data.Slice(headerLength)); - suffix = EndOfContentsEncodedLength; - } - - ReadOnlySpan contents = Slice(_data, headerLength, length.Value); - - _data = _data.Slice(headerLength + contents.Length + suffix); - return OpenUnchecked(contents, RuleSet); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with tag UNIVERSAL 16 - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public AsnReader ReadSequence() => ReadSequence(Asn1Tag.Sequence); - - /// - /// Reads the next value as a SEQUENCE or SEQUENCE-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the sequence (or with == false). - /// - /// The tag to check for before reading. - /// - /// an positioned at the first - /// value in the sequence (or with == false). - /// - /// - /// the nested content is not evaluated by this method, and may contain data - /// which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnReader ReadSequence(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - AsnValueReader innerValueReader = valueReader.ReadSequence(expectedTag); - - AsnReader ret = new AsnReader(_data, RuleSet); - innerValueReader.MatchSlice(ref ret._data); - - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs deleted file mode 100644 index cd3adaa..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.SetOf.cs +++ /dev/null @@ -1,184 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public AsnValueReader ReadSetOf(bool skipSortOrderValidation = false) => - ReadSetOf(Asn1Tag.SetOf, skipSortOrderValidation); - - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// The tag to check for before reading. - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnValueReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) - { - Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); - CheckExpectedTag(tag, expectedTag, UniversalTagNumber.SetOf); - - // T-REC-X.690-201508 sec 8.12.1 - if (!tag.IsConstructed) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - int suffix = 0; - - if (length == null) - { - length = SeekEndOfContents(_data.Slice(headerLength)); - suffix = EndOfContentsEncodedLength; - } - - ReadOnlySpan contents = Slice(_data, headerLength, length.Value); - - if (!skipSortOrderValidation) - { - // T-REC-X.690-201508 sec 11.6 - // BER data is not required to be sorted. - if (RuleSet == AsnEncodingRules.DER || - RuleSet == AsnEncodingRules.CER) - { - AsnValueReader reader = OpenUnchecked(contents, RuleSet); - ReadOnlySpan current = ReadOnlySpan.Empty; - - while (reader.HasData) - { - ReadOnlySpan previous = current; - current = reader.ReadEncodedValue(); - - if (SetOfValueComparer.Compare(current, previous) < 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - } - } - - _data = _data.Slice(headerLength + contents.Length + suffix); - return OpenUnchecked(contents, RuleSet); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public AsnReader ReadSetOf(bool skipSortOrderValidation = false) => - ReadSetOf(Asn1Tag.SetOf, skipSortOrderValidation); - - /// - /// Reads the next value as a SET-OF with the specified tag - /// and returns the result as an positioned at the first - /// value in the set-of (or with == false). - /// - /// The tag to check for before reading. - /// - /// true to always accept the data in the order it is presented, - /// false to verify that the data is sorted correctly when the - /// encoding rules say sorting was required (CER and DER). - /// - /// - /// an positioned at the first - /// value in the set-of (or with == false). - /// - /// - /// the nested content is not evaluated by this method (aside from sort order, when - /// required), and may contain data which is not valid under the current encoding rules. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public AsnReader ReadSetOf(Asn1Tag expectedTag, bool skipSortOrderValidation = false) - { - AsnValueReader valueReader = OpenValueReader(); - AsnValueReader innerValueReader = valueReader.ReadSetOf(expectedTag, skipSortOrderValidation); - - AsnReader ret = new AsnReader(_data, RuleSet); - innerValueReader.MatchSlice(ref ret._data); - - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs deleted file mode 100644 index 6775e77..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Text.cs +++ /dev/null @@ -1,1163 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal ref partial struct AsnValueReader - { - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the contents as an unprocessed - /// over the original data. - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the character string. - /// - /// - /// true and advances the reader if the value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber encodingType, - out ReadOnlySpan contents) - { - return TryReadPrimitiveCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - out contents); - } - - /// - /// Reads the next value as a character with a specified tag, returning the contents - /// as an unprocessed over the original data. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - out ReadOnlySpan contents) - { - CheckCharacterStringEncodingType(encodingType); - - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - return TryReadPrimitiveOctetStringBytes(expectedTag, encodingType, out contents); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - CheckCharacterStringEncodingType(encodingType); - - bool copied = TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination, - out int bytesRead, - out bytesWritten); - - if (copied) - { - _data = _data.Slice(bytesRead); - } - - return copied; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - public bool TryCopyCharacterString( - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - return TryCopyCharacterString( - new Asn1Tag(encodingType), - encodingType, - destination, - out charsWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - return TryCopyCharacterString(expectedTag, encodingType, encoding, destination, out charsWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the decoded value as a . - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - /// - public string ReadCharacterString(UniversalTagNumber encodingType) => - ReadCharacterString(new Asn1Tag(encodingType), encodingType); - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, returning the decoded value as a . - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public string ReadCharacterString(Asn1Tag expectedTag, UniversalTagNumber encodingType) - { - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - return ReadCharacterString(expectedTag, encodingType, encoding); - } - - // T-REC-X.690-201508 sec 8.23 - private bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - Span destination, - out int bytesRead, - out int bytesWritten) - { - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - if (TryReadPrimitiveOctetStringBytes( - expectedTag, - out Asn1Tag actualTag, - out int? contentLength, - out int headerLength, - out ReadOnlySpan contents, - universalTagNumber)) - { - bytesWritten = contents.Length; - - if (destination.Length < bytesWritten) - { - bytesWritten = 0; - bytesRead = 0; - return false; - } - - contents.CopyTo(destination); - bytesRead = headerLength + bytesWritten; - return true; - } - - Debug.Assert(actualTag.IsConstructed); - - bool copied = TryCopyConstructedOctetStringContents( - Slice(_data, headerLength, contentLength), - destination, - contentLength == null, - out int contentBytesRead, - out bytesWritten); - - if (copied) - { - bytesRead = headerLength + contentBytesRead; - } - else - { - bytesRead = 0; - } - - return copied; - } - - private static unsafe bool TryCopyCharacterString( - ReadOnlySpan source, - Span destination, - Text.Encoding encoding, - out int charsWritten) - { - if (source.Length == 0) - { - charsWritten = 0; - return true; - } - - fixed (byte* bytePtr = &MemoryMarshal.GetReference(source)) - fixed (char* charPtr = &MemoryMarshal.GetReference(destination)) - { - try - { - int charCount = encoding.GetCharCount(bytePtr, source.Length); - - if (charCount > destination.Length) - { - charsWritten = 0; - return false; - } - - charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length); - Debug.Assert(charCount == charsWritten); - } - catch (DecoderFallbackException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - - return true; - } - } - - private string ReadCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - Text.Encoding encoding) - { - byte[]? rented = null; - - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - ReadOnlySpan contents = GetOctetStringContents( - expectedTag, - universalTagNumber, - out int bytesRead, - ref rented); - - try - { - string str; - - if (contents.Length == 0) - { - str = string.Empty; - } - else - { - unsafe - { - fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents)) - { - try - { - str = encoding.GetString(bytePtr, contents.Length); - } - catch (DecoderFallbackException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - } - } - - _data = _data.Slice(bytesRead); - return str; - } - finally - { - if (rented != null) - { - CryptoPool.Return(rented, contents.Length); - } - } - } - - private bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber universalTagNumber, - Text.Encoding encoding, - Span destination, - out int charsWritten) - { - byte[]? rented = null; - - // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. - ReadOnlySpan contents = GetOctetStringContents( - expectedTag, - universalTagNumber, - out int bytesRead, - ref rented); - - try - { - bool copied = TryCopyCharacterString( - contents, - destination, - encoding, - out charsWritten); - - if (copied) - { - _data = _data.Slice(bytesRead); - } - - return copied; - } - finally - { - if (rented != null) - { - CryptoPool.Return(rented, contents.Length); - } - } - } - - private static void CheckCharacterStringEncodingType(UniversalTagNumber encodingType) - { - // T-REC-X.680-201508 sec 41 - switch (encodingType) - { - case UniversalTagNumber.BMPString: - case UniversalTagNumber.GeneralString: - case UniversalTagNumber.GraphicString: - case UniversalTagNumber.IA5String: - case UniversalTagNumber.ISO646String: - case UniversalTagNumber.NumericString: - case UniversalTagNumber.PrintableString: - case UniversalTagNumber.TeletexString: - // T61String is an alias for TeletexString (already listed) - case UniversalTagNumber.UniversalString: - case UniversalTagNumber.UTF8String: - case UniversalTagNumber.VideotexString: - // VisibleString is an alias for ISO646String (already listed) - return; - } - - throw new ArgumentOutOfRangeException(nameof(encodingType)); - } - } - - internal partial class AsnReader - { - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the contents as an unprocessed - /// over the original data. - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the contents of the character string. - /// - /// - /// true and advances the reader if the value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber encodingType, - out ReadOnlyMemory contents) - { - return TryReadPrimitiveCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - out contents); - } - - /// - /// Reads the next value as a character with a specified tag, returning the contents - /// as an unprocessed over the original data. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// On success, receives a over the original data - /// corresponding to the value of the OCTET STRING. - /// - /// - /// true and advances the reader if the OCTET STRING value had a primitive encoding, - /// false and does not advance the reader if it had a constructed encoding. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - public bool TryReadPrimitiveCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - out ReadOnlyMemory contents) - { - AsnValueReader valueReader = OpenValueReader(); - ReadOnlySpan span; - - if (valueReader.TryReadPrimitiveCharacterStringBytes(expectedTag, encodingType, out span)) - { - contents = AsnValueReader.Slice(_data, span); - valueReader.MatchSlice(ref _data); - return true; - } - - contents = default; - return false; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination, - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int bytesWritten) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryCopyCharacterStringBytes(expectedTag, encodingType, destination, out bytesWritten)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - new Asn1Tag(encodingType), - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of bytes written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// This method does not determine if the string used only characters defined by the encoding. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterStringBytes( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int bytesWritten) - { - return TryCopyCharacterStringBytes( - expectedTag, - encodingType, - destination.AsSpan(), - out bytesWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - public bool TryCopyCharacterString( - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - return TryCopyCharacterString( - new Asn1Tag(encodingType), - encodingType, - destination, - out charsWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - Span destination, - out int charsWritten) - { - AsnValueReader valueReader = OpenValueReader(); - - if (valueReader.TryCopyCharacterString(expectedTag, encodingType, destination, out charsWritten)) - { - valueReader.MatchSlice(ref _data); - return true; - } - - return false; - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - /// - public bool TryCopyCharacterString( - UniversalTagNumber encodingType, - ArraySegment destination, - out int charsWritten) - { - return TryCopyCharacterString( - new Asn1Tag(encodingType), - encodingType, - destination.AsSpan(), - out charsWritten); - } - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, copying the decoded value into a provided destination buffer. - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// The buffer in which to write. - /// - /// On success, receives the number of chars written to . - /// - /// - /// true and advances the reader if had sufficient - /// length to receive the value, otherwise - /// false and the reader does not advance. - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public bool TryCopyCharacterString( - Asn1Tag expectedTag, - UniversalTagNumber encodingType, - ArraySegment destination, - out int charsWritten) - { - return TryCopyCharacterString( - expectedTag, - encodingType, - destination.AsSpan(), - out charsWritten); - } - - /// - /// Reads the next value as character string with a UNIVERSAL tag appropriate to the specified - /// encoding type, returning the decoded value as a . - /// - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// - /// - /// - public string ReadCharacterString(UniversalTagNumber encodingType) => - ReadCharacterString(new Asn1Tag(encodingType), encodingType); - - /// - /// Reads the next value as character string with the specified tag and - /// encoding type, returning the decoded value as a . - /// - /// The tag to check for before reading. - /// - /// A corresponding to the value type to process. - /// - /// - /// the decoded value as a . - /// - /// - /// is not a known character string type. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules --OR-- - /// the string did not successfully decode - /// - /// - /// . is - /// , but - /// . is not the same as - /// . - /// - /// - /// - /// - public string ReadCharacterString(Asn1Tag expectedTag, UniversalTagNumber encodingType) - { - AsnValueReader valueReader = OpenValueReader(); - string ret = valueReader.ReadCharacterString(expectedTag, encodingType); - valueReader.MatchSlice(ref _data); - return ret; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs deleted file mode 100644 index 81bcbfd..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.cs +++ /dev/null @@ -1,675 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers.Text; -using System.Diagnostics; - -namespace System.Security.Cryptography.Asn1 -{ - /// - /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. - /// - internal ref partial struct AsnValueReader - { - // T-REC-X.690-201508 sec 9.2 - internal const int MaxCERSegmentSize = 1000; - - // T-REC-X.690-201508 sec 8.1.5 says only 0000 is legal. - internal const int EndOfContentsEncodedLength = 2; - - private ReadOnlySpan _data; - - /// - /// The in use by this reader. - /// - public AsnEncodingRules RuleSet { get; } - - /// - /// An indication of whether or not the reader has remaining data available to process. - /// - public bool HasData => !_data.IsEmpty; - - /// - /// Construct an over with a given ruleset. - /// - /// The data to read. - /// The encoding constraints for the reader. - /// - /// This constructor does not evaluate for correctness, - /// any correctness checks are done as part of member methods. - /// - /// This constructor does not copy . The caller is responsible for - /// ensuring that the values do not change until the reader is finished. - /// - /// - /// is not defined. - /// - public AsnValueReader(ReadOnlySpan data, AsnEncodingRules ruleSet) - { - CheckEncodingRules(ruleSet); - - _data = data; - RuleSet = ruleSet; - } - - // Reversed parameter order, to be used by OpenUnchecked. - private AsnValueReader(AsnEncodingRules ruleSet, ReadOnlySpan data) - { - _data = data; - RuleSet = ruleSet; - } - - internal static AsnValueReader OpenUnchecked(ReadOnlySpan data, AsnEncodingRules ruleSet) - { - return new AsnValueReader(ruleSet, data); - } - - /// - /// Throws a standardized if the reader has remaining - /// data, performs no function if returns false. - /// - /// - /// This method provides a standardized target and standardized exception for reading a - /// "closed" structure, such as the nested content for an explicitly tagged value. - /// - public void ThrowIfNotEmpty() - { - if (HasData) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - /// - /// Read the encoded tag at the next data position, without advancing the reader. - /// - /// - /// The decoded value. - /// - /// - /// a tag could not be decoded at the reader's current position. - /// - public Asn1Tag PeekTag() - { - if (Asn1Tag.TryDecode(_data, out Asn1Tag tag, out int bytesRead)) - { - return tag; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - /// - /// Get a view of the next encoded value without - /// advancing the reader. For indefinite length encodings this includes the - /// End of Contents marker. - /// - /// A view of the next encoded value. - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - /// - public ReadOnlySpan PeekEncodedValue() - { - return Slice(_data, 0, GetNextEncodedValueLength()); - } - - internal int GetNextEncodedValueLength() - { - ReadTagAndLength(out int? length, out int bytesRead); - - if (length == null) - { - int contentsLength = SeekEndOfContents(_data.Slice(bytesRead)); - return bytesRead + contentsLength + AsnReader.EndOfContentsEncodedLength; - } - - return bytesRead + length.Value; - } - - /// - /// Get a view of the content octets (bytes) of the - /// next encoded value without advancing the reader. - /// - /// - /// A view of the contents octets of the next encoded value. - /// - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - public ReadOnlySpan PeekContentBytes() - { - (int offset, int length) = GetNextContentRange(); - return Slice(_data, offset, length); - } - - internal (int Offset, int Length) GetNextContentRange() - { - ReadTagAndLength(out int? length, out int bytesRead); - - if (length == null) - { - return (bytesRead, SeekEndOfContents(_data.Slice(bytesRead))); - } - - return (bytesRead, length.Value); - } - - /// - /// Get a view of the next encoded value, - /// and advance the reader past it. For an indefinite length encoding this includes - /// the End of Contents marker. - /// - /// A view of the next encoded value. - /// - public ReadOnlySpan ReadEncodedValue() - { - ReadOnlySpan encodedValue = PeekEncodedValue(); - _data = _data.Slice(encodedValue.Length); - return encodedValue; - } - - private static bool TryReadLength( - ReadOnlySpan source, - AsnEncodingRules ruleSet, - out int? length, - out int bytesRead) - { - length = null; - bytesRead = 0; - - AssertEncodingRules(ruleSet); - - if (source.IsEmpty) - { - return false; - } - - // T-REC-X.690-201508 sec 8.1.3 - - byte lengthOrLengthLength = source[bytesRead]; - bytesRead++; - const byte MultiByteMarker = 0x80; - - // 0x00-0x7F are direct length values. - // 0x80 is BER/CER indefinite length. - // 0x81-0xFE says that the length takes the next 1-126 bytes. - // 0xFF is forbidden. - if (lengthOrLengthLength == MultiByteMarker) - { - // T-REC-X.690-201508 sec 10.1 (DER: Length forms) - if (ruleSet == AsnEncodingRules.DER) - { - bytesRead = 0; - return false; - } - - // Null length == indefinite. - return true; - } - - if (lengthOrLengthLength < MultiByteMarker) - { - length = lengthOrLengthLength; - return true; - } - - if (lengthOrLengthLength == 0xFF) - { - bytesRead = 0; - return false; - } - - byte lengthLength = (byte)(lengthOrLengthLength & ~MultiByteMarker); - - // +1 for lengthOrLengthLength - if (lengthLength + 1 > source.Length) - { - bytesRead = 0; - return false; - } - - // T-REC-X.690-201508 sec 9.1 (CER: Length forms) - // T-REC-X.690-201508 sec 10.1 (DER: Length forms) - bool minimalRepresentation = - ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER; - - // The ITU-T specifications tecnically allow lengths up to ((2^128) - 1), but - // since Span's length is a signed Int32 we're limited to identifying memory - // that is within ((2^31) - 1) bytes of the tag start. - if (minimalRepresentation && lengthLength > sizeof(int)) - { - bytesRead = 0; - return false; - } - - uint parsedLength = 0; - - for (int i = 0; i < lengthLength; i++) - { - byte current = source[bytesRead]; - bytesRead++; - - if (parsedLength == 0) - { - if (minimalRepresentation && current == 0) - { - bytesRead = 0; - return false; - } - - if (!minimalRepresentation && current != 0) - { - // Under BER rules we could have had padding zeros, so - // once the first data bits come in check that we fit within - // sizeof(int) due to Span bounds. - - if (lengthLength - i > sizeof(int)) - { - bytesRead = 0; - return false; - } - } - } - - parsedLength <<= 8; - parsedLength |= current; - } - - // This value cannot be represented as a Span length. - if (parsedLength > int.MaxValue) - { - bytesRead = 0; - return false; - } - - if (minimalRepresentation && parsedLength < MultiByteMarker) - { - bytesRead = 0; - return false; - } - - Debug.Assert(bytesRead > 0); - length = (int)parsedLength; - return true; - } - - internal Asn1Tag ReadTagAndLength(out int? contentsLength, out int bytesRead) - { - if (Asn1Tag.TryDecode(_data, out Asn1Tag tag, out int tagBytesRead) && - TryReadLength(_data.Slice(tagBytesRead), RuleSet, out int? length, out int lengthBytesRead)) - { - int allBytesRead = tagBytesRead + lengthBytesRead; - - if (tag.IsConstructed) - { - // T-REC-X.690-201508 sec 9.1 (CER: Length forms) says constructed is always indefinite. - if (RuleSet == AsnEncodingRules.CER && length != null) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - else if (length == null) - { - // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - bytesRead = allBytesRead; - contentsLength = length; - return tag; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLength) - { - // T-REC-X.690-201508 sec 8.1.5 excludes the BER 8100 length form for 0. - if (tag.IsConstructed || length != 0 || headerLength != EndOfContentsEncodedLength) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - /// - /// Get the number of bytes between the start of and - /// the End-of-Contents marker - /// - private int SeekEndOfContents(ReadOnlySpan source) - { - return SeekEndOfContents(source, RuleSet); - } - - /// - /// Get the number of bytes between the start of and - /// the End-of-Contents marker - /// - internal static int SeekEndOfContents(ReadOnlySpan source, AsnEncodingRules ruleSet) - { - ReadOnlySpan cur = source; - int totalLen = 0; - - AsnValueReader tmpReader = OpenUnchecked(cur, ruleSet); - // Our reader is bounded by int.MaxValue. - // The most aggressive data input would be a one-byte tag followed by - // indefinite length "ad infinitum", which would be half the input. - // So the depth marker can never overflow the signed integer space. - int depth = 1; - - while (tmpReader.HasData) - { - Asn1Tag tag = tmpReader.ReadTagAndLength(out int? length, out int bytesRead); - - if (tag == Asn1Tag.EndOfContents) - { - ValidateEndOfContents(tag, length, bytesRead); - - depth--; - - if (depth == 0) - { - // T-REC-X.690-201508 sec 8.1.1.1 / 8.1.1.3 indicate that the - // End-of-Contents octets are "after" the contents octets, not - // "at the end" of them, so we don't include these bytes in the - // accumulator. - return totalLen; - } - } - - // We found another indefinite length, that means we need to find another - // EndOfContents marker to balance it out. - if (length == null) - { - depth++; - tmpReader._data = tmpReader._data.Slice(bytesRead); - totalLen += bytesRead; - } - else - { - // This will throw a CryptographicException if the length exceeds our bounds. - ReadOnlySpan tlv = Slice(tmpReader._data, 0, bytesRead + length.Value); - - // No exception? Then slice the data and continue. - tmpReader._data = tmpReader._data.Slice(tlv.Length); - totalLen += tlv.Length; - } - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan data, int bytesToRead) - { - int value = ParseNonNegativeInt(Slice(data, 0, bytesToRead)); - data = data.Slice(bytesToRead); - - return value; - } - - internal static int ParseNonNegativeInt(ReadOnlySpan data) - { - if (Utf8Parser.TryParse(data, out uint value, out int consumed) && - value <= int.MaxValue && - consumed == data.Length) - { - return (int)value; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private static ReadOnlySpan SliceAtMost(ReadOnlySpan source, int longestPermitted) - { - int len = Math.Min(longestPermitted, source.Length); - return source.Slice(0, len); - } - - private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int length) - { - Debug.Assert(offset >= 0); - - if (length < 0 || source.Length - offset < length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return source.Slice(offset, length); - } - - private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int? length) - { - Debug.Assert(offset >= 0); - - if (length == null) - { - return source.Slice(offset); - } - - int lengthVal = length.Value; - - if (lengthVal < 0 || source.Length - offset < lengthVal) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return source.Slice(offset, lengthVal); - } - - internal static ReadOnlyMemory Slice(ReadOnlyMemory bigger, ReadOnlySpan smaller) - { - if (smaller.IsEmpty) - { - return default; - } - - if (bigger.Span.Overlaps(smaller, out int offset)) - { - return bigger.Slice(offset, smaller.Length); - } - - Debug.Fail("AsnReader asked for a matching slice from a non-overlapping input"); - throw new CryptographicException(); - } - - /// - /// Slices to represent the same data this reader value has. - /// - /// - /// does not represent a super-range of the data this reader is processing. - /// - internal void MatchSlice(ref ReadOnlyMemory memory) - { - memory = Slice(memory, _data); - } - - [Conditional("DEBUG")] - private static void AssertEncodingRules(AsnEncodingRules ruleSet) - { - Debug.Assert(ruleSet >= AsnEncodingRules.BER && ruleSet <= AsnEncodingRules.DER); - } - - internal static void CheckEncodingRules(AsnEncodingRules ruleSet) - { - if (ruleSet != AsnEncodingRules.BER && - ruleSet != AsnEncodingRules.CER && - ruleSet != AsnEncodingRules.DER) - { - throw new ArgumentOutOfRangeException(nameof(ruleSet)); - } - } - - internal static void CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber) - { - if (expectedTag.TagClass == TagClass.Universal && expectedTag.TagValue != (int)tagNumber) - { - throw new ArgumentException( - SR.Cryptography_Asn_UniversalValueIsFixed, - nameof(expectedTag)); - } - - if (expectedTag.TagClass != tag.TagClass || expectedTag.TagValue != tag.TagValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - } - - /// - /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. - /// - internal partial class AsnReader - { - internal const int MaxCERSegmentSize = AsnValueReader.MaxCERSegmentSize; - internal const int EndOfContentsEncodedLength = AsnValueReader.EndOfContentsEncodedLength; - - private ReadOnlyMemory _data; - - /// - /// The in use by this reader. - /// - public AsnEncodingRules RuleSet { get; } - - /// - /// An indication of whether or not the reader has remaining data available to process. - /// - public bool HasData => !_data.IsEmpty; - - /// - /// Construct an over with a given ruleset. - /// - /// The data to read. - /// The encoding constraints for the reader. - /// - /// This constructor does not evaluate for correctness, - /// any correctness checks are done as part of member methods. - /// - /// This constructor does not copy . The caller is responsible for - /// ensuring that the values do not change until the reader is finished. - /// - /// - /// is not defined. - /// - public AsnReader(ReadOnlyMemory data, AsnEncodingRules ruleSet) - { - AsnValueReader.CheckEncodingRules(ruleSet); - - _data = data; - RuleSet = ruleSet; - } - - /// - /// Throws a standardized if the reader has remaining - /// data, performs no function if returns false. - /// - /// - /// This method provides a standardized target and standardized exception for reading a - /// "closed" structure, such as the nested content for an explicitly tagged value. - /// - public void ThrowIfNotEmpty() - { - if (HasData) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - - /// - /// Read the encoded tag at the next data position, without advancing the reader. - /// - /// - /// The decoded value. - /// - /// - /// a tag could not be decoded at the reader's current position. - /// - public Asn1Tag PeekTag() - { - if (Asn1Tag.TryDecode(_data.Span, out Asn1Tag tag, out int bytesRead)) - { - return tag; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - private AsnValueReader OpenValueReader() - { - return AsnValueReader.OpenUnchecked(_data.Span, RuleSet); - } - - /// - /// Get a view of the next encoded value without - /// advancing the reader. For indefinite length encodings this includes the - /// End of Contents marker. - /// - /// A view of the next encoded value. - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - /// - public ReadOnlyMemory PeekEncodedValue() - { - AsnValueReader reader = OpenValueReader(); - return Slice(_data, 0, reader.GetNextEncodedValueLength()); - } - - /// - /// Get a view of the content octets (bytes) of the - /// next encoded value without advancing the reader. - /// - /// - /// A view of the contents octets of the next encoded value. - /// - /// - /// The reader is positioned at a point where the tag or length is invalid - /// under the current encoding rules. - /// - /// - public ReadOnlyMemory PeekContentBytes() - { - AsnValueReader reader = OpenValueReader(); - (int offset, int length) = reader.GetNextContentRange(); - return Slice(_data, offset, length); - } - - /// - /// Get a view of the next encoded value, - /// and advance the reader past it. For an indefinite length encoding this includes - /// the End of Contents marker. - /// - /// A view of the next encoded value. - /// - public ReadOnlyMemory ReadEncodedValue() - { - ReadOnlyMemory encodedValue = PeekEncodedValue(); - _data = _data.Slice(encodedValue.Length); - return encodedValue; - } - - internal Asn1Tag ReadTagAndLength(out int? contentsLength, out int bytesRead) - { - AsnValueReader reader = OpenValueReader(); - return reader.ReadTagAndLength(out contentsLength, out bytesRead); - } - - private static ReadOnlyMemory Slice(ReadOnlyMemory source, int offset, int length) - { - Debug.Assert(offset >= 0); - - if (length < 0 || source.Length - offset < length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return source.Slice(offset, length); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs new file mode 100644 index 0000000..23b3292 --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + internal ref struct AsnValueReader + { + private static readonly byte[] s_singleByte = new byte[1]; + + private ReadOnlySpan _span; + private readonly AsnEncodingRules _ruleSet; + + internal AsnValueReader(ReadOnlySpan span, AsnEncodingRules ruleSet) + { + _span = span; + _ruleSet = ruleSet; + } + + internal bool HasData => !_span.IsEmpty; + + internal void ThrowIfNotEmpty() + { + if (!_span.IsEmpty) + { + new AsnReader(s_singleByte, _ruleSet).ThrowIfNotEmpty(); + } + } + + internal Asn1Tag PeekTag() + { + return Asn1Tag.Decode(_span, out _); + } + + public ReadOnlySpan PeekEncodedValue() + { + AsnDecoder.ReadEncodedValue(_span, _ruleSet, out _, out _, out int consumed); + return _span.Slice(0, consumed); + } + + internal ReadOnlySpan ReadEncodedValue() + { + ReadOnlySpan value = PeekEncodedValue(); + _span = _span.Slice(value.Length); + return value; + } + + internal bool ReadBoolean(Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.ReadBoolean(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal BigInteger ReadInteger(Asn1Tag? expectedTag = default) + { + BigInteger ret = AsnDecoder.ReadInteger(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal bool TryReadInt32(out int value, Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.TryReadInt32(_span, _ruleSet, out value, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal ReadOnlySpan ReadIntegerBytes(Asn1Tag? expectedTag = default) + { + ReadOnlySpan ret = AsnDecoder.ReadIntegerBytes(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal bool TryReadPrimitiveBitString( + out int unusedBitCount, + out ReadOnlySpan value, + Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.TryReadPrimitiveBitString( + _span, + _ruleSet, + out unusedBitCount, + out value, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = default) + { + byte[] ret = AsnDecoder.ReadBitString( + _span, + _ruleSet, + out unusedBitCount, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal TFlagsEnum ReadNamedBitListValue(Asn1Tag? expectedTag = default) where TFlagsEnum : Enum + { + TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal bool TryReadPrimitiveOctetString( + out ReadOnlySpan value, + Asn1Tag? expectedTag = default) + { + bool ret = AsnDecoder.TryReadPrimitiveOctetString( + _span, + _ruleSet, + out value, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal byte[] ReadOctetString(Asn1Tag? expectedTag = default) + { + byte[] ret = AsnDecoder.ReadOctetString( + _span, + _ruleSet, + out int consumed, + expectedTag); + + _span = _span.Slice(consumed); + return ret; + } + + internal string ReadObjectIdentifier(Asn1Tag? expectedTag = default) + { + string ret = AsnDecoder.ReadObjectIdentifier(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + internal AsnValueReader ReadSequence(Asn1Tag? expectedTag = default) + { + AsnDecoder.ReadSequence( + _span, + _ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + expectedTag); + + ReadOnlySpan content = _span.Slice(contentOffset, contentLength); + _span = _span.Slice(bytesConsumed); + return new AsnValueReader(content, _ruleSet); + } + + internal AsnValueReader ReadSetOf(Asn1Tag? expectedTag = default) + { + AsnDecoder.ReadSetOf( + _span, + _ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + expectedTag: expectedTag); + + ReadOnlySpan content = _span.Slice(contentOffset, contentLength); + _span = _span.Slice(bytesConsumed); + return new AsnValueReader(content, _ruleSet); + } + + internal DateTimeOffset ReadUtcTime(Asn1Tag? expectedTag = default) + { + DateTimeOffset ret = AsnDecoder.ReadUtcTime(_span, _ruleSet, out int consumed, expectedTag: expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + public DateTimeOffset ReadGeneralizedTime(Asn1Tag? expectedTag = default) + { + DateTimeOffset ret = AsnDecoder.ReadGeneralizedTime(_span, _ruleSet, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + + public string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = default) + { + string ret = AsnDecoder.ReadCharacterString(_span, _ruleSet, encodingType, out int consumed, expectedTag); + _span = _span.Slice(consumed); + return ret; + } + } + + internal static class AsnWriterExtensions + { + internal static void WriteEncodedValueForCrypto( + this AsnWriter writer, + ReadOnlySpan value) + { + try + { + writer.WriteEncodedValue(value); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void WriteObjectIdentifierForCrypto( + this AsnWriter writer, + string value) + { + try + { + writer.WriteObjectIdentifier(value); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs deleted file mode 100644 index 64eb110..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.BitString.cs +++ /dev/null @@ -1,387 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Buffers; -using System.Diagnostics; - -#nullable enable -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write a Bit String value with a tag UNIVERSAL 3. - /// - /// The value to write. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// is not in the range [0,7] - /// - /// - /// has length 0 and is not 0 --OR-- - /// is not empty and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString(ReadOnlySpan bitString, int unusedBitCount = 0) - { - WriteBitStringCore(Asn1Tag.PrimitiveBitString, bitString, unusedBitCount); - } - - /// - /// Write a Bit String value with a specified tag. - /// - /// The tag to write. - /// The value to write. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// is not in the range [0,7] - /// - /// - /// has length 0 and is not 0 --OR-- - /// is not empty and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount = 0) - { - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - // Primitive or constructed, doesn't matter. - WriteBitStringCore(tag, bitString, unusedBitCount); - } - - // T-REC-X.690-201508 sec 8.6 - private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount) - { - // T-REC-X.690-201508 sec 8.6.2.2 - if (unusedBitCount < 0 || unusedBitCount > 7) - { - throw new ArgumentOutOfRangeException( - nameof(unusedBitCount), - unusedBitCount, - SR.Cryptography_Asn_UnusedBitCountRange); - } - - CheckDisposed(); - - // T-REC-X.690-201508 sec 8.6.2.3 - if (bitString.Length == 0 && unusedBitCount != 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1]; - - // T-REC-X.690-201508 sec 11.2 - // - // This could be ignored for BER, but since DER is more common and - // it likely suggests a program error on the caller, leave it enabled for - // BER for now. - if (!CheckValidLastByte(lastByte, unusedBitCount)) - { - // TODO: Probably warrants a distinct message. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (RuleSet == AsnEncodingRules.CER) - { - // T-REC-X.690-201508 sec 9.2 - // - // If it's not within a primitive segment, use the constructed encoding. - // (>= instead of > because of the unused bit count byte) - if (bitString.Length >= AsnReader.MaxCERSegmentSize) - { - WriteConstructedCerBitString(tag, bitString, unusedBitCount); - return; - } - } - - // Clear the constructed flag, if present. - WriteTag(tag.AsPrimitive()); - // The unused bits byte requires +1. - WriteLength(bitString.Length + 1); - _buffer[_offset] = (byte)unusedBitCount; - _offset++; - bitString.CopyTo(_buffer.AsSpan(_offset)); - _offset += bitString.Length; - } - -#if NETCOREAPP || NETSTANDARD2_1 - /// - /// Write a Bit String value via a callback, with a tag UNIVERSAL 3. - /// - /// The total number of bytes to write. - /// A state object to pass to . - /// A callback to invoke for populating the Bit String. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// is negative --OR-- - /// is not in the range [0,7] - /// - /// - /// is 0 and is not 0 --OR-- - /// is not 0 and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString( - int byteLength, - TState state, - SpanAction action, - int unusedBitCount = 0) - { - WriteBitStringCore(Asn1Tag.PrimitiveBitString, byteLength, state, action, unusedBitCount); - } - - /// - /// Write a Bit String value via a callback, with a specified tag. - /// - /// The tag to write. - /// The total number of bytes to write. - /// A state object to pass to . - /// A callback to invoke for populating the Bit String. - /// - /// The number of trailing bits which are not semantic. - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// is negative --OR-- - /// is not in the range [0,7] - /// - /// - /// is 0 and is not 0 --OR-- - /// is not 0 and any of the bits identified by - /// is set - /// - /// The writer has been Disposed. - public void WriteBitString( - Asn1Tag tag, - int byteLength, - TState state, - SpanAction action, - int unusedBitCount = 0) - { - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - // Primitive or constructed, doesn't matter. - WriteBitStringCore(tag, byteLength, state, action, unusedBitCount); - } - - // T-REC-X.690-201508 sec 8.6 - private void WriteBitStringCore( - Asn1Tag tag, - int byteLength, - TState state, - SpanAction action, - int unusedBitCount = 0) - { - if (byteLength == 0) - { - WriteBitStringCore(tag, ReadOnlySpan.Empty, unusedBitCount); - return; - } - - // T-REC-X.690-201508 sec 8.6.2.2 - if (unusedBitCount < 0 || unusedBitCount > 7) - { - throw new ArgumentOutOfRangeException( - nameof(unusedBitCount), - unusedBitCount, - SR.Cryptography_Asn_UnusedBitCountRange); - } - - CheckDisposed(); - - int savedOffset = _offset; - Span scratchSpace; - byte[]? ensureNoExtraCopy = null; - int expectedSize = 0; - - // T-REC-X.690-201508 sec 9.2 - // - // If it's not within a primitive segment, use the constructed encoding. - // (>= instead of > because of the unused bit count byte) - bool segmentedWrite = - RuleSet == AsnEncodingRules.CER && byteLength >= AsnReader.MaxCERSegmentSize; - - if (segmentedWrite) - { - // Rather than call the callback multiple times, grow the buffer to allow - // for enough space for the final output, then return a buffer where the last segment - // is in the correct place. (Data will shift backwards to the right spot while writing - // other segments). - expectedSize = DetermineCerBitStringTotalLength(tag, byteLength); - EnsureWriteCapacity(expectedSize); - int overhead = expectedSize - byteLength; - - // Start writing where the last content byte is in the correct place, which is - // after all of the overhead, but ending before the two byte end-of-contents marker. - int scratchStart = overhead - 2; - ensureNoExtraCopy = _buffer; - scratchSpace = _buffer.AsSpan(scratchStart, byteLength); - - // Don't let gapped-writes be unpredictable. - scratchSpace.Clear(); - } - else - { - WriteTag(tag.AsPrimitive()); - // The unused bits byte requires +1. - WriteLength(byteLength + 1); - - _buffer[_offset] = (byte)unusedBitCount; - _offset++; - - scratchSpace = _buffer.AsSpan(_offset, byteLength); - } - - action(scratchSpace, state); - - // T-REC-X.690-201508 sec 11.2 - // - // This could be ignored for BER, but since DER is more common and - // it likely suggests a program error on the caller, leave it enabled for - // BER for now. - if (!CheckValidLastByte(scratchSpace[byteLength - 1], unusedBitCount)) - { - // Since we are restoring _offset we won't clear this on a grow or Dispose, - // so clear it now. - _offset = savedOffset; - scratchSpace.Clear(); - - // TODO: Probably warrants a distinct message. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (segmentedWrite) - { - WriteConstructedCerBitString(tag, scratchSpace, unusedBitCount); - Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); - Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during while writing a bit string via callback"); - } - else - { - _offset += byteLength; - } - } -#endif - - private static bool CheckValidLastByte(byte lastByte, int unusedBitCount) - { - // If 3 bits are "unused" then build a mask for them to check for 0. - // 1 << 3 => 0b0000_1000 - // subtract 1 => 0b000_0111 - int mask = (1 << unusedBitCount) - 1; - return ((lastByte & mask) == 0); - } - - private static int DetermineCerBitStringTotalLength(Asn1Tag tag, int contentLength) - { - const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; - // Every segment has an "unused bit count" byte. - const int MaxCERContentSize = MaxCERSegmentSize - 1; - Debug.Assert(contentLength > MaxCERContentSize); - - int fullSegments = Math.DivRem(contentLength, MaxCERContentSize, out int lastContentSize); - - // The tag size is 1 byte. - // The length will always be encoded as 82 03 E8 (3 bytes) - // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) - const int FullSegmentEncodedSize = 1004; - Debug.Assert( - FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); - - int remainingEncodedSize; - - if (lastContentSize == 0) - { - remainingEncodedSize = 0; - } - else - { - // One byte of tag, minimum one byte of length, and one byte of unused bit count. - remainingEncodedSize = 3 + lastContentSize + GetEncodedLengthSubsequentByteCount(lastContentSize); - } - - // Reduce the number of copies by pre-calculating the size. - // +2 for End-Of-Contents - // +1 for 0x80 indefinite length - // +tag length - return fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 3 + tag.CalculateEncodedSize(); - } - - // T-REC-X.690-201508 sec 9.2, 8.6 - private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan payload, int unusedBitCount) - { - const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; - // Every segment has an "unused bit count" byte. - const int MaxCERContentSize = MaxCERSegmentSize - 1; - Debug.Assert(payload.Length > MaxCERContentSize); - - int expectedSize = DetermineCerBitStringTotalLength(tag, payload.Length); - EnsureWriteCapacity(expectedSize); - int savedOffset = _offset; - - WriteTag(tag.AsConstructed()); - // T-REC-X.690-201508 sec 9.1 - // Constructed CER uses the indefinite form. - WriteLength(-1); - - byte[] ensureNoExtraCopy = _buffer; - - ReadOnlySpan remainingData = payload; - Span dest; - Asn1Tag primitiveBitString = Asn1Tag.PrimitiveBitString; - - while (remainingData.Length > MaxCERContentSize) - { - // T-REC-X.690-201508 sec 8.6.4.1 - WriteTag(primitiveBitString); - WriteLength(MaxCERSegmentSize); - // 0 unused bits in this segment. - _buffer[_offset] = 0; - _offset++; - - dest = _buffer.AsSpan(_offset); - remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); - - remainingData = remainingData.Slice(MaxCERContentSize); - _offset += MaxCERContentSize; - } - - WriteTag(primitiveBitString); - WriteLength(remainingData.Length + 1); - - _buffer[_offset] = (byte)unusedBitCount; - _offset++; - - dest = _buffer.AsSpan(_offset); - remainingData.CopyTo(dest); - _offset += remainingData.Length; - - WriteEndOfContents(); - - Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); - Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerBitString)}"); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs deleted file mode 100644 index 695c958..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Enumerated.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The boxed enumeration value to write - /// - /// is null - /// - /// - /// is not a boxed enum value --OR-- - /// the unboxed type of is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - WriteEnumeratedValue(Asn1Tag.Enumerated, enumValue); - } - - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The boxed enumeration value to write - /// - /// is null - /// - /// - /// is not a boxed enum value --OR-- - /// is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(TEnum enumValue) where TEnum : struct - { - WriteEnumeratedValue(Asn1Tag.Enumerated, typeof(TEnum), enumValue); - } - - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The tag to write. - /// The boxed enumeration value to write. - /// - /// is null - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not a boxed enum value --OR-- - /// the unboxed type of is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(Asn1Tag tag, object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - WriteEnumeratedValue(tag.AsPrimitive(), enumValue.GetType(), enumValue); - } - - /// - /// Write a non-[] enum value as an Enumerated with - /// tag UNIVERSAL 10. - /// - /// The tag to write. - /// The boxed enumeration value to write. - /// - /// is null - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not an enum --OR-- - /// is declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteEnumeratedValue(Asn1Tag tag, TEnum enumValue) where TEnum : struct - { - WriteEnumeratedValue(tag.AsPrimitive(), typeof(TEnum), enumValue); - } - - // T-REC-X.690-201508 sec 8.4 - private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object enumValue) - { - CheckUniversalTag(tag, UniversalTagNumber.Enumerated); - - Type backingType = tEnum.GetEnumUnderlyingType(); - - if (tEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_EnumeratedValueRequiresNonFlagsEnum, - nameof(tEnum)); - } - - if (backingType == typeof(ulong)) - { - ulong numericValue = Convert.ToUInt64(enumValue); - // T-REC-X.690-201508 sec 8.4 - WriteNonNegativeIntegerCore(tag, numericValue); - } - else - { - // All other types fit in a (signed) long. - long numericValue = Convert.ToInt64(enumValue); - // T-REC-X.690-201508 sec 8.4 - WriteIntegerCore(tag, numericValue); - } - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs deleted file mode 100644 index 76c7d0d..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.NamedBitList.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write a [] enum value as a NamedBitList with - /// tag UNIVERSAL 3. - /// - /// The boxed enumeration value to write - /// - /// is null - /// - /// - /// is not a boxed enum value --OR-- - /// the unboxed type of is not declared [] - /// - /// The writer has been Disposed. - /// - /// - public void WriteNamedBitList(object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); - } - - /// - /// Write a [] enum value as a NamedBitList with - /// tag UNIVERSAL 3. - /// - /// The enumeration value to write - /// - /// is not an enum value --OR-- - /// is not declared [] - /// - /// The writer has been Disposed. - /// - public void WriteNamedBitList(TEnum enumValue) where TEnum : struct - { - WriteNamedBitList(Asn1Tag.PrimitiveBitString, enumValue); - } - - /// - /// Write a [] enum value as a NamedBitList with - /// a specified tag. - /// - /// The tag to write. - /// The boxed enumeration value to write - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not a boxed enum value --OR-- - /// the unboxed type of is not declared [] - /// - /// - /// is null - /// - /// The writer has been Disposed. - public void WriteNamedBitList(Asn1Tag tag, object enumValue) - { - if (enumValue == null) - throw new ArgumentNullException(nameof(enumValue)); - - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - WriteNamedBitList(tag, enumValue.GetType(), enumValue); - } - - /// - /// Write a [] enum value as a NamedBitList with - /// a specified tag. - /// - /// The tag to write. - /// The enumeration value to write - /// - /// . is - /// , but - /// . is not correct for - /// the method --OR-- - /// is not an enum value --OR-- - /// is not declared [] - /// - /// The writer has been Disposed. - public void WriteNamedBitList(Asn1Tag tag, TEnum enumValue) where TEnum : struct - { - CheckUniversalTag(tag, UniversalTagNumber.BitString); - - WriteNamedBitList(tag, typeof(TEnum), enumValue); - } - - private void WriteNamedBitList(Asn1Tag tag, Type tEnum, object enumValue) - { - Type backingType = tEnum.GetEnumUnderlyingType(); - - if (!tEnum.IsDefined(typeof(FlagsAttribute), false)) - { - throw new ArgumentException( - SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, - nameof(tEnum)); - } - - ulong integralValue; - - if (backingType == typeof(ulong)) - { - integralValue = Convert.ToUInt64(enumValue); - } - else - { - // All other types fit in a (signed) long. - long numericValue = Convert.ToInt64(enumValue); - integralValue = unchecked((ulong)numericValue); - } - - WriteNamedBitList(tag, integralValue); - } - - // T-REC-X.680-201508 sec 22 - // T-REC-X.690-201508 sec 8.6, 11.2.2 - private void WriteNamedBitList(Asn1Tag tag, ulong integralValue) - { - Span temp = stackalloc byte[sizeof(ulong)]; - // Reset to all zeros, since we're just going to or-in bits we need. - temp.Clear(); - - int indexOfHighestSetBit = -1; - - for (int i = 0; integralValue != 0; integralValue >>= 1, i++) - { - if ((integralValue & 1) != 0) - { - temp[i / 8] |= (byte)(0x80 >> (i % 8)); - indexOfHighestSetBit = i; - } - } - - if (indexOfHighestSetBit < 0) - { - // No bits were set; this is an empty bit string. - // T-REC-X.690-201508 sec 11.2.2-note2 - WriteBitString(tag, ReadOnlySpan.Empty); - } - else - { - // At least one bit was set. - // Determine the shortest length necessary to represent the bit string. - - // Since "bit 0" gets written down 0 => 1. - // Since "bit 8" is in the second byte 8 => 2. - // That makes the formula ((bit / 8) + 1) instead of ((bit + 7) / 8). - int byteLen = (indexOfHighestSetBit / 8) + 1; - int unusedBitCount = 7 - (indexOfHighestSetBit % 8); - - WriteBitString( - tag, - temp.Slice(0, byteLen), - unusedBitCount); - } - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs deleted file mode 100644 index 78d4268..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Sequence.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Begin writing a Sequence with tag UNIVERSAL 16. - /// - /// The writer has been Disposed. - /// - /// - public void PushSequence() - { - PushSequenceCore(Asn1Tag.Sequence); - } - - /// - /// Begin writing a Sequence with a specified tag. - /// - /// The tag to write. - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// The writer has been Disposed. - /// - public void PushSequence(Asn1Tag tag) - { - CheckUniversalTag(tag, UniversalTagNumber.Sequence); - - // Assert the constructed flag, in case it wasn't. - PushSequenceCore(tag.AsConstructed()); - } - - /// - /// Indicate that the open Sequence with tag UNIVERSAL 16 is closed, - /// returning the writer to the parent context. - /// - /// - /// the writer is not currently positioned within a Sequence with tag UNIVERSAL 16 - /// - /// The writer has been Disposed. - /// - /// - public void PopSequence() - { - PopSequenceCore(Asn1Tag.Sequence); - } - - /// - /// Indicate that the open Sequence with the specified tag is closed, - /// returning the writer to the parent context. - /// - /// The tag to write. - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// the writer is not currently positioned within a Sequence with the specified tag - /// - /// The writer has been Disposed. - /// - /// - public void PopSequence(Asn1Tag tag) - { - // PopSequence shouldn't be used to pop a SetOf. - CheckUniversalTag(tag, UniversalTagNumber.Sequence); - - // Assert the constructed flag, in case it wasn't. - PopSequenceCore(tag.AsConstructed()); - } - - // T-REC-X.690-201508 sec 8.9, 8.10 - private void PushSequenceCore(Asn1Tag tag) - { - PushTag(tag.AsConstructed(), UniversalTagNumber.Sequence); - } - - // T-REC-X.690-201508 sec 8.9, 8.10 - private void PopSequenceCore(Asn1Tag tag) - { - PopTag(tag, UniversalTagNumber.Sequence); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs deleted file mode 100644 index 83cb6e8..0000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Text.cs +++ /dev/null @@ -1,203 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Security.Cryptography.Asn1 -{ - internal sealed partial class AsnWriter - { - /// - /// Write the provided string using the specified encoding type using the UNIVERSAL - /// tag corresponding to the encoding type. - /// - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// is null - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// The writer has been Disposed. - /// - public void WriteCharacterString(UniversalTagNumber encodingType, string str) - { - if (str == null) - throw new ArgumentNullException(nameof(str)); - - WriteCharacterString(encodingType, str.AsSpan()); - } - - /// - /// Write the provided string using the specified encoding type using the UNIVERSAL - /// tag corresponding to the encoding type. - /// - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// The writer has been Disposed. - /// - public void WriteCharacterString(UniversalTagNumber encodingType, ReadOnlySpan str) - { - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - - WriteCharacterStringCore(new Asn1Tag(encodingType), encoding, str); - } - - /// - /// Write the provided string using the specified encoding type using the specified - /// tag corresponding to the encoding type. - /// - /// The tag to write. - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// is null - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// The writer has been Disposed. - public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, string str) - { - if (str == null) - throw new ArgumentNullException(nameof(str)); - - WriteCharacterString(tag, encodingType, str.AsSpan()); - } - - /// - /// Write the provided string using the specified encoding type using the specified - /// tag corresponding to the encoding type. - /// - /// The tag to write. - /// - /// The corresponding to the encoding to use. - /// - /// The string to write. - /// - /// is not a restricted character string encoding type --OR-- - /// is a restricted character string encoding type that is not - /// currently supported by this method - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// The writer has been Disposed. - public void WriteCharacterString(Asn1Tag tag, UniversalTagNumber encodingType, ReadOnlySpan str) - { - CheckUniversalTag(tag, encodingType); - - Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); - WriteCharacterStringCore(tag, encoding, str); - } - - // T-REC-X.690-201508 sec 8.23 - private void WriteCharacterStringCore(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str) - { - int size = -1; - - // T-REC-X.690-201508 sec 9.2 - if (RuleSet == AsnEncodingRules.CER) - { - // TODO: Split this for netstandard vs netcoreapp for span?. - unsafe - { - fixed (char* strPtr = &MemoryMarshal.GetReference(str)) - { - size = encoding.GetByteCount(strPtr, str.Length); - - // If it exceeds the primitive segment size, use the constructed encoding. - if (size > AsnReader.MaxCERSegmentSize) - { - WriteConstructedCerCharacterString(tag, encoding, str, size); - return; - } - } - } - } - - // TODO: Split this for netstandard vs netcoreapp for span?. - unsafe - { - fixed (char* strPtr = &MemoryMarshal.GetReference(str)) - { - if (size < 0) - { - size = encoding.GetByteCount(strPtr, str.Length); - } - - // Clear the constructed tag, if present. - WriteTag(tag.AsPrimitive()); - WriteLength(size); - Span dest = _buffer.AsSpan(_offset, size); - - fixed (byte* destPtr = &MemoryMarshal.GetReference(dest)) - { - int written = encoding.GetBytes(strPtr, str.Length, destPtr, dest.Length); - - if (written != size) - { - Debug.Fail($"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); - throw new InvalidOperationException(); - } - } - - _offset += size; - } - } - } - - private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str, int size) - { - Debug.Assert(size > AsnReader.MaxCERSegmentSize); - - byte[] tmp; - - unsafe - { - fixed (char* strPtr = &MemoryMarshal.GetReference(str)) - { - tmp = CryptoPool.Rent(size); - - fixed (byte* destPtr = tmp) - { - int written = encoding.GetBytes(strPtr, str.Length, destPtr, tmp.Length); - - if (written != size) - { - Debug.Fail( - $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); - throw new InvalidOperationException(); - } - } - } - } - - WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size)); - CryptoPool.Return(tmp, size); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems index f495ac7..a1b1326 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 26022e2..22e5925 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 7de0284..60e5d30 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 0744b81..857cca0 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 @@ namespace System.Security.Cryptography private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters) { + AsnWriter keyWriter; + bool hasPrivateKey; + if (parameters.X != null) { // DSAPrivateKey ::= SEQUENCE( @@ -214,25 +217,44 @@ namespace System.Security.Cryptography // 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 8452c50..fe9a956 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.Buffers; 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography // 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 @@ namespace System.Security.Cryptography 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,6 +282,9 @@ namespace System.Security.Cryptography case Oids.secp521r1: curveOid = new Oid(Oids.secp521r1, nameof(ECCurve.NamedCurves.nistP521)); break; + default: + curveOid = new Oid(domainParameters.Named); + break; } return ECCurve.CreateFromOid(curveOid); @@ -278,6 +292,18 @@ namespace System.Security.Cryptography 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: // // Versions 1, 2, and 3 are defined. @@ -302,6 +328,7 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography int k2 = -1; int k3 = -1; - switch (innerReader.ReadObjectIdentifierAsString()) + switch (innerReader.ReadObjectIdentifier()) { case Oids.EcChar2TrinomialBasis: // Trinomial ::= INTEGER @@ -486,12 +513,11 @@ namespace System.Security.Cryptography } // 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 301851b..3eba157 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 @@ namespace System.Security.Cryptography 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 b5aab14..1df65b6 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 49e058f..69d9300 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography AsnWriter pkcs8Writer, PbeParameters pbeParameters) { - ReadOnlySpan pkcs8Span = pkcs8Writer.EncodeAsSpan(); - PasswordBasedEncryption.InitiateEncryption( pbeParameters, out SymmetricAlgorithm cipher, @@ -352,7 +397,7 @@ namespace System.Security.Cryptography // 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 @@ namespace System.Security.Cryptography passwordBytes, cipher, isPkcs12, - pkcs8Span, + pkcs8Writer, pbeParameters, salt, encryptedRent, @@ -388,17 +433,13 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 5780e28..f250a06 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 @@ namespace System.Security.Cryptography bool pkcs12 = false; - switch (algorithmIdentifier.Algorithm.Value) + switch (algorithmIdentifier.Algorithm) { case Oids.PbeWithMD5AndDESCBC: digestAlgorithmName = HashAlgorithmName.MD5; @@ -134,7 +135,7 @@ namespace System.Security.Cryptography throw new CryptographicException( SR.Format( SR.Cryptography_UnknownAlgorithmIdentifier, - algorithmIdentifier.Algorithm.Value)); + algorithmIdentifier.Algorithm)); } Debug.Assert(digestAlgorithmName.Name != null); @@ -146,7 +147,7 @@ namespace System.Security.Cryptography { if (password.IsEmpty && passwordBytes.Length > 0) { - throw AlgorithmKdfRequiresChars(algorithmIdentifier.Algorithm.Value); + throw AlgorithmKdfRequiresChars(algorithmIdentifier.Algorithm); } return Pkcs12PbeDecrypt( @@ -294,7 +295,7 @@ namespace System.Security.Cryptography ReadOnlySpan passwordBytes, SymmetricAlgorithm cipher, bool isPkcs12, - ReadOnlySpan source, + AsnWriter source, PbeParameters pbeParameters, ReadOnlySpan salt, byte[] destination, @@ -304,7 +305,8 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography } finally { - CryptoPool.Return(sourceRent, source.Length); + CryptoPool.Return(sourceRent, sourceLength); } } } @@ -501,16 +507,16 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography [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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography if (isPkcs12) { - writer.WriteObjectIdentifier(encryptionAlgorithmOid); + writer.WriteObjectIdentifierForCrypto(encryptionAlgorithmOid); // pkcs-12PbeParams { @@ -1002,7 +1021,7 @@ namespace System.Security.Cryptography } else { - writer.WriteObjectIdentifier(Oids.PasswordBasedEncryptionScheme2); + writer.WriteObjectIdentifierForCrypto(Oids.PasswordBasedEncryptionScheme2); // PBES2-params { @@ -1011,7 +1030,7 @@ namespace System.Security.Cryptography // keyDerivationFunc { writer.PushSequence(); - writer.WriteObjectIdentifier(Oids.Pbkdf2); + writer.WriteObjectIdentifierForCrypto(Oids.Pbkdf2); // PBKDF2-params { @@ -1024,7 +1043,7 @@ namespace System.Security.Cryptography if (hmacOid != Oids.HmacWithSha1) { writer.PushSequence(); - writer.WriteObjectIdentifier(hmacOid); + writer.WriteObjectIdentifierForCrypto(hmacOid); writer.WriteNull(); writer.PopSequence(); } @@ -1038,7 +1057,7 @@ namespace System.Security.Cryptography // 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 926cf94..0319e2c 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 @@ namespace System.Security.Cryptography 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 fbe1383..4d062ed 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 @@ namespace System.Security.Cryptography { 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 a21db3d..eb26d14 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography private static SafeSecKeyRefHandle ImportKey(RSAParameters parameters) { + AsnWriter keyWriter; + bool hasPrivateKey; + if (parameters.D != null) { - using (AsnWriter pkcs1PrivateKey = RSAKeyFormatHelper.WritePkcs1PrivateKey(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(pkcs1PrivateKey.EncodeAsSpan(), true); - } + keyWriter = RSAKeyFormatHelper.WritePkcs1PrivateKey(parameters); + hasPrivateKey = true; } else { - using (AsnWriter pkcs1PublicKey = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters)) - { - return Interop.AppleCrypto.ImportEphemeralKey(pkcs1PublicKey.EncodeAsSpan(), false); - } + keyWriter = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); + hasPrivateKey = false; + } + + byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); + + if (!keyWriter.TryEncode(rented, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + // Explicitly clear the inner buffer + keyWriter.Reset(); + + try + { + return Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey); + } + finally + { + CryptoPool.Return(rented, written); } } diff --git a/src/libraries/System.Formats.Asn1/Directory.Build.props b/src/libraries/System.Formats.Asn1/Directory.Build.props new file mode 100644 index 0000000..749d7fc --- /dev/null +++ b/src/libraries/System.Formats.Asn1/Directory.Build.props @@ -0,0 +1,7 @@ + + + + Open + true + + diff --git a/src/libraries/System.Formats.Asn1/System.Formats.Asn1.sln b/src/libraries/System.Formats.Asn1/System.Formats.Asn1.sln new file mode 100644 index 0000000..dfd2a9e --- /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 0000000..5dbc975 --- /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 0000000..921b832 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.cs @@ -0,0 +1,243 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Formats.Asn1 +{ + public readonly partial struct Asn1Tag : System.IEquatable + { + private readonly int _dummyPrimitive; + public static readonly System.Formats.Asn1.Asn1Tag Boolean; + public static readonly System.Formats.Asn1.Asn1Tag ConstructedBitString; + public static readonly System.Formats.Asn1.Asn1Tag ConstructedOctetString; + public static readonly System.Formats.Asn1.Asn1Tag Enumerated; + public static readonly System.Formats.Asn1.Asn1Tag GeneralizedTime; + public static readonly System.Formats.Asn1.Asn1Tag Integer; + public static readonly System.Formats.Asn1.Asn1Tag Null; + public static readonly System.Formats.Asn1.Asn1Tag ObjectIdentifier; + public static readonly System.Formats.Asn1.Asn1Tag PrimitiveBitString; + public static readonly System.Formats.Asn1.Asn1Tag PrimitiveOctetString; + public static readonly System.Formats.Asn1.Asn1Tag Sequence; + public static readonly System.Formats.Asn1.Asn1Tag SetOf; + public static readonly System.Formats.Asn1.Asn1Tag UtcTime; + public Asn1Tag(System.Formats.Asn1.TagClass tagClass, int tagValue, bool isConstructed = false) { throw null; } + public Asn1Tag(System.Formats.Asn1.UniversalTagNumber universalTagNumber, bool isConstructed = false) { throw null; } + public bool IsConstructed { get { throw null; } } + public System.Formats.Asn1.TagClass TagClass { get { throw null; } } + public int TagValue { get { throw null; } } + public System.Formats.Asn1.Asn1Tag AsConstructed() { throw null; } + public System.Formats.Asn1.Asn1Tag AsPrimitive() { throw null; } + public int CalculateEncodedSize() { throw null; } + public static System.Formats.Asn1.Asn1Tag Decode(System.ReadOnlySpan source, out int bytesConsumed) { throw null; } + public int Encode(System.Span destination) { throw null; } + public bool Equals(System.Formats.Asn1.Asn1Tag other) { throw null; } + public override bool Equals(object? obj) { throw null; } + public override int GetHashCode() { throw null; } + public bool HasSameClassAndValue(System.Formats.Asn1.Asn1Tag other) { throw null; } + public static bool operator ==(System.Formats.Asn1.Asn1Tag left, System.Formats.Asn1.Asn1Tag right) { throw null; } + public static bool operator !=(System.Formats.Asn1.Asn1Tag left, System.Formats.Asn1.Asn1Tag right) { throw null; } + public override string ToString() { throw null; } + public static bool TryDecode(System.ReadOnlySpan source, out System.Formats.Asn1.Asn1Tag tag, out int bytesConsumed) { throw null; } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + } + public partial class AsnContentException : System.Exception + { + public AsnContentException() { } + protected AsnContentException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public AsnContentException(string? message) { } + public AsnContentException(string? message, System.Exception? inner) { } + } + public static partial class AsnDecoder + { + public static byte[] ReadBitString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int unusedBitCount, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool ReadBoolean(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static string ReadCharacterString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.UniversalTagNumber encodingType, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Formats.Asn1.Asn1Tag ReadEncodedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed) { throw null; } + public static System.ReadOnlySpan ReadEnumeratedBytes(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Enum ReadEnumeratedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Type enumType, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static TEnum ReadEnumeratedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { throw null; } + public static System.DateTimeOffset ReadGeneralizedTime(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Numerics.BigInteger ReadInteger(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.ReadOnlySpan ReadIntegerBytes(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Collections.BitArray ReadNamedBitList(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.Enum ReadNamedBitListValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Type flagsEnumType, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static TFlagsEnum ReadNamedBitListValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TFlagsEnum : System.Enum { throw null; } + public static void ReadNull(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static string ReadObjectIdentifier(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static byte[] ReadOctetString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static void ReadSequence(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static void ReadSetOf(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int contentOffset, out int contentLength, out int bytesConsumed, bool skipSortOrderValidation = false, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static System.DateTimeOffset ReadUtcTime(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, int twoDigitYearMax = 2049, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadBitString(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, out int unusedBitCount, out int bytesConsumed, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadCharacterString(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.UniversalTagNumber encodingType, out int bytesConsumed, out int charsWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadCharacterStringBytes(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.Asn1Tag expectedTag, out int bytesConsumed, out int bytesWritten) { throw null; } + public static bool TryReadEncodedValue(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out System.Formats.Asn1.Asn1Tag tag, out int contentOffset, out int contentLength, out int bytesConsumed) { throw null; } + public static bool TryReadInt32(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadInt64(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out long value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadOctetString(System.ReadOnlySpan source, System.Span destination, System.Formats.Asn1.AsnEncodingRules ruleSet, out int bytesConsumed, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadPrimitiveBitString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out int unusedBitCount, out System.ReadOnlySpan value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public static bool TryReadPrimitiveCharacterStringBytes(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.Asn1Tag expectedTag, out System.ReadOnlySpan value, out int bytesConsumed) { throw null; } + public static bool TryReadPrimitiveOctetString(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out System.ReadOnlySpan value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt32(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out uint value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public static bool TryReadUInt64(System.ReadOnlySpan source, System.Formats.Asn1.AsnEncodingRules ruleSet, out ulong value, out int bytesConsumed, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + } + public enum AsnEncodingRules + { + BER = 0, + CER = 1, + DER = 2, + } + public partial class AsnReader + { + public AsnReader(System.ReadOnlyMemory data, System.Formats.Asn1.AsnEncodingRules ruleSet, System.Formats.Asn1.AsnReaderOptions options = default(System.Formats.Asn1.AsnReaderOptions)) { } + public bool HasData { get { throw null; } } + public System.Formats.Asn1.AsnEncodingRules RuleSet { get { throw null; } } + public System.ReadOnlyMemory PeekContentBytes() { throw null; } + public System.ReadOnlyMemory PeekEncodedValue() { throw null; } + public System.Formats.Asn1.Asn1Tag PeekTag() { throw null; } + public byte[] ReadBitString(out int unusedBitCount, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool ReadBoolean(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public string ReadCharacterString(System.Formats.Asn1.UniversalTagNumber encodingType, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.ReadOnlyMemory ReadEncodedValue() { throw null; } + public System.ReadOnlyMemory ReadEnumeratedBytes(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Enum ReadEnumeratedValue(System.Type enumType, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public TEnum ReadEnumeratedValue(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { throw null; } + public System.DateTimeOffset ReadGeneralizedTime(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Numerics.BigInteger ReadInteger(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.ReadOnlyMemory ReadIntegerBytes(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Collections.BitArray ReadNamedBitList(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Enum ReadNamedBitListValue(System.Type flagsEnumType, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public TFlagsEnum ReadNamedBitListValue(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) where TFlagsEnum : System.Enum { throw null; } + public void ReadNull(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { } + public string ReadObjectIdentifier(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public byte[] ReadOctetString(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnReader ReadSequence(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnReader ReadSetOf(bool skipSortOrderValidation, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnReader ReadSetOf(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.DateTimeOffset ReadUtcTime(int twoDigitYearMax, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.DateTimeOffset ReadUtcTime(System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public void ThrowIfNotEmpty() { } + public bool TryReadBitString(System.Span destination, out int unusedBitCount, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadCharacterString(System.Span destination, System.Formats.Asn1.UniversalTagNumber encodingType, out int charsWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadCharacterStringBytes(System.Span destination, System.Formats.Asn1.Asn1Tag expectedTag, out int bytesWritten) { throw null; } + public bool TryReadInt32(out int value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadInt64(out long value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadOctetString(System.Span destination, out int bytesWritten, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadPrimitiveBitString(out int unusedBitCount, out System.ReadOnlyMemory value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public bool TryReadPrimitiveCharacterStringBytes(System.Formats.Asn1.Asn1Tag expectedTag, out System.ReadOnlyMemory contents) { throw null; } + public bool TryReadPrimitiveOctetString(out System.ReadOnlyMemory contents, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool TryReadUInt32(out uint value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + [System.CLSCompliantAttribute(false)] + public bool TryReadUInt64(out ulong value, System.Formats.Asn1.Asn1Tag? expectedTag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + } + public partial struct AsnReaderOptions + { + private int _dummyPrimitive; + public bool SkipSetSortOrderVerification { readonly get { throw null; } set { } } + public int UtcTimeTwoDigitYearMax { get { throw null; } set { } } + } + public sealed partial class AsnWriter + { + public AsnWriter(System.Formats.Asn1.AsnEncodingRules ruleSet) { } + public System.Formats.Asn1.AsnEncodingRules RuleSet { get { throw null; } } + public void CopyTo(System.Formats.Asn1.AsnWriter destination) { } + public byte[] Encode() { throw null; } + public bool EncodedValueEquals(System.Formats.Asn1.AsnWriter other) { throw null; } + public bool EncodedValueEquals(System.ReadOnlySpan other) { throw null; } + public int GetEncodedLength() { throw null; } + public void PopOctetString(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void PopSequence(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void PopSetOf(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public System.Formats.Asn1.AsnWriter.Scope PushOctetString(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnWriter.Scope PushSequence(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public System.Formats.Asn1.AsnWriter.Scope PushSetOf(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { throw null; } + public void Reset() { } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + public void WriteBitString(System.ReadOnlySpan value, int unusedBitCount = 0, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteBoolean(bool value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteCharacterString(System.Formats.Asn1.UniversalTagNumber encodingType, System.ReadOnlySpan str, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteCharacterString(System.Formats.Asn1.UniversalTagNumber encodingType, string value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteEncodedValue(System.ReadOnlySpan value) { } + public void WriteEnumeratedValue(System.Enum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteEnumeratedValue(TEnum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { } + public void WriteGeneralizedTime(System.DateTimeOffset value, bool omitFractionalSeconds = false, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteInteger(long value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteInteger(System.Numerics.BigInteger value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteInteger(System.ReadOnlySpan value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + [System.CLSCompliantAttribute(false)] + public void WriteInteger(ulong value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteIntegerUnsigned(System.ReadOnlySpan value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteNamedBitList(System.Collections.BitArray value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteNamedBitList(System.Enum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteNamedBitList(TEnum value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) where TEnum : System.Enum { } + public void WriteNull(System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteObjectIdentifier(System.ReadOnlySpan oidValue, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteObjectIdentifier(string oidValue, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteOctetString(System.ReadOnlySpan value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteUtcTime(System.DateTimeOffset value, int twoDigitYearMax, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public void WriteUtcTime(System.DateTimeOffset value, System.Formats.Asn1.Asn1Tag? tag = default(System.Formats.Asn1.Asn1Tag?)) { } + public readonly partial struct Scope : System.IDisposable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public void Dispose() { } + } + } + public enum TagClass + { + Universal = 0, + Application = 64, + ContextSpecific = 128, + Private = 192, + } + public enum UniversalTagNumber + { + EndOfContents = 0, + Boolean = 1, + Integer = 2, + BitString = 3, + OctetString = 4, + Null = 5, + ObjectIdentifier = 6, + ObjectDescriptor = 7, + External = 8, + InstanceOf = 8, + Real = 9, + Enumerated = 10, + Embedded = 11, + UTF8String = 12, + RelativeObjectIdentifier = 13, + Time = 14, + Sequence = 16, + SequenceOf = 16, + Set = 17, + SetOf = 17, + NumericString = 18, + PrintableString = 19, + T61String = 20, + TeletexString = 20, + VideotexString = 21, + IA5String = 22, + UtcTime = 23, + GeneralizedTime = 24, + GraphicString = 25, + ISO646String = 26, + VisibleString = 26, + GeneralString = 27, + UniversalString = 28, + UnrestrictedCharacterString = 29, + BMPString = 30, + Date = 31, + TimeOfDay = 32, + DateTime = 33, + Duration = 34, + ObjectIdentifierIRI = 35, + RelativeObjectIdentifierIRI = 36, + } +} diff --git a/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj new file mode 100644 index 0000000..7715f69 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/ref/System.Formats.Asn1.csproj @@ -0,0 +1,12 @@ + + + netstandard2.0 + enable + + + + + + + + diff --git a/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx new file mode 100644 index 0000000..18ac1f4 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The destination is too small to hold the encoded value. + + + ASN.1 Enumerated values only apply to enum types without the [Flags] attribute. + + + Enumerations with a backing type of '{0}' are not supported for ReadEnumeratedValue. + + + The OID value is invalid. + + + Named bit list operations require an enum with the [Flags] attribute. + + + The destination buffer overlaps the source buffer. + + + The specified tag has the Universal TagClass, but the TagValue does not correspond with a known character string type. + + + An integer value cannot be empty. + + + The first 9 bits of the integer value all have the same value. Ensure the input is in big-endian byte order and that all redundant leading bytes have been removed. + + + Tags with TagClass Universal must have the appropriate TagValue value for the data type being read or written. + + + Unused bit count must be 0 when the bit string is empty. + + + Unused bit count must be between 0 and 7, inclusive. + + + One or more of the bits covered by the provided unusedBitCount value is set. All unused bits must be cleared. + + + The input to WriteEncodedValue must represent a single encoded value with no trailing data. + + + Encode cannot be called while a Sequence, Set-Of, or Octet String is still open. + + + Cannot pop the requested tag as it is not currently in progress. + + + A constructed tag used a definite length encoding, which is invalid for CER data. The input may be encoded with BER or DER. + + + The encoded value uses a primitive encoding, which is invalid for '{0}' values. + + + The ASN.1 value is invalid. + + + The encoded enumerated value is larger than the value size of the '{0}' enum. + + + The encoded value is not valid under the selected encoding, but it may be valid under the BER or DER encoding. + + + The encoded value is not valid under the selected encoding, but it may be valid under the BER encoding. + + + The encoded value is not valid under the selected encoding, but it may be valid under the BER or CER encoding. + + + The provided data does not represent a valid tag. + + + The encoded length exceeds the number of bytes remaining in the input buffer. + + + The encoded length is not valid under the requested encoding rules, the value may be valid under the BER encoding. + + + The encoded length exceeds the maximum supported by this library (Int32.MaxValue). + + + The encoded named bit list value is larger than the value size of the '{0}' enum. + + + The encoded value uses a constructed encoding, which is invalid for '{0}' values. + + + The encoded set is not sorted as required by the current encoding rules. The value may be valid under the BER encoding, or you can ignore the sort validation by specifying skipSortValidation=true. + + + The last expected value has been read, but the reader still has pending data. This value may be from a newer schema, or is corrupt. + + + The provided data is tagged with '{0}' class value '{1}', but it should have been '{2}' class value '{3}'. + + diff --git a/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj new file mode 100644 index 0000000..ddd7a52 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj @@ -0,0 +1,53 @@ + + + true + enable + netstandard2.0 + + + + Common\System\Security\Cryptography\CryptoPool.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.Accelerators.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs similarity index 94% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.Accelerators.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs index f34f6d9..82c6305 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.Accelerators.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.Accelerators.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal partial struct Asn1Tag + public partial struct Asn1Tag { /// /// Represents the End-of-Contents meta-tag. /// - public static readonly Asn1Tag EndOfContents = new Asn1Tag(0, (int)UniversalTagNumber.EndOfContents); + internal static readonly Asn1Tag EndOfContents = new Asn1Tag(0, (int)UniversalTagNumber.EndOfContents); /// /// Represents the universal class tag for a Boolean value. diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs similarity index 72% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs index daefd32..3ecf79d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/Asn1Tag.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/Asn1Tag.cs @@ -4,14 +4,13 @@ using System.Diagnostics; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// This type represents an ASN.1 tag, as described in ITU-T Recommendation X.680. /// // T-REC-X.690-201508 sec 8.1.2 - internal partial struct Asn1Tag : IEquatable + public readonly partial struct Asn1Tag : IEquatable { private const byte ClassMask = 0b1100_0000; private const byte ConstructedMask = 0b0010_0000; @@ -26,8 +25,8 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 /// The number of bytes written to . /// /// - /// + /// /// . < . /// public int Encode(Span destination) @@ -365,25 +366,7 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// /// Tag to test for concept equality. /// - /// true if has the same and - /// as this tag, false otherwise. + /// if has the same and + /// as this tag, otherwise. /// public bool HasSameClassAndValue(Asn1Tag other) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnCharacterStringEncodings.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs similarity index 82% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnCharacterStringEncodings.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs index c8d2487..a9d24cf 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnCharacterStringEncodings.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnCharacterStringEncodings.cs @@ -4,21 +4,24 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { internal static class AsnCharacterStringEncodings { - private static readonly Text.Encoding s_utf8Encoding = new UTF8Encoding(false, throwOnInvalidBytes: true); - private static readonly Text.Encoding s_bmpEncoding = new BMPEncoding(); - private static readonly Text.Encoding s_ia5Encoding = new IA5Encoding(); - private static readonly Text.Encoding s_visibleStringEncoding = new VisibleStringEncoding(); - private static readonly Text.Encoding s_numericStringEncoding = new NumericStringEncoding(); - private static readonly Text.Encoding s_printableStringEncoding = new PrintableStringEncoding(); - private static readonly Text.Encoding s_t61Encoding = new T61Encoding(); - - internal static Text.Encoding GetEncoding(UniversalTagNumber encodingType) => + private static readonly Encoding s_utf8Encoding = + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + + private static readonly Encoding s_bmpEncoding = new BMPEncoding(); + private static readonly Encoding s_ia5Encoding = new IA5Encoding(); + private static readonly Encoding s_visibleStringEncoding = new VisibleStringEncoding(); + private static readonly Encoding s_numericStringEncoding = new NumericStringEncoding(); + private static readonly Encoding s_printableStringEncoding = new PrintableStringEncoding(); + private static readonly Encoding s_t61Encoding = new T61Encoding(); + + internal static Encoding GetEncoding(UniversalTagNumber encodingType) => encodingType switch { UniversalTagNumber.UTF8String => s_utf8Encoding, @@ -30,9 +33,50 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Asn1 } } - internal class PrintableStringEncoding : RestrictedAsciiStringEncoding + internal sealed class PrintableStringEncoding : RestrictedAsciiStringEncoding { // T-REC-X.680-201508 sec 41.4 internal PrintableStringEncoding() @@ -228,7 +272,7 @@ namespace System.Security.Cryptography.Asn1 EncoderFallback.CreateFallbackBuffer().Fallback(c, i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } if (write) @@ -258,7 +302,7 @@ namespace System.Security.Cryptography.Asn1 i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } if (write) @@ -277,7 +321,7 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 EncoderFallback.CreateFallbackBuffer().Fallback(c, i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } ushort val16 = c; @@ -328,7 +372,7 @@ namespace System.Security.Cryptography.Asn1 bytes.Length - 1); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } int writeIdx = 0; @@ -345,7 +389,7 @@ namespace System.Security.Cryptography.Asn1 i); Debug.Fail("Fallback should have thrown"); - throw new CryptographicException(); + throw new InvalidOperationException(); } if (write) @@ -377,10 +421,10 @@ namespace System.Security.Cryptography.Asn1 /// 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 0000000..3f7fa48 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnContentException.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; + +namespace System.Formats.Asn1 +{ + [Serializable] + public class AsnContentException : Exception + { + public AsnContentException() + : base(SR.ContentException_DefaultMessage) + { + } + + public AsnContentException(string? message) + : base(message ?? SR.ContentException_DefaultMessage) + { + } + + public AsnContentException(string? message, Exception? inner) + : base(message ?? SR.ContentException_DefaultMessage, inner) + { + } + + protected AsnContentException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs new file mode 100644 index 0000000..f87776f --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.BitString.cs @@ -0,0 +1,854 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Attempts to get a Bit String value from with a specified tag under + /// the specified encoding rules, if the value is contained in a single (primitive) encoding. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// This parameter is treated as uninitialized. + /// + /// + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Bit String. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// if the Bit String value has a primitive encoding and all of the bits + /// reported as unused are set to 0; + /// otherwise, . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public static bool TryReadPrimitiveBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out ReadOnlySpan value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + contentsLength: out _, + headerLength: out _, + out int localUbc, + out ReadOnlySpan localValue, + out int consumed, + out byte normalizedLastByte)) + { + // Check that this isn't a BER reader which encountered a situation where + // an "unused" bit was not set to 0. + if (localValue.Length == 0 || normalizedLastByte == localValue[localValue.Length - 1]) + { + unusedBitCount = localUbc; + value = localValue; + bytesConsumed = consumed; + return true; + } + } + + unusedBitCount = 0; + value = default; + bytesConsumed = 0; + return false; + } + + /// + /// Attempts to copy a Bit String value from with a specified tag under + /// the specified encoding rules into . + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes written to . + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// if is large enough to receive the + /// value of the Bit String; + /// otherwise, . + /// + /// + /// The least significant bits in the last byte which are reported as "unused" by the + /// value will be copied into + /// as unset bits, irrespective of their value in the encoded representation. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// overlaps . + /// + /// + /// + public static bool TryReadBitString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out int bytesConsumed, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } + + int localUbc; + byte normalizedLastByte; + int consumed; + int? contentsLength; + int headerLength; + + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + out contentsLength, + out headerLength, + out localUbc, + out ReadOnlySpan value, + out consumed, + out normalizedLastByte)) + { + if (value.Length > destination.Length) + { + bytesConsumed = 0; + bytesWritten = 0; + unusedBitCount = 0; + return false; + } + + CopyBitStringValue(value, normalizedLastByte, destination); + + bytesWritten = value.Length; + bytesConsumed = consumed; + unusedBitCount = localUbc; + return true; + } + + // If we get here, the tag was appropriate, but the encoding was constructed. + + if (TryCopyConstructedBitStringValue( + Slice(source, headerLength, contentsLength), + ruleSet, + destination, + contentsLength == null, + out localUbc, + out int bytesRead, + out int written)) + { + unusedBitCount = localUbc; + bytesConsumed = headerLength + bytesRead; + bytesWritten = written; + return true; + } + + bytesWritten = bytesConsumed = unusedBitCount = 0; + return false; + } + + /// + /// Reads a Bit String value from with a specified tag under + /// the specified encoding rules, returning the contents in a new array. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// An array containing the contents of the Bit String value. + /// + /// + /// The least significant bits in the last byte which are reported as "unused" by the + /// value will be copied into the return value + /// as unset bits, irrespective of their value in the encoded representation. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public static byte[] ReadBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadPrimitiveBitStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveBitString, + out int? contentsLength, + out int headerLength, + out int localUbc, + out ReadOnlySpan localValue, + out int consumed, + out byte normalizedLastByte)) + { + byte[] ret = localValue.ToArray(); + + // Update the last byte in case it's a non-canonical byte in a BER encoding. + if (localValue.Length > 0) + { + ret[ret.Length - 1] = normalizedLastByte; + } + + unusedBitCount = localUbc; + bytesConsumed = consumed; + return ret; + } + + // If we get here, the tag was appropriate, but the encoding was constructed. + + // Guaranteed long enough + int tooBig = contentsLength ?? SeekEndOfContents(source.Slice(headerLength), ruleSet); + + byte[] rented = CryptoPool.Rent(tooBig); + + if (TryCopyConstructedBitStringValue( + Slice(source, headerLength, contentsLength), + ruleSet, + rented, + contentsLength == null, + out localUbc, + out int bytesRead, + out int written)) + { + byte[] ret = rented.AsSpan(0, written).ToArray(); + CryptoPool.Return(rented, written); + unusedBitCount = localUbc; + bytesConsumed = headerLength + bytesRead; + return ret; + } + + Debug.Fail("TryCopyConstructedBitStringValue failed with a pre-allocated buffer"); + throw new AsnContentException(); + } + + private static void ParsePrimitiveBitStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int unusedBitCount, + out ReadOnlySpan value, + out byte normalizedLastByte) + { + // T-REC-X.690-201508 sec 9.2 + if (ruleSet == AsnEncodingRules.CER && source.Length > MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); + } + + // T-REC-X.690-201508 sec 8.6.2.3 + if (source.Length == 0) + { + throw new AsnContentException(); + } + + unusedBitCount = source[0]; + + // T-REC-X.690-201508 sec 8.6.2.2 + if (unusedBitCount > 7) + { + throw new AsnContentException(); + } + + if (source.Length == 1) + { + // T-REC-X.690-201508 sec 8.6.2.4 + if (unusedBitCount > 0) + { + throw new AsnContentException(); + } + + Debug.Assert(unusedBitCount == 0); + value = ReadOnlySpan.Empty; + normalizedLastByte = 0; + return; + } + + // Build a mask for the bits that are used so the normalized value can be computed + // + // If 3 bits are "unused" then build a mask for them to check for 0. + // -1 << 3 => 0b1111_1111 << 3 => 0b1111_1000 + int mask = -1 << unusedBitCount; + byte lastByte = source[source.Length - 1]; + byte maskedByte = (byte)(lastByte & mask); + + if (maskedByte != lastByte) + { + // T-REC-X.690-201508 sec 11.2.1 + if (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + } + + normalizedLastByte = maskedByte; + value = source.Slice(1); + } + + private delegate void BitStringCopyAction( + ReadOnlySpan value, + byte normalizedLastByte, + Span destination); + + private static void CopyBitStringValue( + ReadOnlySpan value, + byte normalizedLastByte, + Span destination) + { + if (value.Length == 0) + { + return; + } + + value.CopyTo(destination); + // Replace the last byte with the normalized answer. + destination[value.Length - 1] = normalizedLastByte; + } + + private static int CountConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + bool isIndefinite) + { + Span destination = Span.Empty; + + return ProcessConstructedBitString( + source, + ruleSet, + destination, + null, + isIndefinite, + out _, + out _); + } + + private static void CopyConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + bool isIndefinite, + out int unusedBitCount, + out int bytesRead, + out int bytesWritten) + { + Span tmpDest = destination; + + bytesWritten = ProcessConstructedBitString( + source, + ruleSet, + tmpDest, + (value, lastByte, dest) => CopyBitStringValue(value, lastByte, dest), + isIndefinite, + out unusedBitCount, + out bytesRead); + } + + private static int ProcessConstructedBitString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + BitStringCopyAction? copyAction, + bool isIndefinite, + out int lastUnusedBitCount, + out int bytesRead) + { + lastUnusedBitCount = 0; + bytesRead = 0; + int lastSegmentLength = MaxCERSegmentSize; + + ReadOnlySpan cur = source; + Stack<(int Offset, int Length, bool Indefinite, int BytesRead)>? readerStack = null; + int totalLength = 0; + Asn1Tag tag = Asn1Tag.ConstructedBitString; + Span curDest = destination; + + while (true) + { + while (!cur.IsEmpty) + { + tag = ReadTagAndLength(cur, ruleSet, out int? length, out int headerLength); + + if (tag == Asn1Tag.PrimitiveBitString) + { + if (lastUnusedBitCount != 0) + { + // T-REC-X.690-201508 sec 8.6.4, only the last segment may have + // a number of bits not a multiple of 8. + throw new AsnContentException(); + } + + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); + } + + Debug.Assert(length != null); + ReadOnlySpan encodedValue = Slice(cur, headerLength, length.Value); + + ParsePrimitiveBitStringContents( + encodedValue, + ruleSet, + out lastUnusedBitCount, + out ReadOnlySpan contents, + out byte normalizedLastByte); + + int localLen = headerLength + encodedValue.Length; + cur = cur.Slice(localLen); + + bytesRead += localLen; + totalLength += contents.Length; + lastSegmentLength = encodedValue.Length; + + if (copyAction != null) + { + copyAction(contents, normalizedLastByte, curDest); + curDest = curDest.Slice(contents.Length); + } + } + else if (tag == Asn1Tag.EndOfContents && isIndefinite) + { + ValidateEndOfContents(tag, length, headerLength); + + bytesRead += headerLength; + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + cur = topSpan.Slice(bytesRead); + + bytesRead += pushedBytesRead; + isIndefinite = wasIndefinite; + } + else + { + // We have matched the EndOfContents that brought us here. + break; + } + } + else if (tag == Asn1Tag.ConstructedBitString) + { + if (ruleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (readerStack == null) + { + readerStack = new Stack<(int, int, bool, int)>(); + } + + if (!source.Overlaps(cur, out int curOffset)) + { + Debug.Fail("Non-overlapping data encountered..."); + throw new AsnContentException(); + } + + readerStack.Push((curOffset, cur.Length, isIndefinite, bytesRead)); + + cur = Slice(cur, headerLength, length); + bytesRead = headerLength; + isIndefinite = (length == null); + } + else + { + // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) + throw new AsnContentException(); + } + } + + if (isIndefinite && tag != Asn1Tag.EndOfContents) + { + throw new AsnContentException(); + } + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + + ReadOnlySpan tmpSpan = source.Slice(topOffset, topLength); + cur = tmpSpan.Slice(bytesRead); + + isIndefinite = wasIndefinite; + bytesRead += pushedBytesRead; + } + else + { + return totalLength; + } + } + } + + private static bool TryCopyConstructedBitStringValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span dest, + bool isIndefinite, + out int unusedBitCount, + out int bytesRead, + out int bytesWritten) + { + // Call CountConstructedBitString to get the required byte and to verify that the + // data is well-formed before copying into dest. + int contentLength = CountConstructedBitString(source, ruleSet, isIndefinite); + + // Since the unused bits byte from the segments don't count, only one segment + // returns 999 (or less), the second segment bumps the count to 1000, and is legal. + // + // T-REC-X.690-201508 sec 9.2 + if (ruleSet == AsnEncodingRules.CER && contentLength < MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (dest.Length < contentLength) + { + unusedBitCount = 0; + bytesRead = 0; + bytesWritten = 0; + return false; + } + + CopyConstructedBitString( + source, + ruleSet, + dest, + isIndefinite, + out unusedBitCount, + out bytesRead, + out bytesWritten); + + Debug.Assert(bytesWritten == contentLength); + return true; + } + + private static bool TryReadPrimitiveBitStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + out int? contentsLength, + out int headerLength, + out int unusedBitCount, + out ReadOnlySpan value, + out int bytesConsumed, + out byte normalizedLastByte) + { + Asn1Tag actualTag = + ReadTagAndLength(source, ruleSet, out contentsLength, out headerLength); + + CheckExpectedTag(actualTag, expectedTag, UniversalTagNumber.BitString); + + // Ensure the length made sense. + ReadOnlySpan encodedValue = Slice(source, headerLength, contentsLength); + + if (actualTag.IsConstructed) + { + if (ruleSet == AsnEncodingRules.DER) + { + throw new AsnContentException(SR.ContentException_InvalidUnderDer_TryBerOrCer); + } + + unusedBitCount = 0; + value = default; + normalizedLastByte = 0; + bytesConsumed = 0; + return false; + } + + Debug.Assert(contentsLength.HasValue); + + ParsePrimitiveBitStringContents( + encodedValue, + ruleSet, + out unusedBitCount, + out value, + out normalizedLastByte); + + bytesConsumed = headerLength + encodedValue.Length; + return true; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a BIT STRING with a specified tag, returning the contents + /// as a over the original data. + /// + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// + /// + /// On success, receives a over the original data + /// corresponding to the value of the BIT STRING. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// and advances the reader if the BIT STRING value had a primitive encoding, + /// and does not advance the reader if it had a constructed encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public bool TryReadPrimitiveBitString( + out int unusedBitCount, + out ReadOnlyMemory value, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadPrimitiveBitString( + _data.Span, + RuleSet, + out unusedBitCount, + out ReadOnlySpan span, + out int consumed, + expectedTag); + + if (ret) + { + value = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + value = default; + } + + return ret; + } + + /// + /// Reads the next value as a BIT STRING with a specified tag, copying the value + /// into a provided destination buffer. + /// + /// The buffer in which to write. + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// + /// + /// On success, receives the number of bytes written to . + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public bool TryReadBitString( + Span destination, + out int unusedBitCount, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadBitString( + _data.Span, + destination, + RuleSet, + out unusedBitCount, + out int consumed, + out bytesWritten, + expectedTag); + + if (ret) + { + _data = _data.Slice(consumed); + } + + return ret; + } + + /// + /// Reads the next value as a BIT STRING with a specified tag, returning the value + /// in a byte array. + /// + /// + /// On success, receives the number of bits in the last byte which were reported as + /// "unused" by the writer. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// A copy of the value in a newly allocated, precisely sized, array. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = null) + { + byte[] ret = AsnDecoder.ReadBitString( + _data.Span, + RuleSet, + out unusedBitCount, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs new file mode 100644 index 0000000..dc2b113 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Boolean.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Boolean value from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// The decoded value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static bool ReadBoolean( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + // T-REC-X.690-201508 sec 8.2.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag ?? Asn1Tag.Boolean, + UniversalTagNumber.Boolean, + out int consumed); + + // T-REC-X.690-201508 sec 8.2.1 + if (contents.Length != 1) + { + throw new AsnContentException(); + } + + byte val = contents[0]; + + // T-REC-X.690-201508 sec 8.2.2 + if (val == 0) + { + bytesConsumed = consumed; + return false; + } + + // T-REC-X.690-201508 sec 11.1 + if (val != 0xFF && (ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER)) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + bytesConsumed = consumed; + return true; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a Boolean with a specified tag. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 1). + /// + /// + /// The decoded value. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public bool ReadBoolean(Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.ReadBoolean(_data.Span, RuleSet, out int bytesConsumed, expectedTag); + _data = _data.Slice(bytesConsumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs new file mode 100644 index 0000000..605d44b --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Enumerated.cs @@ -0,0 +1,412 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads an Enumerated value from with a specified tag under + /// the specified encoding rules, returning the contents as a slice of the buffer. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The slice of the buffer containing the bytes of the Enumerated value, + /// in signed big-endian form. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static ReadOnlySpan ReadEnumeratedBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return GetIntegerContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.Enumerated, + UniversalTagNumber.Enumerated, + out bytesConsumed); + } + + /// + /// Reads an Enumerated from with a specified tag under + /// the specified encoding rules, converting it to the + /// non-[] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// Destination enum type + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static TEnum ReadEnumeratedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + where TEnum : Enum + { + Type tEnum = typeof(TEnum); + + return (TEnum)Enum.ToObject( + tEnum, + ReadEnumeratedValue( + source, + ruleSet, + tEnum, + out bytesConsumed, + expectedTag)); + } + + /// + /// Reads an Enumerated from with a specified tag under + /// the specified encoding rules, converting it to the + /// non-[] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// Type object representing the destination type. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is . + /// + public static Enum ReadEnumeratedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Type enumType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (enumType == null) + throw new ArgumentNullException(nameof(enumType)); + + const UniversalTagNumber TagNumber = UniversalTagNumber.Enumerated; + Asn1Tag localTag = expectedTag ?? Asn1Tag.Enumerated; + + // This will throw an ArgumentException if TEnum isn't an enum type, + // so we don't need to validate it. + Type backingType = enumType.GetEnumUnderlyingType(); + + if (enumType.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_EnumeratedValueRequiresNonFlagsEnum, + nameof(enumType)); + } + + // T-REC-X.690-201508 sec 8.4 says the contents are the same as for integers. + int sizeLimit = Marshal.SizeOf(backingType); + + if (backingType == typeof(int) || + backingType == typeof(long) || + backingType == typeof(short) || + backingType == typeof(sbyte)) + { + if (!TryReadSignedInteger( + source, + ruleSet, + sizeLimit, + localTag, + TagNumber, + out long value, + out int consumed)) + { + throw new AsnContentException(SR.ContentException_EnumeratedValueTooBig); + } + + bytesConsumed = consumed; + return (Enum)Enum.ToObject(enumType, value); + } + + if (backingType == typeof(uint) || + backingType == typeof(ulong) || + backingType == typeof(ushort) || + backingType == typeof(byte)) + { + if (!TryReadUnsignedInteger( + source, + ruleSet, + sizeLimit, + localTag, + TagNumber, + out ulong value, + out int consumed)) + { + throw new AsnContentException(SR.ContentException_EnumeratedValueTooBig); + } + + bytesConsumed = consumed; + return (Enum)Enum.ToObject(enumType, value); + } + + throw new AsnContentException( + SR.Format( + SR.Argument_EnumeratedValueBackingTypeNotSupported, + backingType.FullName)); + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a Enumerated with a specified tag, returning the contents + /// as a over the original data. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The bytes of the Enumerated value, in signed big-endian form. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public ReadOnlyMemory ReadEnumeratedBytes(Asn1Tag? expectedTag = null) + { + ReadOnlySpan bytes = + AsnDecoder.ReadEnumeratedBytes(_data.Span, RuleSet, out int consumed, expectedTag); + + ReadOnlyMemory memory = AsnDecoder.Slice(_data, bytes); + + _data = _data.Slice(consumed); + return memory; + } + + /// + /// Reads the next value as an Enumerated with a specified tag, converting it to the + /// non-[] enum specified by . + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// Destination enum type + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public TEnum ReadEnumeratedValue(Asn1Tag? expectedTag = null) where TEnum : Enum + { + TEnum ret = AsnDecoder.ReadEnumeratedValue(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as an Enumerated with a specified tag, converting it to the + /// non-[] enum specified by . + /// + /// Type object representing the destination type. + /// + /// The tag to check for before reading, or for the default tag (Universal 10). + /// + /// + /// The Enumerated value converted to a . + /// + /// + /// This method does not validate that the return value is defined within + /// . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was declared with . + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is . + /// + public Enum ReadEnumeratedValue(Type enumType, Asn1Tag? expectedTag = null) + { + Enum ret = AsnDecoder.ReadEnumeratedValue(_data.Span, RuleSet, enumType, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs similarity index 67% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs index 9c55f21..1962f18 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.GeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.GeneralizedTime.cs @@ -2,93 +2,86 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Buffers.Text; using System.Diagnostics; +using System.Security.Cryptography; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a GeneralizedTime with tag UNIVERSAL 24. + /// Reads a GeneralizedTime value from with a specified tag under + /// the specified encoding rules. /// - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public DateTimeOffset ReadGeneralizedTime(bool disallowFractions = false) => - ReadGeneralizedTime(Asn1Tag.GeneralizedTime, disallowFractions); - - /// - /// Reads the next value as a GeneralizedTime with a specified tag. - /// - /// The tag to check for before reading. - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. + /// + /// The tag to check for before reading, or for the default tag (Universal 24). /// /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public DateTimeOffset ReadGeneralizedTime(Asn1Tag expectedTag, bool disallowFractions = false) + public static DateTimeOffset ReadGeneralizedTime( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { byte[]? rented = null; - Span tmpSpace; - unsafe - { - // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or - // BER specified offset. - const int StackBufSize = 64; - byte* stackBuf = stackalloc byte[StackBufSize]; - tmpSpace = new Span(stackBuf, StackBufSize); - } + // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or + // BER specified offset. + const int StackBufSize = 64; + Span tmpSpace = stackalloc byte[StackBufSize]; ReadOnlySpan contents = GetOctetStringContents( - expectedTag, + source, + ruleSet, + expectedTag ?? Asn1Tag.GeneralizedTime, UniversalTagNumber.GeneralizedTime, out int bytesRead, ref rented, tmpSpace); - DateTimeOffset value = ParseGeneralizedTime(RuleSet, contents, disallowFractions); + DateTimeOffset value = ParseGeneralizedTime(ruleSet, contents); if (rented != null) { CryptoPool.Return(rented, contents.Length); } - _data = _data.Slice(bytesRead); + bytesConsumed = bytesRead; return value; } private static DateTimeOffset ParseGeneralizedTime( AsnEncodingRules ruleSet, - ReadOnlySpan contentOctets, - bool disallowFractions) + ReadOnlySpan contentOctets) { // T-REC-X.680-201510 sec 46 defines a lot of formats for GeneralizedTime. // @@ -125,12 +118,12 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } else { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } } else @@ -197,11 +190,6 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 } catch (Exception e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + throw new AsnContentException(SR.ContentException_DefaultMessage, e); } } } - internal partial class AsnReader + public partial class AsnReader { /// - /// Reads the next value as a GeneralizedTime with tag UNIVERSAL 24. - /// - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. - /// - /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public DateTimeOffset ReadGeneralizedTime(bool disallowFractions = false) => - ReadGeneralizedTime(Asn1Tag.GeneralizedTime, disallowFractions); - - /// /// Reads the next value as a GeneralizedTime with a specified tag. /// - /// The tag to check for before reading. - /// - /// true to cause a to be thrown if a - /// fractional second is encountered, such as the restriction on the PKCS#7 Signing - /// Time attribute. + /// + /// The tag to check for before reading, or for the default tag (Universal 24). /// /// - /// a DateTimeOffset representing the value encoded in the GeneralizedTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public DateTimeOffset ReadGeneralizedTime(Asn1Tag expectedTag, bool disallowFractions = false) + public DateTimeOffset ReadGeneralizedTime(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - DateTimeOffset ret = valueReader.ReadGeneralizedTime(expectedTag, disallowFractions); - valueReader.MatchSlice(ref _data); + DateTimeOffset ret = AsnDecoder.ReadGeneralizedTime(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); return ret; } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs new file mode 100644 index 0000000..5e6e084 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Integer.cs @@ -0,0 +1,747 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Binary; +using System.Diagnostics; +using System.Numerics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads an Integer value from with a specified tag under + /// the specified encoding rules, returning the contents as a slice of the buffer. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The slice of the buffer containing the bytes of the Integer value, in signed big-endian form. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static ReadOnlySpan ReadIntegerBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return GetIntegerContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out bytesConsumed); + } + + /// + /// Reads an Integer value from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The decoded numeric value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static BigInteger ReadInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + ReadOnlySpan contents = ReadIntegerBytes(source, ruleSet, out int consumed, expectedTag); + + // TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing + byte[] tmp = CryptoPool.Rent(contents.Length); + BigInteger value; + + try + { + byte fill = (contents[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF; + // Fill the unused portions of tmp with positive or negative padding. + new Span(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill); + contents.CopyTo(tmp); + // Convert to Little-Endian. + AsnWriter.Reverse(new Span(tmp, 0, contents.Length)); + value = new BigInteger(tmp); + } + finally + { + // Let CryptoPool.Return clear the whole tmp so that not even the sign bit + // is returned to the array pool. + CryptoPool.Return(tmp); + } + + bytesConsumed = consumed; + return value; + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as a signed 32-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static bool TryReadInt32( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadSignedInteger( + source, + ruleSet, + sizeof(int), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out long longValue, + out bytesConsumed)) + { + value = (int)longValue; + return true; + } + + value = 0; + return false; + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as an unsigned 32-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public static bool TryReadUInt32( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out uint value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (TryReadUnsignedInteger( + source, + ruleSet, + sizeof(uint), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out ulong ulongValue, + out bytesConsumed)) + { + value = (uint)ulongValue; + return true; + } + + value = 0; + return false; + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as a signed 64-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static bool TryReadInt64( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out long value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return TryReadSignedInteger( + source, + ruleSet, + sizeof(long), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out value, + out bytesConsumed); + } + + /// + /// Attempts to read an Integer value from with a specified tag under + /// the specified encoding rules as an unsigned 64-bit value. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives the interpreted numeric value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// if the Integer represents value is between + /// and , inclusive; otherwise, + /// . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public static bool TryReadUInt64( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out ulong value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return TryReadUnsignedInteger( + source, + ruleSet, + sizeof(ulong), + expectedTag ?? Asn1Tag.Integer, + UniversalTagNumber.Integer, + out value, + out bytesConsumed); + } + + private static ReadOnlySpan GetIntegerContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out int bytesConsumed) + { + // T-REC-X.690-201508 sec 8.3.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); + + // T-REC-X.690-201508 sec 8.3.1 + if (contents.IsEmpty) + { + throw new AsnContentException(); + } + + // T-REC-X.690-201508 sec 8.3.2 + if (BinaryPrimitives.TryReadUInt16BigEndian(contents, out ushort bigEndianValue)) + { + const ushort RedundancyMask = 0b1111_1111_1000_0000; + ushort masked = (ushort)(bigEndianValue & RedundancyMask); + + // If the first 9 bits are all 0 or are all 1, the value is invalid. + if (masked == 0 || masked == RedundancyMask) + { + throw new AsnContentException(); + } + } + + bytesConsumed = consumed; + return contents; + } + + private static bool TryReadSignedInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + int sizeLimit, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out long value, + out int bytesConsumed) + { + Debug.Assert(sizeLimit <= sizeof(long)); + + ReadOnlySpan contents = GetIntegerContents( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); + + if (contents.Length > sizeLimit) + { + value = 0; + bytesConsumed = 0; + return false; + } + + bool isNegative = (contents[0] & 0x80) != 0; + long accum = isNegative ? -1 : 0; + + for (int i = 0; i < contents.Length; i++) + { + accum <<= 8; + accum |= contents[i]; + } + + bytesConsumed = consumed; + value = accum; + return true; + } + + private static bool TryReadUnsignedInteger( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + int sizeLimit, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out ulong value, + out int bytesConsumed) + { + Debug.Assert(sizeLimit <= sizeof(ulong)); + + ReadOnlySpan contents = GetIntegerContents( + source, + ruleSet, + expectedTag, + tagNumber, + out int consumed); + + bool isNegative = (contents[0] & 0x80) != 0; + + if (isNegative) + { + bytesConsumed = 0; + value = 0; + return false; + } + + // Ignore any padding zeros. + if (contents.Length > 1 && contents[0] == 0) + { + contents = contents.Slice(1); + } + + if (contents.Length > sizeLimit) + { + bytesConsumed = 0; + value = 0; + return false; + } + + ulong accum = 0; + + for (int i = 0; i < contents.Length; i++) + { + accum <<= 8; + accum |= contents[i]; + } + + bytesConsumed = consumed; + value = accum; + return true; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a Integer with a specified tag, returning the contents + /// as a over the original data. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The bytes of the Integer value, in signed big-endian form. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public ReadOnlyMemory ReadIntegerBytes(Asn1Tag? expectedTag = null) + { + ReadOnlySpan bytes = + AsnDecoder.ReadIntegerBytes(_data.Span, RuleSet, out int consumed, expectedTag); + + ReadOnlyMemory ret = AsnDecoder.Slice(_data, bytes); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as an Integer with a specified tag. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// The decoded value. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public BigInteger ReadInteger(Asn1Tag? expectedTag = null) + { + BigInteger ret = AsnDecoder.ReadInteger(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as a signed 32-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public bool TryReadInt32(out int value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadInt32(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as an unsigned 32-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public bool TryReadUInt32(out uint value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadUInt32(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as a signed 64-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public bool TryReadInt64(out long value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadInt64(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + + /// + /// Attempts to read the next value as an Integer with a specified tag, + /// as an unsigned 64-bit value. + /// + /// + /// On success, receives the decoded value. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 2). + /// + /// + /// and does not advance the reader if the value is not between + /// and , inclusive; otherwise + /// is returned and the reader advances. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + [CLSCompliant(false)] + public bool TryReadUInt64(out ulong value, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadUInt64(_data.Span, RuleSet, out value, out int read, expectedTag); + _data = _data.Slice(read); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs new file mode 100644 index 0000000..3000f88 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.NamedBitList.cs @@ -0,0 +1,593 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules, converting it to the + /// [] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// Destination enum type + /// + /// The NamedBitList value converted to a . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// The bit alignment performed by this method is to interpret the most significant bit + /// in the first byte of the value as the least significant bit in , + /// with bits increasing in value until the least significant bit of the first byte, proceeding + /// with the most significant bit of the second byte, and so on. Under this scheme, the following + /// ASN.1 type declaration and C# enumeration can be used together: + /// + /// + /// KeyUsage ::= BIT STRING { + /// digitalSignature (0), + /// nonRepudiation (1), + /// keyEncipherment (2), + /// dataEncipherment (3), + /// keyAgreement (4), + /// keyCertSign (5), + /// cRLSign (6), + /// encipherOnly (7), + /// decipherOnly (8) } + /// + /// + /// + /// [Flags] + /// enum KeyUsage + /// { + /// None = 0, + /// DigitalSignature = 1 << (0), + /// NonRepudiation = 1 << (1), + /// KeyEncipherment = 1 << (2), + /// DataEncipherment = 1 << (3), + /// KeyAgreement = 1 << (4), + /// KeyCertSign = 1 << (5), + /// CrlSign = 1 << (6), + /// EncipherOnly = 1 << (7), + /// DecipherOnly = 1 << (8), + /// } + /// + /// + /// Note that while the example here uses the KeyUsage NamedBitList from + /// RFC 3280 (4.2.1.3), + /// the example enum uses values thar are different from + /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. + /// + public static TFlagsEnum ReadNamedBitListValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + where TFlagsEnum : Enum + { + Type tFlagsEnum = typeof(TFlagsEnum); + + TFlagsEnum ret = (TFlagsEnum)Enum.ToObject( + tFlagsEnum, + ReadNamedBitListValue(source, ruleSet, tFlagsEnum, out int consumed, expectedTag)); + + // Now that there's nothing left to throw, assign bytesConsumed. + bytesConsumed = consumed; + return ret; + } + + /// + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules, converting it to the + /// [] enum specified by . + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// Type object representing the destination type. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// The NamedBitList value converted to a . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + ///- + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is + /// + /// + public static Enum ReadNamedBitListValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Type flagsEnumType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + if (flagsEnumType == null) + throw new ArgumentNullException(nameof(flagsEnumType)); + + // This will throw an ArgumentException if TEnum isn't an enum type, + // so we don't need to validate it. + Type backingType = flagsEnumType.GetEnumUnderlyingType(); + + if (!flagsEnumType.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_NamedBitListRequiresFlagsEnum, + nameof(flagsEnumType)); + } + + Span stackSpan = stackalloc byte[sizeof(ulong)]; + int sizeLimit = Marshal.SizeOf(backingType); + stackSpan = stackSpan.Slice(0, sizeLimit); + + bool read = TryReadBitString( + source, + stackSpan, + ruleSet, + out int unusedBitCount, + out int consumed, + out int bytesWritten, + expectedTag); + + if (!read) + { + throw new AsnContentException( + SR.Format(SR.ContentException_NamedBitListValueTooBig, flagsEnumType.Name)); + } + + Enum ret; + + if (bytesWritten == 0) + { + // The mode isn't relevant, zero is always zero. + ret = (Enum)Enum.ToObject(flagsEnumType, 0); + bytesConsumed = consumed; + return ret; + } + + ReadOnlySpan valueSpan = stackSpan.Slice(0, bytesWritten); + + // Now that the 0-bounds check is out of the way: + // + // T-REC-X.690-201508 sec 11.2.2 + if (ruleSet == AsnEncodingRules.DER || + ruleSet == AsnEncodingRules.CER) + { + byte lastByte = valueSpan[bytesWritten - 1]; + + // No unused bits tests 0x01, 1 is 0x02, 2 is 0x04, etc. + // We already know that TryCopyBitStringBytes checked that the + // declared unused bits were 0, this checks that the last "used" bit + // isn't also zero. + byte testBit = (byte)(1 << unusedBitCount); + + if ((lastByte & testBit) == 0) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + } + + // Consider a NamedBitList defined as + // + // SomeList ::= BIT STRING { + // a(0), b(1), c(2), d(3), e(4), f(5), g(6), h(7), i(8), j(9), k(10) + // } + // + // The BIT STRING encoding of (a | j) is + // unusedBitCount = 6, + // contents: 0x80 0x40 (0b10000000_01000000) + // + // A the C# exposure of this structure we adhere to is + // + // [Flags] + // enum SomeList + // { + // A = 1, + // B = 1 << 1, + // C = 1 << 2, + // ... + // } + // + // Which happens to be exactly backwards from how the bits are encoded, but the complexity + // only needs to live here. + ret = (Enum)Enum.ToObject(flagsEnumType, InterpretNamedBitListReversed(valueSpan)); + bytesConsumed = consumed; + return ret; + } + + /// + /// Reads a NamedBitList from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 3). + /// + /// + /// The bits from the encoded value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// The bit alignment performed by this method is to interpret the most significant bit + /// in the first byte of the value as bit 0, + /// with bits increasing in value until the least significant bit of the first byte, proceeding + /// with the most significant bit of the second byte, and so on. + /// This means that the number used in an ASN.1 NamedBitList construction is the index in the + /// return value. + /// + public static BitArray ReadNamedBitList( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + Asn1Tag actualTag = ReadEncodedValue(source, ruleSet, out _, out int contentLength, out _); + + // Get the last ArgumentException out of the way before we rent arrays + if (expectedTag != null) + { + CheckExpectedTag(actualTag, expectedTag.Value, UniversalTagNumber.BitString); + } + + // The number of interpreted bytes is at most contentLength - 1, just ask for contentLength. + byte[] rented = CryptoPool.Rent(contentLength); + + if (!TryReadBitString( + source, + rented, + ruleSet, + out int unusedBitCount, + out int consumed, + out int written, + expectedTag)) + { + Debug.Fail("TryReadBitString failed with an over-allocated buffer"); + throw new InvalidOperationException(); + } + + int validBitCount = checked(written * 8 - unusedBitCount); + + Span valueSpan = rented.AsSpan(0, written); + ReverseBitsPerByte(valueSpan); + + BitArray ret = new BitArray(rented); + CryptoPool.Return(rented, written); + + // Trim off all of the unnecessary parts. + ret.Length = validBitCount; + + bytesConsumed = consumed; + return ret; + } + + private static long InterpretNamedBitListReversed(ReadOnlySpan valueSpan) + { + Debug.Assert(valueSpan.Length <= sizeof(long)); + + long accum = 0; + long currentBitValue = 1; + + for (int byteIdx = 0; byteIdx < valueSpan.Length; byteIdx++) + { + byte byteVal = valueSpan[byteIdx]; + + for (int bitIndex = 7; bitIndex >= 0; bitIndex--) + { + int test = 1 << bitIndex; + + if ((byteVal & test) != 0) + { + accum |= currentBitValue; + } + + currentBitValue <<= 1; + } + } + + return accum; + } + + internal static void ReverseBitsPerByte(Span value) + { + for (int byteIdx = 0; byteIdx < value.Length; byteIdx++) + { + byte cur = value[byteIdx]; + byte mask = 0b1000_0000; + byte next = 0; + + for (; cur != 0; cur >>= 1, mask >>= 1) + { + next |= (byte)((cur & 1) * mask); + } + + value[byteIdx] = next; + } + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a NamedBitList with a specified tag, converting it to the + /// [] enum specified by . + /// + /// The tag to check for before reading. + /// Destination enum type + /// + /// The NamedBitList value converted to a . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// The bit alignment performed by this method is to interpret the most significant bit + /// in the first byte of the value as the least significant bit in , + /// with bits increasing in value until the least significant bit of the first byte, proceeding + /// with the most significant bit of the second byte, and so on. Under this scheme, the following + /// ASN.1 type declaration and C# enumeration can be used together: + /// + /// + /// KeyUsage ::= BIT STRING { + /// digitalSignature (0), + /// nonRepudiation (1), + /// keyEncipherment (2), + /// dataEncipherment (3), + /// keyAgreement (4), + /// keyCertSign (5), + /// cRLSign (6), + /// encipherOnly (7), + /// decipherOnly (8) } + /// + /// + /// + /// [Flags] + /// enum KeyUsage + /// { + /// None = 0, + /// DigitalSignature = 1 << (0), + /// NonRepudiation = 1 << (1), + /// KeyEncipherment = 1 << (2), + /// DataEncipherment = 1 << (3), + /// KeyAgreement = 1 << (4), + /// KeyCertSign = 1 << (5), + /// CrlSign = 1 << (6), + /// EncipherOnly = 1 << (7), + /// DecipherOnly = 1 << (8), + /// } + /// + /// + /// Note that while the example here uses the KeyUsage NamedBitList from + /// RFC 3280 (4.2.1.3), + /// the example enum uses values thar are different from + /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. + /// + public TFlagsEnum ReadNamedBitListValue(Asn1Tag? expectedTag = null) where TFlagsEnum : Enum + { + TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue( + _data.Span, + RuleSet, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as a NamedBitList with a specified tag, converting it to the + /// [] enum specified by . + /// + /// The tag to check for before reading. + /// Type object representing the destination type. + /// + /// The NamedBitList value converted to a . + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the encoded value is too big to fit in a value. + /// + /// + /// is not an enum type. + /// + /// -or- + /// + /// was not declared with + /// + /// -or- + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is + /// + /// + public Enum ReadNamedBitListValue(Type flagsEnumType, Asn1Tag? expectedTag = null) + { + Enum ret = AsnDecoder.ReadNamedBitListValue( + _data.Span, + RuleSet, + flagsEnumType, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as a NamedBitList with a specified tag. + /// + /// The tag to check for before reading. + /// + /// The bits from the encoded value. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public BitArray ReadNamedBitList(Asn1Tag? expectedTag = null) + { + BitArray ret = AsnDecoder.ReadNamedBitList(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs new file mode 100644 index 0000000..f8c91c2 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Null.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Null value from with a specified tag under + /// the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 5). + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static void ReadNull( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + // T-REC-X.690-201508 sec 8.8.1 + ReadOnlySpan contents = GetPrimitiveContentSpan( + source, + ruleSet, + expectedTag ?? Asn1Tag.Null, + UniversalTagNumber.Null, + out int consumed); + + // T-REC-X.690-201508 sec 8.8.2 + if (contents.Length != 0) + { + throw new AsnContentException(); + } + + bytesConsumed = consumed; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a NULL with a specified tag. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 5). + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public void ReadNull(Asn1Tag? expectedTag = null) + { + AsnDecoder.ReadNull(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs new file mode 100644 index 0000000..2f7422d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.OctetString.cs @@ -0,0 +1,686 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Attempts to get an Octet String value from with a specified tag under + /// the specified encoding rules, copying the value into the provided destination buffer. + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes written to . + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// if is large enough to receive the + /// value of the Octet String; + /// otherwise, . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// overlaps . + /// + /// + /// + public static bool TryReadOctetString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int bytesConsumed, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } + + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out int consumed)) + { + if (contents.Length > destination.Length) + { + bytesWritten = 0; + bytesConsumed = 0; + return false; + } + + contents.CopyTo(destination); + bytesWritten = contents.Length; + bytesConsumed = consumed; + return true; + } + + bool copied = TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, + destination, + contentLength == null, + out int bytesRead, + out bytesWritten); + + if (copied) + { + bytesConsumed = headerLength + bytesRead; + } + else + { + bytesConsumed = 0; + } + + return copied; + } + + /// + /// Reads an Octet String value from with a specified tag under + /// the specified encoding rules, returning the contents in a new array. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// An array containing the contents of the Octet String value. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public static byte[] ReadOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + byte[]? rented = null; + + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + out int consumed, + ref rented); + + byte[] ret = contents.ToArray(); + + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } + + bytesConsumed = consumed; + return ret; + } + + private static bool TryReadPrimitiveOctetStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out int bytesConsumed) + { + Asn1Tag actualTag = ReadTagAndLength(source, ruleSet, out contentLength, out headerLength); + CheckExpectedTag(actualTag, expectedTag, universalTagNumber); + + // Ensure the length makes sense. + ReadOnlySpan encodedValue = Slice(source, headerLength, contentLength); + + if (actualTag.IsConstructed) + { + if (ruleSet == AsnEncodingRules.DER) + { + throw new AsnContentException(SR.ContentException_InvalidUnderDer_TryBerOrCer); + } + + contents = default; + bytesConsumed = 0; + return false; + } + + Debug.Assert(contentLength.HasValue); + + if (ruleSet == AsnEncodingRules.CER && encodedValue.Length > MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCer_TryBerOrDer); + } + + contents = encodedValue; + bytesConsumed = headerLength + encodedValue.Length; + return true; + } + + /// + /// Attempts to get an Octet String value from with a specified tag under + /// the specified encoding rules, if the value is contained in a single (primitive) encoding. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Octet String. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// if the Octet String value has a primitive encoding; + /// otherwise, . + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public static bool TryReadPrimitiveOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out ReadOnlySpan value, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + return TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag ?? Asn1Tag.PrimitiveOctetString, + UniversalTagNumber.OctetString, + contentLength: out _, + headerLength: out _, + out value, + out bytesConsumed); + } + + private static int CountConstructedOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + bool isIndefinite) + { + int contentLength = CopyConstructedOctetString( + source, + ruleSet, + Span.Empty, + false, + isIndefinite, + out _); + + // T-REC-X.690-201508 sec 9.2 + if (ruleSet == AsnEncodingRules.CER && contentLength <= MaxCERSegmentSize) + { + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + return contentLength; + } + + private static void CopyConstructedOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + bool isIndefinite, + out int bytesRead, + out int bytesWritten) + { + bytesWritten = CopyConstructedOctetString( + source, + ruleSet, + destination, + true, + isIndefinite, + out bytesRead); + } + + private static int CopyConstructedOctetString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span destination, + bool write, + bool isIndefinite, + out int bytesRead) + { + bytesRead = 0; + int lastSegmentLength = MaxCERSegmentSize; + + ReadOnlySpan cur = source; + Stack<(int Offset, int Length, bool IsIndefinite, int BytesRead)>? readerStack = null; + int totalLength = 0; + Asn1Tag tag = Asn1Tag.ConstructedBitString; + Span curDest = destination; + + while (true) + { + while (!cur.IsEmpty) + { + tag = ReadTagAndLength(cur, ruleSet, out int? length, out int headerLength); + + if (tag == Asn1Tag.PrimitiveOctetString) + { + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + Debug.Assert(length != null); + + // The call to Slice here sanity checks the data bounds, length.Value is not + // reliable unless this call has succeeded. + ReadOnlySpan contents = Slice(cur, headerLength, length.Value); + + int localLen = headerLength + contents.Length; + cur = cur.Slice(localLen); + + bytesRead += localLen; + totalLength += contents.Length; + lastSegmentLength = contents.Length; + + if (ruleSet == AsnEncodingRules.CER && lastSegmentLength > MaxCERSegmentSize) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (write) + { + contents.CopyTo(curDest); + curDest = curDest.Slice(contents.Length); + } + } + else if (tag == Asn1Tag.EndOfContents && isIndefinite) + { + ValidateEndOfContents(tag, length, headerLength); + + bytesRead += headerLength; + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + cur = topSpan.Slice(bytesRead); + + bytesRead += pushedBytesRead; + isIndefinite = wasIndefinite; + } + else + { + // We have matched the EndOfContents that brought us here. + break; + } + } + else if (tag == Asn1Tag.ConstructedOctetString) + { + if (ruleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + throw new AsnContentException(SR.ContentException_InvalidUnderCerOrDer_TryBer); + } + + if (readerStack == null) + { + readerStack = new Stack<(int, int, bool, int)>(); + } + + if (!source.Overlaps(cur, out int curOffset)) + { + Debug.Fail("Non-overlapping data encountered..."); + throw new AsnContentException(); + } + + readerStack.Push((curOffset, cur.Length, isIndefinite, bytesRead)); + + cur = Slice(cur, headerLength, length); + bytesRead = headerLength; + isIndefinite = (length == null); + } + else + { + // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) + throw new AsnContentException(); + } + } + + if (isIndefinite && tag != Asn1Tag.EndOfContents) + { + throw new AsnContentException(); + } + + if (readerStack?.Count > 0) + { + (int topOffset, int topLength, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); + ReadOnlySpan topSpan = source.Slice(topOffset, topLength); + + cur = topSpan.Slice(bytesRead); + + isIndefinite = wasIndefinite; + bytesRead += pushedBytesRead; + } + else + { + return totalLength; + } + } + } + + private static bool TryCopyConstructedOctetStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Span dest, + bool isIndefinite, + out int bytesRead, + out int bytesWritten) + { + bytesRead = 0; + + int contentLength = CountConstructedOctetString(source, ruleSet, isIndefinite); + + if (dest.Length < contentLength) + { + bytesWritten = 0; + return false; + } + + CopyConstructedOctetString(source, ruleSet, dest, isIndefinite, out bytesRead, out bytesWritten); + + Debug.Assert(bytesWritten == contentLength); + return true; + } + + private static ReadOnlySpan GetOctetStringContents( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + out int bytesConsumed, + ref byte[]? rented, + Span tmpSpace = default) + { + Debug.Assert(rented == null); + + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out bytesConsumed)) + { + return contents; + } + + // If we get here, the tag was appropriate, but the encoding was constructed. + + // Guaranteed long enough + contents = source.Slice(headerLength); + int tooBig = contentLength ?? SeekEndOfContents(contents, ruleSet); + + if (tmpSpace.Length > 0 && tooBig > tmpSpace.Length) + { + bool isIndefinite = contentLength == null; + tooBig = CountConstructedOctetString(contents, ruleSet, isIndefinite); + } + + if (tooBig > tmpSpace.Length) + { + rented = CryptoPool.Rent(tooBig); + tmpSpace = rented; + } + + if (TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, + tmpSpace, + contentLength == null, + out int bytesRead, + out int bytesWritten)) + { + bytesConsumed = headerLength + bytesRead; + return tmpSpace.Slice(0, bytesWritten); + } + + Debug.Fail("TryCopyConstructedOctetStringContents failed with a pre-allocated buffer"); + throw new AsnContentException(); + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as an OCTET STRING with a specified tag, copying the value + /// into a provided destination buffer. + /// + /// The buffer in which to write. + /// + /// On success, receives the number of bytes written to . + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public bool TryReadOctetString( + Span destination, + out int bytesWritten, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadOctetString( + _data.Span, + destination, + RuleSet, + out int bytesConsumed, + out bytesWritten, + expectedTag); + + if (ret) + { + _data = _data.Slice(bytesConsumed); + } + + return ret; + } + + /// + /// Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value + /// in a byte array. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 4). + /// + /// + /// A copy of the value in a newly allocated, precisely sized, array. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// + public byte[] ReadOctetString(Asn1Tag? expectedTag = null) + { + byte[] ret = AsnDecoder.ReadOctetString(_data.Span, RuleSet, out int consumed, expectedTag); + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Attempts to read the next value as an OCTET STRING with a specified tag, returning the contents + /// as a over the original data. + /// + /// The tag to check for before reading. + /// + /// On success, receives a over the original data + /// corresponding to the value of the OCTET STRING. + /// + /// + /// and advances the reader if the OCTET STRING value had a primitive encoding, + /// and does not advance the reader if it had a constructed encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public bool TryReadPrimitiveOctetString(out ReadOnlyMemory contents, Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadPrimitiveOctetString( + _data.Span, + RuleSet, + out ReadOnlySpan span, + out int consumed, + expectedTag); + + if (ret) + { + contents = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + contents = default; + } + + return ret; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs similarity index 56% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs index 7569239..5f3b3b3 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.Oid.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs @@ -2,93 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; using System.Numerics; +using System.Security.Cryptography; using System.Text; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value in a dotted decimal format string. + /// Reads an Object Identifier value from with a specified tag under + /// the specified encoding rules. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 6). + /// + /// + /// The decoded object identifier, in dotted-decimal notation. + /// + /// + /// is not defined. /// - public string ReadObjectIdentifierAsString() => - ReadObjectIdentifierAsString(Asn1Tag.ObjectIdentifier); - - /// - /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning - /// the value in a dotted decimal format string. - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public string ReadObjectIdentifierAsString(Asn1Tag expectedTag) - { - string oidValue = ReadObjectIdentifierAsString(expectedTag, out int bytesRead); - - _data = _data.Slice(bytesRead); - - return oidValue; - } - - /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value as an . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public Oid ReadObjectIdentifier() => - ReadObjectIdentifier(Asn1Tag.ObjectIdentifier); - - /// - /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning - /// the value as an . - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Oid ReadObjectIdentifier(Asn1Tag expectedTag) + public static string ReadObjectIdentifier( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + Asn1Tag? expectedTag = null) { - string oidValue = ReadObjectIdentifierAsString(expectedTag, out int bytesRead); - // Specifying null for friendly name makes the lookup deferred until first read - // of the Oid.FriendlyName property. - Oid oid = new Oid(oidValue, null); - - // Don't slice until the return object has been created. - _data = _data.Slice(bytesRead); - - return oid; + // TODO: Inline this call when it won't cause a PR/diff problem. + return ReadObjectIdentifier(source, ruleSet, expectedTag, out bytesConsumed); } private static void ReadSubIdentifier( @@ -102,7 +69,7 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 if (end < 0) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } bytesRead = end + 1; @@ -206,20 +173,26 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 contents = contents.Slice(bytesRead); } - totalBytesRead = headerLength + length.Value; + totalBytesRead = consumed; return builder.ToString(); } } - internal partial class AsnReader + public partial class AsnReader { /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value in a dotted decimal format string. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public string ReadObjectIdentifierAsString() => - ReadObjectIdentifierAsString(Asn1Tag.ObjectIdentifier); - - /// /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning /// the value in a dotted decimal format string. /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// The tag to check for before reading, or for the default tag (Universal 6). + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - public string ReadObjectIdentifierAsString(Asn1Tag expectedTag) + public string ReadObjectIdentifier(Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - string oidValue = valueReader.ReadObjectIdentifierAsString(expectedTag); - valueReader.MatchSlice(ref _data); - return oidValue; - } + string oidValue = + AsnDecoder.ReadObjectIdentifier(_data.Span, RuleSet, out int consumed, expectedTag); - /// - /// Reads the next value as an OBJECT IDENTIFIER with tag UNIVERSAL 6, returning - /// the value as an . - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - public Oid ReadObjectIdentifier() => - ReadObjectIdentifier(Asn1Tag.ObjectIdentifier); - - /// - /// Reads the next value as an OBJECT IDENTIFIER with a specified tag, returning - /// the value as an . - /// - /// The tag to check for before reading. - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - public Oid ReadObjectIdentifier(Asn1Tag expectedTag) - { - AsnValueReader valueReader = OpenValueReader(); - Oid oid = valueReader.ReadObjectIdentifier(expectedTag); - valueReader.MatchSlice(ref _data); - return oid; + _data = _data.Slice(consumed); + return oidValue; } } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs new file mode 100644 index 0000000..dde93f9 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Sequence.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Sequence or Sequence-Of value from with a specified tag + /// under the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 16). + /// + /// + /// The nested content is not evaluated by this method, except for minimal processing to + /// determine the location of an end-of-contents marker. + /// Therefore, the contents may contain data which is not valid under the current encoding rules. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static void ReadSequence( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + Asn1Tag? expectedTag = default) + { + Asn1Tag tag = ReadTagAndLength(source, ruleSet, out int? length, out int headerLength); + CheckExpectedTag(tag, expectedTag ?? Asn1Tag.Sequence, UniversalTagNumber.Sequence); + + // T-REC-X.690-201508 sec 8.9.1 + // T-REC-X.690-201508 sec 8.10.1 + if (!tag.IsConstructed) + { + throw new AsnContentException( + SR.Format( + SR.ContentException_ConstructedEncodingRequired, + UniversalTagNumber.Sequence)); + } + + if (length.HasValue) + { + if (length.Value + headerLength > source.Length) + { + throw GetValidityException(LengthValidity.LengthExceedsInput); + } + + contentLength = length.Value; + contentOffset = headerLength; + bytesConsumed = contentLength + headerLength; + } + else + { + int len = SeekEndOfContents(source.Slice(headerLength), ruleSet); + + contentLength = len; + contentOffset = headerLength; + bytesConsumed = len + headerLength + EndOfContentsEncodedLength; + } + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a SEQUENCE or SEQUENCE-OF with the specified tag + /// and returns the result as a new reader positioned at the first + /// value in the sequence (or with == ). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 16). + /// + /// + /// A new reader positioned at the first + /// value in the sequence (or with == ). + /// + /// + /// the nested content is not evaluated by this method, and may contain data + /// which is not valid under the current encoding rules. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public AsnReader ReadSequence(Asn1Tag? expectedTag = null) + { + AsnDecoder.ReadSequence( + _data.Span, + RuleSet, + out int contentStart, + out int contentLength, + out int bytesConsumed, + expectedTag); + + AsnReader ret = CloneAtSlice(contentStart, contentLength); + _data = _data.Slice(bytesConsumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs new file mode 100644 index 0000000..ee91de3 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.SetOf.cs @@ -0,0 +1,227 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Reads a Set-Of value from with a specified tag + /// under the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// to always accept the data in the order it is presented, + /// to verify that the data is sorted correctly when the + /// encoding rules say sorting was required (CER and DER). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// + /// + /// The nested content is not evaluated by this method, except for minimal processing to + /// determine the location of an end-of-contents marker or verification of the content + /// sort order. + /// Therefore, the contents may contain data which is not valid under the current encoding rules. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public static void ReadSetOf( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + bool skipSortOrderValidation = false, + Asn1Tag? expectedTag = null) + { + Asn1Tag tag = ReadTagAndLength(source, ruleSet, out int? length, out int headerLength); + CheckExpectedTag(tag, expectedTag ?? Asn1Tag.SetOf, UniversalTagNumber.SetOf); + + // T-REC-X.690-201508 sec 8.12.1 + if (!tag.IsConstructed) + { + throw new AsnContentException( + SR.Format( + SR.ContentException_ConstructedEncodingRequired, + UniversalTagNumber.SetOf)); + } + + int suffix; + ReadOnlySpan contents; + + if (length.HasValue) + { + suffix = 0; + contents = Slice(source, headerLength, length.Value); + } + else + { + int actualLength = SeekEndOfContents(source.Slice(headerLength), ruleSet); + contents = Slice(source, headerLength, actualLength); + suffix = EndOfContentsEncodedLength; + } + + if (!skipSortOrderValidation) + { + // T-REC-X.690-201508 sec 11.6 + // BER data is not required to be sorted. + if (ruleSet == AsnEncodingRules.DER || + ruleSet == AsnEncodingRules.CER) + { + ReadOnlySpan remaining = contents; + ReadOnlySpan previous = default; + + while (!remaining.IsEmpty) + { + ReadEncodedValue(remaining, ruleSet, out _, out _, out int consumed); + + ReadOnlySpan current = remaining.Slice(0, consumed); + remaining = remaining.Slice(consumed); + + if (SetOfValueComparer.Compare(current, previous) < 0) + { + throw new AsnContentException(SR.ContentException_SetOfNotSorted); + } + + previous = current; + } + } + } + + contentOffset = headerLength; + contentLength = contents.Length; + bytesConsumed = headerLength + contents.Length + suffix; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a SET-OF with the specified tag + /// and returns the result as a new reader positioned at the first + /// value in the set-of (or with == ), + /// using the value + /// from the constructor (default ). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// + /// + /// A new reader positioned at the first + /// value in the set-of (or with == ). + /// + /// + /// the nested content is not evaluated by this method (aside from sort order, when + /// required), and may contain data which is not valid under the current encoding rules. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public AsnReader ReadSetOf(Asn1Tag? expectedTag = null) + { + return ReadSetOf(_options.SkipSetSortOrderVerification, expectedTag); + } + + /// + /// Reads the next value as a SET-OF with the specified tag + /// and returns the result as a new reader positioned at the first + /// value in the set-of (or with == ). + /// + /// + /// to always accept the data in the order it is presented, + /// to verify that the data is sorted correctly when the + /// encoding rules say sorting was required (CER and DER). + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 17). + /// + /// + /// A new reader positioned at the first + /// value in the set-of (or with == ). + /// + /// + /// the nested content is not evaluated by this method (aside from sort order, when + /// required), and may contain data which is not valid under the current encoding rules. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public AsnReader ReadSetOf(bool skipSortOrderValidation, Asn1Tag? expectedTag = null) + { + AsnDecoder.ReadSetOf( + _data.Span, + RuleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed, + skipSortOrderValidation, + expectedTag); + + AsnReader ret = CloneAtSlice(contentOffset, contentLength); + _data = _data.Slice(bytesConsumed); + return ret; + } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs new file mode 100644 index 0000000..1ac341a --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs @@ -0,0 +1,784 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; + +namespace System.Formats.Asn1 +{ + public static partial class AsnDecoder + { + /// + /// Attempts to get an unprocessed character string value from with a + /// specified tag under the specified encoding rules, if the value is contained in a single + /// (primitive) encoding. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// The tag to check for before reading. + /// + /// + /// On success, receives a slice of the input buffer that corresponds to + /// the value of the Bit String. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// if the character string value has a primitive encoding; + /// otherwise, . + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// + public static bool TryReadPrimitiveCharacterStringBytes( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + out ReadOnlySpan value, + out int bytesConsumed) + { + // This doesn't matter, except for universal tags. It's eventually used to check that + // we're not expecting the wrong universal tag; but we'll remove the need for that by + // IsCharacterStringEncodingType. + UniversalTagNumber universalTagNumber = UniversalTagNumber.IA5String; + + if (expectedTag.TagClass == TagClass.Universal) + { + universalTagNumber = (UniversalTagNumber)expectedTag.TagValue; + + if (!IsCharacterStringEncodingType(universalTagNumber)) + { + throw new ArgumentException(SR.Argument_Tag_NotCharacterString, nameof(expectedTag)); + } + } + + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + return TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + contentLength: out _, + headerLength: out _, + out value, + out bytesConsumed); + } + + /// + /// Attempts to read a character string value from with a + /// specified tag under the specified encoding rules, + /// copying the unprocessed bytes into the provided destination buffer. + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// The tag to check for before reading. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// On success, receives the number of bytes written to . + /// + /// + /// if is large enough to receive the + /// value of the unprocessed character string; + /// otherwise, . + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// is not defined. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// -or- + /// + /// overlaps . + /// + /// + /// + /// + public static bool TryReadCharacterStringBytes( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + out int bytesConsumed, + out int bytesWritten) + { + if (source.Overlaps(destination)) + { + throw new ArgumentException( + SR.Argument_SourceOverlapsDestination, + nameof(destination)); + } + + // This doesn't matter, except for universal tags. It's eventually used to check that + // we're not expecting the wrong universal tag; but we'll remove the need for that by + // IsCharacterStringEncodingType. + UniversalTagNumber universalTagNumber = UniversalTagNumber.IA5String; + + if (expectedTag.TagClass == TagClass.Universal) + { + universalTagNumber = (UniversalTagNumber)expectedTag.TagValue; + + if (!IsCharacterStringEncodingType(universalTagNumber)) + { + throw new ArgumentException(SR.Argument_Tag_NotCharacterString, nameof(expectedTag)); + } + } + + return TryReadCharacterStringBytesCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + destination, + out bytesConsumed, + out bytesWritten); + } + + /// + /// Reads a character string value from with a specified tag under + /// the specified encoding rules, copying the decoded string into a a provided destination buffer. + /// + /// The buffer containing encoded data. + /// The buffer in which to write. + /// The encoding constraints to use when interpreting the data. + /// + /// One of the enumeration values which represents the value type to process. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of chars written to . + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// is not defined. + /// + /// -or- + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + public static bool TryReadCharacterString( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + UniversalTagNumber encodingType, + out int bytesConsumed, + out int charsWritten, + Asn1Tag? expectedTag = null) + { + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + + return TryReadCharacterStringCore( + source, + ruleSet, + expectedTag ?? new Asn1Tag(encodingType), + encodingType, + encoding, + destination, + out bytesConsumed, + out charsWritten); + } + + /// + /// Reads the next value as character string with the specified tag and + /// encoding type, returning the decoded string. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// One of the enumeration values which represents the value type to process. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// The decoded value. + /// + /// + /// is not defined. + /// + /// -or- + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + /// + public static string ReadCharacterString( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + UniversalTagNumber encodingType, + out int bytesConsumed, + Asn1Tag? expectedTag = null) + { + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + + return ReadCharacterStringCore( + source, + ruleSet, + expectedTag ?? new Asn1Tag(encodingType), + encodingType, + encoding, + out bytesConsumed); + } + + // T-REC-X.690-201508 sec 8.23 + private static bool TryReadCharacterStringBytesCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + Span destination, + out int bytesConsumed, + out int bytesWritten) + { + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + if (TryReadPrimitiveOctetStringCore( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int? contentLength, + out int headerLength, + out ReadOnlySpan contents, + out int consumed)) + { + if (contents.Length > destination.Length) + { + bytesWritten = 0; + bytesConsumed = 0; + return false; + } + + contents.CopyTo(destination); + bytesWritten = contents.Length; + bytesConsumed = consumed; + return true; + } + + bool copied = TryCopyConstructedOctetStringContents( + Slice(source, headerLength, contentLength), + ruleSet, + destination, + contentLength == null, + out int bytesRead, + out bytesWritten); + + if (copied) + { + bytesConsumed = headerLength + bytesRead; + } + else + { + bytesConsumed = 0; + } + + return copied; + } + + private static unsafe bool TryReadCharacterStringCore( + ReadOnlySpan source, + Span destination, + Text.Encoding encoding, + out int charsWritten) + { + if (source.Length == 0) + { + charsWritten = 0; + return true; + } + + fixed (byte* bytePtr = &MemoryMarshal.GetReference(source)) + fixed (char* charPtr = &MemoryMarshal.GetReference(destination)) + { + try + { + int charCount = encoding.GetCharCount(bytePtr, source.Length); + + if (charCount > destination.Length) + { + charsWritten = 0; + return false; + } + + charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length); + Debug.Assert(charCount == charsWritten); + } + catch (DecoderFallbackException e) + { + throw new AsnContentException(SR.ContentException_DefaultMessage, e); + } + + return true; + } + } + + private static string ReadCharacterStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + Text.Encoding encoding, + out int bytesConsumed) + { + byte[]? rented = null; + + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int bytesRead, + ref rented); + + string str; + + if (contents.Length == 0) + { + str = string.Empty; + } + else + { + unsafe + { + fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents)) + { + try + { + str = encoding.GetString(bytePtr, contents.Length); + } + catch (DecoderFallbackException e) + { + throw new AsnContentException(SR.ContentException_DefaultMessage, e); + } + } + } + } + + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } + + bytesConsumed = bytesRead; + return str; + } + + private static bool TryReadCharacterStringCore( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber universalTagNumber, + Text.Encoding encoding, + Span destination, + out int bytesConsumed, + out int charsWritten) + { + byte[]? rented = null; + + // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings. + ReadOnlySpan contents = GetOctetStringContents( + source, + ruleSet, + expectedTag, + universalTagNumber, + out int bytesRead, + ref rented); + + bool copied = TryReadCharacterStringCore( + contents, + destination, + encoding, + out charsWritten); + + if (rented != null) + { + CryptoPool.Return(rented, contents.Length); + } + + if (copied) + { + bytesConsumed = bytesRead; + } + else + { + bytesConsumed = 0; + } + + return copied; + } + + private static bool IsCharacterStringEncodingType(UniversalTagNumber encodingType) + { + // T-REC-X.680-201508 sec 41 + switch (encodingType) + { + case UniversalTagNumber.BMPString: + case UniversalTagNumber.GeneralString: + case UniversalTagNumber.GraphicString: + case UniversalTagNumber.IA5String: + case UniversalTagNumber.ISO646String: + case UniversalTagNumber.NumericString: + case UniversalTagNumber.PrintableString: + case UniversalTagNumber.TeletexString: + // T61String is an alias for TeletexString (already listed) + case UniversalTagNumber.UniversalString: + case UniversalTagNumber.UTF8String: + case UniversalTagNumber.VideotexString: + // VisibleString is an alias for ISO646String (already listed) + return true; + } + + return false; + } + } + + public partial class AsnReader + { + /// + /// Reads the next value as a character with a specified tag, returning the contents + /// as an unprocessed over the original data. + /// + /// The tag to check for before reading. + /// + /// On success, receives a over the original data + /// corresponding to the value of the character string. + /// + /// + /// and advances the reader if the character string value had a primitive encoding, + /// and does not advance the reader if it had a constructed encoding. + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// + public bool TryReadPrimitiveCharacterStringBytes( + Asn1Tag expectedTag, + out ReadOnlyMemory contents) + { + bool ret = AsnDecoder.TryReadPrimitiveCharacterStringBytes( + _data.Span, + RuleSet, + expectedTag, + out ReadOnlySpan span, + out int consumed); + + if (ret) + { + contents = AsnDecoder.Slice(_data, span); + _data = _data.Slice(consumed); + } + else + { + contents = default; + } + + return ret; + } + + /// + /// Reads the next value as character string with the specified tag, + /// copying the unprocessed bytes into a provided destination buffer. + /// + /// The buffer in which to write. + /// The tag to check for before reading. + /// + /// On success, receives the number of bytes written to . + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// This method does not determine if the string used only characters defined by the encoding. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// + /// . is + /// , but + /// . is not a character + /// string tag type. + /// + /// + /// + /// + public bool TryReadCharacterStringBytes( + Span destination, + Asn1Tag expectedTag, + out int bytesWritten) + { + bool ret = AsnDecoder.TryReadCharacterStringBytes( + _data.Span, + destination, + RuleSet, + expectedTag, + out int consumed, + out bytesWritten); + + if (ret) + { + _data = _data.Slice(consumed); + } + + return ret; + } + + /// + /// Reads the next value as character string with the specified tag and + /// encoding type, copying the decoded value into a provided destination buffer. + /// + /// + /// One of the enumeration values representing the value type to process. + /// + /// The buffer in which to write. + /// + /// On success, receives the number of chars written to . + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// and advances the reader if had sufficient + /// length to receive the value, otherwise + /// and the reader does not advance. + /// + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + /// + public bool TryReadCharacterString( + Span destination, + UniversalTagNumber encodingType, + out int charsWritten, + Asn1Tag? expectedTag = null) + { + bool ret = AsnDecoder.TryReadCharacterString( + _data.Span, + destination, + RuleSet, + encodingType, + out int consumed, + out charsWritten, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + + /// + /// Reads the next value as character string with the specified tag and + /// encoding type, returning the decoded value as a string. + /// + /// + /// One of the enumeration values representing the value type to process. + /// + /// + /// The tag to check for before reading, or for the universal tag that is + /// appropriate to the requested encoding type. + /// + /// + /// The decoded value. + /// + /// + /// is not a known character string type. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. + /// + /// -or- + /// + /// the string did not successfully decode. + /// + /// + /// . is + /// , but + /// . is not the same as + /// . + /// + /// + /// + /// + public string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = null) + { + string ret = AsnDecoder.ReadCharacterString( + _data.Span, + RuleSet, + encodingType, + out int consumed, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs similarity index 60% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs index 4760ebf..adf1781 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnReader.UtcTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.UtcTime.cs @@ -2,59 +2,70 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Diagnostics; +using System.Security.Cryptography; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal ref partial struct AsnValueReader + public static partial class AsnDecoder { /// - /// Reads the next value as a UTCTime with tag UNIVERSAL 23. + /// Reads a UtcTime value from with a specified tag under + /// the specified encoding rules. /// - /// - /// The largest year to represent with this value. - /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. /// - /// - /// a DateTimeOffset representing the value encoded in the UTCTime. - /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules - /// - /// - /// - public DateTimeOffset ReadUtcTime(int twoDigitYearMax = 2049) => - ReadUtcTime(Asn1Tag.UtcTime, twoDigitYearMax); - - /// - /// Reads the next value as a UTCTime with a specified tag. - /// - /// The tag to check for before reading. /// /// The largest year to represent with this value. /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. /// + /// + /// The tag to check for before reading, or for the default tag (Universal 24). + /// /// - /// a DateTimeOffset representing the value encoded in the UTCTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// is not defined. + /// + /// -or- + /// + /// is not in the range [99, 9999]. + /// + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// - public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 2049) + public static DateTimeOffset ReadUtcTime( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int bytesConsumed, + int twoDigitYearMax = 2049, + Asn1Tag? expectedTag = null) { + if (twoDigitYearMax < 1 || twoDigitYearMax > 9999) + { + throw new ArgumentOutOfRangeException(nameof(twoDigitYearMax)); + } + // T-REC-X.680-201510 sec 47.3 says it is IMPLICIT VisibleString, which means // that BER is allowed to do complex constructed forms. @@ -69,25 +80,20 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 { if (contents[0] != 'Z') { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } } else @@ -176,7 +185,7 @@ namespace System.Security.Cryptography.Asn1 } else if (contents[0] != '+') { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new AsnContentException(); } contents = contents.Slice(1); @@ -190,7 +199,7 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 } catch (Exception e) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + throw new AsnContentException(SR.ContentException_DefaultMessage, e); } } } - internal partial class AsnReader + public partial class AsnReader { /// - /// Reads the next value as a UTCTime with tag UNIVERSAL 23. + /// Reads the next value as a UTCTime with a specified tag using the + /// value from options passed to + /// the constructor (with a default of 2049). /// - /// - /// The largest year to represent with this value. - /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. + /// + /// The tag to check for before reading, or for the default tag (Universal 23). /// /// - /// a DateTimeOffset representing the value encoded in the UTCTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// - /// - /// - public DateTimeOffset ReadUtcTime(int twoDigitYearMax = 2049) => - ReadUtcTime(Asn1Tag.UtcTime, twoDigitYearMax); + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public DateTimeOffset ReadUtcTime(Asn1Tag? expectedTag = null) + { + DateTimeOffset ret = AsnDecoder.ReadUtcTime( + _data.Span, + RuleSet, + out int consumed, + _options.UtcTimeTwoDigitYearMax, + expectedTag); + + _data = _data.Slice(consumed); + return ret; + } /// /// Reads the next value as a UTCTime with a specified tag. /// - /// The tag to check for before reading. /// /// The largest year to represent with this value. - /// The default value, 2049, represents the 1950-2049 range for X.509 certificates. + /// + /// + /// The tag to check for before reading, or for the default tag (Universal 23). /// /// - /// a DateTimeOffset representing the value encoded in the UTCTime. + /// The decoded value. /// - /// - /// the next value does not have the correct tag --OR-- - /// the length encoding is not valid under the current encoding rules --OR-- - /// the contents are not valid under the current encoding rules + /// + /// the next value does not have the correct tag. + /// + /// -or- + /// + /// the length encoding is not valid under the current encoding rules. + /// + /// -or- + /// + /// the contents are not valid under the current encoding rules. /// /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// + /// /// - public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 2049) + public DateTimeOffset ReadUtcTime(int twoDigitYearMax, Asn1Tag? expectedTag = null) { - AsnValueReader valueReader = OpenValueReader(); - DateTimeOffset ret = valueReader.ReadUtcTime(expectedTag, twoDigitYearMax); - valueReader.MatchSlice(ref _data); + DateTimeOffset ret = + AsnDecoder.ReadUtcTime(_data.Span, RuleSet, out int consumed, twoDigitYearMax, expectedTag); + + _data = _data.Slice(consumed); return ret; } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs new file mode 100644 index 0000000..6a3f34b --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.cs @@ -0,0 +1,803 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Text; +using System.Diagnostics; + +namespace System.Formats.Asn1 +{ + /// + /// Provides stateless methods for decoding BER-, CER-, or DER-encoded ASN.1 data. + /// + public static partial class AsnDecoder + { + // T-REC-X.690-201508 sec 9.2 + internal const int MaxCERSegmentSize = 1000; + + // T-REC-X.690-201508 sec 8.1.5 says only 0000 is legal. + internal const int EndOfContentsEncodedLength = 2; + + /// + /// Attempts locate the contents range for the encoded value at the beginning of the + /// buffer using the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the tag identifying the content. + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// if represents a valid structural + /// encoding for the specified encoding rules; otherwise, . + /// + /// + /// + /// This method performs very little validation on the contents. + /// If the encoded value uses a definite length, the contents are not inspected at all. + /// If the encoded value uses an indefinite length, the contents are only inspected + /// as necessary to determine the location of the relevant end-of-contents marker. + /// + /// + /// When the encoded value uses an indefinite length, the + /// value will be larger than the sum of and + /// to account for the end-of-contents marker. + /// + /// + /// + /// is not defined. + /// + public static bool TryReadEncodedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out Asn1Tag tag, + out int contentOffset, + out int contentLength, + out int bytesConsumed) + { + CheckEncodingRules(ruleSet); + + if (Asn1Tag.TryDecode(source, out Asn1Tag localTag, out int tagLength) && + TryReadLength(source.Slice(tagLength), ruleSet, out int? encodedLength, out int lengthLength)) + { + int headerLength = tagLength + lengthLength; + + LengthValidity validity = ValidateLength( + source.Slice(headerLength), + ruleSet, + localTag, + encodedLength, + out int len, + out int consumed); + + if (validity == LengthValidity.Valid) + { + tag = localTag; + contentOffset = headerLength; + contentLength = len; + bytesConsumed = headerLength + consumed; + return true; + } + } + + tag = default; + contentOffset = contentLength = bytesConsumed = 0; + return false; + } + + /// + /// Locates the contents range for the encoded value at the beginning of the + /// buffer using the specified encoding rules. + /// + /// The buffer containing encoded data. + /// The encoding constraints to use when interpreting the data. + /// + /// When this method returns, the offset of the content payload relative to the start of + /// . + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the number of bytes in the content payload (which may be 0). + /// This parameter is treated as uninitialized. + /// + /// + /// When this method returns, the total number of bytes for the encoded value. + /// This parameter is treated as uninitialized. + /// + /// + /// The tag identifying the content. + /// + /// + /// + /// This method performs very little validation on the contents. + /// If the encoded value uses a definite length, the contents are not inspected at all. + /// If the encoded value uses an indefinite length, the contents are only inspected + /// as necessary to determine the location of the relevant end-of-contents marker. + /// + /// + /// When the encoded value uses an indefinite length, the + /// value will be larger than the sum of and + /// to account for the end-of-contents marker. + /// + /// + /// + /// is not defined. + /// + /// + /// does not represent a value encoded under the specified + /// encoding rules. + /// + public static Asn1Tag ReadEncodedValue( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int contentOffset, + out int contentLength, + out int bytesConsumed) + { + CheckEncodingRules(ruleSet); + + Asn1Tag tag = Asn1Tag.Decode(source, out int tagLength); + int? encodedLength = ReadLength(source.Slice(tagLength), ruleSet, out int lengthLength); + int headerLength = tagLength + lengthLength; + + LengthValidity validity = ValidateLength( + source.Slice(headerLength), + ruleSet, + tag, + encodedLength, + out int len, + out int consumed); + + if (validity == LengthValidity.Valid) + { + contentOffset = headerLength; + contentLength = len; + bytesConsumed = headerLength + consumed; + return tag; + } + + throw GetValidityException(validity); + } + + private static ReadOnlySpan GetPrimitiveContentSpan( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag expectedTag, + UniversalTagNumber tagNumber, + out int bytesConsumed) + { + CheckEncodingRules(ruleSet); + + Asn1Tag localTag = Asn1Tag.Decode(source, out int tagLength); + int? encodedLength = ReadLength(source.Slice(tagLength), ruleSet, out int lengthLength); + int headerLength = tagLength + lengthLength; + + // Get caller(-of-my-caller) errors out of the way, first. + CheckExpectedTag(localTag, expectedTag, tagNumber); + + // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form, + // and the caller says they only want primitive values. + if (localTag.IsConstructed) + { + throw new AsnContentException( + SR.Format(SR.ContentException_PrimitiveEncodingRequired, tagNumber)); + } + + if (encodedLength == null) + { + throw new AsnContentException(); + } + + ReadOnlySpan ret = Slice(source, headerLength, encodedLength.Value); + bytesConsumed = headerLength + ret.Length; + return ret; + } + + private static bool TryReadLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? length, + out int bytesRead) + { + return DecodeLength(source, ruleSet, out length, out bytesRead) == LengthDecodeStatus.Success; + } + + private static int? ReadLength(ReadOnlySpan source, AsnEncodingRules ruleSet, out int bytesConsumed) + { + LengthDecodeStatus status = DecodeLength(source, ruleSet, out int? length, out bytesConsumed); + + switch (status) + { + case LengthDecodeStatus.Success: + return length; + case LengthDecodeStatus.LengthTooBig: + throw new AsnContentException(SR.ContentException_LengthTooBig); + case LengthDecodeStatus.LaxEncodingProhibited: + case LengthDecodeStatus.DerIndefinite: + throw new AsnContentException(SR.ContentException_LengthRuleSetConstraint); + case LengthDecodeStatus.NeedMoreData: + case LengthDecodeStatus.ReservedValue: + throw new AsnContentException(); + default: + Debug.Fail($"No handler is present for status {status}."); + goto case LengthDecodeStatus.NeedMoreData; + } + } + + private static LengthDecodeStatus DecodeLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? length, + out int bytesRead) + { + length = null; + bytesRead = 0; + + AssertEncodingRules(ruleSet); + + if (source.IsEmpty) + { + return LengthDecodeStatus.NeedMoreData; + } + + // T-REC-X.690-201508 sec 8.1.3 + + byte lengthOrLengthLength = source[bytesRead]; + bytesRead++; + const byte MultiByteMarker = 0x80; + + // 0x00-0x7F are direct length values. + // 0x80 is BER/CER indefinite length. + // 0x81-0xFE says that the length takes the next 1-126 bytes. + // 0xFF is forbidden. + if (lengthOrLengthLength == MultiByteMarker) + { + // T-REC-X.690-201508 sec 10.1 (DER: Length forms) + if (ruleSet == AsnEncodingRules.DER) + { + bytesRead = 0; + return LengthDecodeStatus.DerIndefinite; + } + + // Null length == indefinite. + return LengthDecodeStatus.Success; + } + + if (lengthOrLengthLength < MultiByteMarker) + { + length = lengthOrLengthLength; + return LengthDecodeStatus.Success; + } + + if (lengthOrLengthLength == 0xFF) + { + bytesRead = 0; + return LengthDecodeStatus.ReservedValue; + } + + byte lengthLength = (byte)(lengthOrLengthLength & ~MultiByteMarker); + + // +1 for lengthOrLengthLength + if (lengthLength + 1 > source.Length) + { + bytesRead = 0; + return LengthDecodeStatus.NeedMoreData; + } + + // T-REC-X.690-201508 sec 9.1 (CER: Length forms) + // T-REC-X.690-201508 sec 10.1 (DER: Length forms) + bool minimalRepresentation = + ruleSet == AsnEncodingRules.DER || ruleSet == AsnEncodingRules.CER; + + // The ITU-T specifications technically allow lengths up to ((2^128) - 1), but + // since Span's length is a signed Int32 we're limited to identifying memory + // that is within ((2^31) - 1) bytes of the tag start. + if (minimalRepresentation && lengthLength > sizeof(int)) + { + bytesRead = 0; + return LengthDecodeStatus.LengthTooBig; + } + + uint parsedLength = 0; + + for (int i = 0; i < lengthLength; i++) + { + byte current = source[bytesRead]; + bytesRead++; + + if (parsedLength == 0) + { + if (minimalRepresentation && current == 0) + { + bytesRead = 0; + return LengthDecodeStatus.LaxEncodingProhibited; + } + + if (!minimalRepresentation && current != 0) + { + // Under BER rules we could have had padding zeros, so + // once the first data bits come in check that we fit within + // sizeof(int) due to Span bounds. + + if (lengthLength - i > sizeof(int)) + { + bytesRead = 0; + return LengthDecodeStatus.LengthTooBig; + } + } + } + + parsedLength <<= 8; + parsedLength |= current; + } + + // This value cannot be represented as a Span length. + if (parsedLength > int.MaxValue) + { + bytesRead = 0; + return LengthDecodeStatus.LengthTooBig; + } + + if (minimalRepresentation && parsedLength < MultiByteMarker) + { + bytesRead = 0; + return LengthDecodeStatus.LaxEncodingProhibited; + } + + Debug.Assert(bytesRead > 0); + length = (int)parsedLength; + return LengthDecodeStatus.Success; + } + + private static Asn1Tag ReadTagAndLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? contentsLength, + out int bytesRead) + { + Asn1Tag tag = Asn1Tag.Decode(source, out int tagBytesRead); + int? length = ReadLength(source.Slice(tagBytesRead), ruleSet, out int lengthBytesRead); + + int allBytesRead = tagBytesRead + lengthBytesRead; + + if (tag.IsConstructed) + { + // T-REC-X.690-201508 sec 9.1 (CER: Length forms) says constructed is always indefinite. + if (ruleSet == AsnEncodingRules.CER && length != null) + { + throw GetValidityException(LengthValidity.CerRequiresIndefinite); + } + } + else if (length == null) + { + // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form. + throw GetValidityException(LengthValidity.PrimitiveEncodingRequiresDefinite); + } + + bytesRead = allBytesRead; + contentsLength = length; + return tag; + } + + private static void ValidateEndOfContents(Asn1Tag tag, int? length, int headerLength) + { + // T-REC-X.690-201508 sec 8.1.5 excludes the BER 8100 length form for 0. + if (tag.IsConstructed || length != 0 || headerLength != EndOfContentsEncodedLength) + { + throw new AsnContentException(); + } + } + + private static LengthValidity ValidateLength( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + Asn1Tag localTag, + int? encodedLength, + out int actualLength, + out int bytesConsumed) + { + if (localTag.IsConstructed) + { + // T-REC-X.690-201508 sec 9.1 (CER: Length forms) says constructed is always indefinite. + if (ruleSet == AsnEncodingRules.CER && encodedLength != null) + { + actualLength = bytesConsumed = 0; + return LengthValidity.CerRequiresIndefinite; + } + } + else if (encodedLength == null) + { + // T-REC-X.690-201508 sec 8.1.3.2 says primitive encodings must use a definite form. + actualLength = bytesConsumed = 0; + return LengthValidity.PrimitiveEncodingRequiresDefinite; + } + + if (encodedLength != null) + { + int len = encodedLength.Value; + int totalLength = len; + + if (totalLength > source.Length) + { + actualLength = bytesConsumed = 0; + return LengthValidity.LengthExceedsInput; + } + + actualLength = len; + bytesConsumed = len; + return LengthValidity.Valid; + } + + // Assign actualLength first, so no assignments leak if SeekEndOfContents + // throws an exception. + actualLength = SeekEndOfContents(source, ruleSet); + bytesConsumed = actualLength + EndOfContentsEncodedLength; + return LengthValidity.Valid; + } + + private static AsnContentException GetValidityException(LengthValidity validity) + { + Debug.Assert(validity != LengthValidity.Valid); + + switch (validity) + { + case LengthValidity.CerRequiresIndefinite: + return new AsnContentException(SR.ContentException_CerRequiresIndefiniteLength); + case LengthValidity.LengthExceedsInput: + return new AsnContentException(SR.ContentException_LengthExceedsPayload); + case LengthValidity.PrimitiveEncodingRequiresDefinite: + return new AsnContentException(); + default: + Debug.Fail($"No handler for validity {validity}."); + goto case LengthValidity.PrimitiveEncodingRequiresDefinite; + } + } + + /// + /// Get the number of bytes between the start of and + /// the End-of-Contents marker + /// + private static int SeekEndOfContents(ReadOnlySpan source, AsnEncodingRules ruleSet) + { + ReadOnlySpan cur = source; + int totalLen = 0; + + // Our reader is bounded by int.MaxValue. + // The most aggressive data input would be a one-byte tag followed by + // indefinite length "ad infinitum", which would be half the input. + // So the depth marker can never overflow the signed integer space. + int depth = 1; + + while (!cur.IsEmpty) + { + Asn1Tag tag = ReadTagAndLength(cur, ruleSet, out int? length, out int bytesRead); + + if (tag == Asn1Tag.EndOfContents) + { + ValidateEndOfContents(tag, length, bytesRead); + + depth--; + + if (depth == 0) + { + // T-REC-X.690-201508 sec 8.1.1.1 / 8.1.1.3 indicate that the + // End-of-Contents octets are "after" the contents octets, not + // "at the end" of them, so we don't include these bytes in the + // accumulator. + return totalLen; + } + } + + // We found another indefinite length, that means we need to find another + // EndOfContents marker to balance it out. + if (length == null) + { + depth++; + cur = cur.Slice(bytesRead); + totalLen += bytesRead; + } + else + { + // This will throw an AsnContentException if the length exceeds our bounds. + ReadOnlySpan tlv = Slice(cur, 0, bytesRead + length.Value); + + // No exception? Then slice the data and continue. + cur = cur.Slice(tlv.Length); + totalLen += tlv.Length; + } + } + + throw new AsnContentException(); + } + + private static int ParseNonNegativeIntAndSlice(ref ReadOnlySpan data, int bytesToRead) + { + int value = ParseNonNegativeInt(Slice(data, 0, bytesToRead)); + data = data.Slice(bytesToRead); + + return value; + } + + private static int ParseNonNegativeInt(ReadOnlySpan data) + { + if (Utf8Parser.TryParse(data, out uint value, out int consumed) && + value <= int.MaxValue && + consumed == data.Length) + { + return (int)value; + } + + throw new AsnContentException(); + } + + private static ReadOnlySpan SliceAtMost(ReadOnlySpan source, int longestPermitted) + { + int len = Math.Min(longestPermitted, source.Length); + return source.Slice(0, len); + } + + private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int length) + { + Debug.Assert(offset >= 0); + + if (length < 0 || source.Length - offset < length) + { + throw new AsnContentException(SR.ContentException_LengthExceedsPayload); + } + + return source.Slice(offset, length); + } + + private static ReadOnlySpan Slice(ReadOnlySpan source, int offset, int? length) + { + Debug.Assert(offset >= 0); + + if (length == null) + { + return source.Slice(offset); + } + + int lengthVal = length.Value; + + if (lengthVal < 0 || source.Length - offset < lengthVal) + { + throw new AsnContentException(SR.ContentException_LengthExceedsPayload); + } + + return source.Slice(offset, lengthVal); + } + + internal static ReadOnlyMemory Slice(ReadOnlyMemory bigger, ReadOnlySpan smaller) + { + if (smaller.IsEmpty) + { + return default; + } + + if (bigger.Span.Overlaps(smaller, out int offset)) + { + return bigger.Slice(offset, smaller.Length); + } + + Debug.Fail("AsnReader asked for a matching slice from a non-overlapping input"); + throw new AsnContentException(); + } + + [Conditional("DEBUG")] + private static void AssertEncodingRules(AsnEncodingRules ruleSet) + { + Debug.Assert(ruleSet >= AsnEncodingRules.BER && ruleSet <= AsnEncodingRules.DER); + } + + internal static void CheckEncodingRules(AsnEncodingRules ruleSet) + { + if (ruleSet != AsnEncodingRules.BER && + ruleSet != AsnEncodingRules.CER && + ruleSet != AsnEncodingRules.DER) + { + throw new ArgumentOutOfRangeException(nameof(ruleSet)); + } + } + + private static void CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber) + { + if (expectedTag.TagClass == TagClass.Universal && expectedTag.TagValue != (int)tagNumber) + { + throw new ArgumentException( + SR.Argument_UniversalValueIsFixed, + nameof(expectedTag)); + } + + if (expectedTag.TagClass != tag.TagClass || expectedTag.TagValue != tag.TagValue) + { + throw new AsnContentException( + SR.Format( + SR.ContentException_WrongTag, + tag.TagClass, + tag.TagValue, + expectedTag.TagClass, + expectedTag.TagValue)); + } + } + + private enum LengthDecodeStatus + { + NeedMoreData, + DerIndefinite, + ReservedValue, + LengthTooBig, + LaxEncodingProhibited, + Success, + } + + private enum LengthValidity + { + CerRequiresIndefinite, + PrimitiveEncodingRequiresDefinite, + LengthExceedsInput, + Valid, + } + } + + /// + /// A stateful, forward-only reader for BER-, CER-, or DER-encoded ASN.1 data. + /// + public partial class AsnReader + { + internal const int MaxCERSegmentSize = AsnDecoder.MaxCERSegmentSize; + + private ReadOnlyMemory _data; + private readonly AsnReaderOptions _options; + + /// + /// Gets the encoding rules in use by this reader. + /// + /// + /// The encoding rules in use by this reader. + /// + public AsnEncodingRules RuleSet { get; } + + /// + /// Gets an indication of whether the reader has remaining data available to process. + /// + /// + /// if there is more data available for the reader to process; + /// otherwise, . + /// + public bool HasData => !_data.IsEmpty; + + /// + /// Construct an over with a given ruleset. + /// + /// The data to read. + /// The encoding constraints for the reader. + /// Additional options for the reader. + /// + /// This constructor does not evaluate for correctness, + /// any correctness checks are done as part of member methods. + /// + /// This constructor does not copy . The caller is responsible for + /// ensuring that the values do not change until the reader is finished. + /// + /// + /// is not defined. + /// + public AsnReader(ReadOnlyMemory data, AsnEncodingRules ruleSet, AsnReaderOptions options = default) + { + AsnDecoder.CheckEncodingRules(ruleSet); + + _data = data; + RuleSet = ruleSet; + _options = options; + } + + /// + /// Throws a standardized if the reader has remaining + /// data, performs no function if returns . + /// + /// + /// This method provides a standardized target and standardized exception for reading a + /// "closed" structure, such as the nested content for an explicitly tagged value. + /// + public void ThrowIfNotEmpty() + { + if (HasData) + { + throw new AsnContentException(SR.ContentException_TooMuchData); + } + } + + /// + /// Read the encoded tag at the next data position, without advancing the reader. + /// + /// + /// The decoded tag value. + /// + /// + /// a tag could not be decoded at the reader's current position. + /// + public Asn1Tag PeekTag() + { + return Asn1Tag.Decode(_data.Span, out _); + } + + /// + /// Get a view of the next encoded value without + /// advancing the reader. For indefinite length encodings this includes the + /// End of Contents marker. + /// + /// + /// The bytes of the next encoded value. + /// + /// + /// The reader is positioned at a point where the tag or length is invalid + /// under the current encoding rules. + /// + /// + /// + public ReadOnlyMemory PeekEncodedValue() + { + AsnDecoder.ReadEncodedValue(_data.Span, RuleSet, out _, out _, out int bytesConsumed); + return _data.Slice(0, bytesConsumed); + } + + /// + /// Get a view of the content octets (bytes) of the + /// next encoded value without advancing the reader. + /// + /// + /// The bytes of the contents octets of the next encoded value. + /// + /// + /// The reader is positioned at a point where the tag or length is invalid + /// under the current encoding rules. + /// + /// + public ReadOnlyMemory PeekContentBytes() + { + AsnDecoder.ReadEncodedValue( + _data.Span, + RuleSet, + out int contentOffset, + out int contentLength, + out _); + + return _data.Slice(contentOffset, contentLength); + } + + /// + /// Get a view of the next encoded value, + /// and advance the reader past it. For an indefinite length encoding this includes + /// the End of Contents marker. + /// + /// + /// A view of the next encoded value. + /// + /// + public ReadOnlyMemory ReadEncodedValue() + { + ReadOnlyMemory encodedValue = PeekEncodedValue(); + _data = _data.Slice(encodedValue.Length); + return encodedValue; + } + + private AsnReader CloneAtSlice(int start, int length) + { + return new AsnReader(_data.Slice(start, length), RuleSet, _options); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnEncodingRules.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs similarity index 89% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnEncodingRules.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs index 5062840..c0d87eb 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnEncodingRules.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnEncodingRules.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// The encoding ruleset for an or . /// // ITU-T-REC.X.680-201508 sec 4. - internal enum AsnEncodingRules + public enum AsnEncodingRules { /// /// ITU-T X.690 Basic Encoding Rules diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs new file mode 100644 index 0000000..f96b62a --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnReaderOptions.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + /// + /// Specifies options that modify the behavior of an . + /// + public struct AsnReaderOptions + { + private const int DefaultTwoDigitMax = 2049; + + private ushort _twoDigitYearMax; + + /// + /// Gets or sets the largest year to represent with a UtcTime value. + /// + /// The largest year to represent with a UtcTime value. The default is 2049. + public int UtcTimeTwoDigitYearMax + { + get + { + if (_twoDigitYearMax == 0) + { + return DefaultTwoDigitMax; + } + + return _twoDigitYearMax; + } + set + { + if (value < 1 || value > 9999) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _twoDigitYearMax = (ushort)value; + } + } + + /// + /// Gets or sets a value that indicates whether the reader should bypass sort ordering + /// on a Set or Set-Of value. + /// + /// + /// if the reader should not validate that a Set or Set-Of value + /// is sorted correctly for the current encoding rules; otherwise . + /// The default is . + /// + public bool SkipSetSortOrderVerification { get; set; } + } +} diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs new file mode 100644 index 0000000..1be9f1e --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.BitString.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write a Bit String value with a specified tag. + /// + /// The value to write. + /// + /// The number of trailing bits which are not semantic. + /// + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// has length 0 and is not 0 + /// + /// -or- + /// + /// is not empty and any of the bits identified by + /// is set. + /// + /// + /// is not in the range [0,7]. + /// + public void WriteBitString(ReadOnlySpan value, int unusedBitCount = 0, Asn1Tag? tag = null) + { + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + // Primitive or constructed, doesn't matter. + WriteBitStringCore(tag ?? Asn1Tag.PrimitiveBitString, value, unusedBitCount); + } + + // T-REC-X.690-201508 sec 8.6 + private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan bitString, int unusedBitCount) + { + // T-REC-X.690-201508 sec 8.6.2.2 + if (unusedBitCount < 0 || unusedBitCount > 7) + { + throw new ArgumentOutOfRangeException( + nameof(unusedBitCount), + unusedBitCount, + SR.Argument_UnusedBitCountRange); + } + + // T-REC-X.690-201508 sec 8.6.2.3 + if (bitString.Length == 0 && unusedBitCount != 0) + { + throw new ArgumentException(SR.Argument_UnusedBitCountMustBeZero, nameof(unusedBitCount)); + } + + byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1]; + + // T-REC-X.690-201508 sec 11.2 + // + // This could be ignored for BER, but since DER is more common and + // it likely suggests a program error on the caller, leave it enabled for + // BER for now. + if (!CheckValidLastByte(lastByte, unusedBitCount)) + { + throw new ArgumentException(SR.Argument_UnusedBitWasSet, nameof(unusedBitCount)); + } + + if (RuleSet == AsnEncodingRules.CER) + { + // T-REC-X.690-201508 sec 9.2 + // + // If it's not within a primitive segment, use the constructed encoding. + // (>= instead of > because of the unused bit count byte) + if (bitString.Length >= AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerBitString(tag, bitString, unusedBitCount); + return; + } + } + + // Clear the constructed flag, if present. + WriteTag(tag.AsPrimitive()); + // The unused bits byte requires +1. + WriteLength(bitString.Length + 1); + _buffer[_offset] = (byte)unusedBitCount; + _offset++; + bitString.CopyTo(_buffer.AsSpan(_offset)); + _offset += bitString.Length; + } + + private static bool CheckValidLastByte(byte lastByte, int unusedBitCount) + { + // If 3 bits are "unused" then build a mask for them to check for 0. + // 1 << 3 => 0b0000_1000 + // subtract 1 => 0b000_0111 + int mask = (1 << unusedBitCount) - 1; + return ((lastByte & mask) == 0); + } + + private static int DetermineCerBitStringTotalLength(Asn1Tag tag, int contentLength) + { + const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; + // Every segment has an "unused bit count" byte. + const int MaxCERContentSize = MaxCERSegmentSize - 1; + Debug.Assert(contentLength > MaxCERContentSize); + + int fullSegments = Math.DivRem(contentLength, MaxCERContentSize, out int lastContentSize); + + // The tag size is 1 byte. + // The length will always be encoded as 82 03 E8 (3 bytes) + // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) + const int FullSegmentEncodedSize = 1004; + Debug.Assert( + FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); + + int remainingEncodedSize; + + if (lastContentSize == 0) + { + remainingEncodedSize = 0; + } + else + { + // One byte of tag, minimum one byte of length, and one byte of unused bit count. + remainingEncodedSize = 3 + lastContentSize + GetEncodedLengthSubsequentByteCount(lastContentSize); + } + + // Reduce the number of copies by pre-calculating the size. + // +2 for End-Of-Contents + // +1 for 0x80 indefinite length + // +tag length + return fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 3 + tag.CalculateEncodedSize(); + } + + // T-REC-X.690-201508 sec 9.2, 8.6 + private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan payload, int unusedBitCount) + { + const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; + // Every segment has an "unused bit count" byte. + const int MaxCERContentSize = MaxCERSegmentSize - 1; + Debug.Assert(payload.Length > MaxCERContentSize); + + int expectedSize = DetermineCerBitStringTotalLength(tag, payload.Length); + EnsureWriteCapacity(expectedSize); + int savedOffset = _offset; + + WriteTag(tag.AsConstructed()); + // T-REC-X.690-201508 sec 9.1 + // Constructed CER uses the indefinite form. + WriteLength(-1); + + byte[] ensureNoExtraCopy = _buffer; + + ReadOnlySpan remainingData = payload; + Span dest; + Asn1Tag primitiveBitString = Asn1Tag.PrimitiveBitString; + + while (remainingData.Length > MaxCERContentSize) + { + // T-REC-X.690-201508 sec 8.6.4.1 + WriteTag(primitiveBitString); + WriteLength(MaxCERSegmentSize); + // 0 unused bits in this segment. + _buffer[_offset] = 0; + _offset++; + + dest = _buffer.AsSpan(_offset); + remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); + + remainingData = remainingData.Slice(MaxCERContentSize); + _offset += MaxCERContentSize; + } + + WriteTag(primitiveBitString); + WriteLength(remainingData.Length + 1); + + _buffer[_offset] = (byte)unusedBitCount; + _offset++; + + dest = _buffer.AsSpan(_offset); + remainingData.CopyTo(dest); + _offset += remainingData.Length; + + WriteEndOfContents(); + + Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); + Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerBitString)}"); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Boolean.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs similarity index 65% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Boolean.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs index f65a8a7..9197b90 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Boolean.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Boolean.cs @@ -4,36 +4,26 @@ using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Write a Boolean value with tag UNIVERSAL 1. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteBoolean(bool value) - { - WriteBooleanCore(Asn1Tag.Boolean, value); - } - - /// /// Write a Boolean value with a specified tag. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 1). /// /// . is /// , but /// . is not correct for /// the method /// - public void WriteBoolean(Asn1Tag tag, bool value) + public void WriteBoolean(bool value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Boolean); - WriteBooleanCore(tag.AsPrimitive(), value); + WriteBooleanCore(tag?.AsPrimitive() ?? Asn1Tag.Boolean, value); } // T-REC-X.690-201508 sec 11.1, 8.2 diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs new file mode 100644 index 0000000..abf7e77 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Enumerated.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write a non-[] enum value as an Enumerated with + /// tag UNIVERSAL 10. + /// + /// The boxed enumeration value to write. + /// The tag to write, or for the default tag (Universal 10). + /// + /// is . + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not a boxed enum value. + /// + /// -or- + /// + /// the unboxed type of is declared []. + /// + /// + /// + public void WriteEnumeratedValue(Enum value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + WriteEnumeratedValue(tag?.AsPrimitive() ?? Asn1Tag.Enumerated, value.GetType(), value); + } + + /// + /// Write a non-[] enum value as an Enumerated with + /// tag UNIVERSAL 10. + /// + /// The tag to write. + /// The boxed enumeration value to write. + /// + /// is . + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not an enum. + /// + /// -or- + /// + /// is declared []. + /// + /// + public void WriteEnumeratedValue(TEnum value, Asn1Tag? tag = null) where TEnum : Enum + { + WriteEnumeratedValue(tag?.AsPrimitive() ?? Asn1Tag.Enumerated, typeof(TEnum), value); + } + + // T-REC-X.690-201508 sec 8.4 + private void WriteEnumeratedValue(Asn1Tag tag, Type tEnum, object value) + { + CheckUniversalTag(tag, UniversalTagNumber.Enumerated); + + Type backingType = tEnum.GetEnumUnderlyingType(); + + if (tEnum.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_EnumeratedValueRequiresNonFlagsEnum, + nameof(tEnum)); + } + + if (backingType == typeof(ulong)) + { + ulong numericValue = Convert.ToUInt64(value); + // T-REC-X.690-201508 sec 8.4 + WriteNonNegativeIntegerCore(tag, numericValue); + } + else + { + // All other types fit in a (signed) long. + long numericValue = Convert.ToInt64(value); + // T-REC-X.690-201508 sec 8.4 + WriteIntegerCore(tag, numericValue); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.GeneralizedTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs similarity index 75% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.GeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs index d0600d2..4fc3c6e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.GeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.GeneralizedTime.cs @@ -6,55 +6,46 @@ using System.Buffers; 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 !Utf8Formatter.TryFormat(second, baseSpan.Slice(12, 2), out _, d2)) { Debug.Fail($"Utf8Formatter.TryFormat failed to build components of {normalized:O}"); - throw new CryptographicException(); + throw new InvalidOperationException(); } _offset += IntegerPortionLength; diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Integer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs similarity index 65% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Integer.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs index b8f436b..88444bd 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Integer.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Integer.cs @@ -5,167 +5,109 @@ using System.Diagnostics; using System.Numerics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteInteger(long value) - { - WriteIntegerCore(Asn1Tag.Integer, value); - } - - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteInteger(ulong value) - { - WriteNonNegativeIntegerCore(Asn1Tag.Integer, value); - } - - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The value to write. - /// The writer has been Disposed. - public void WriteInteger(BigInteger value) - { - WriteIntegerCore(Asn1Tag.Integer, value); - } - - /// /// Write an Integer value with a specified tag. /// - /// The integer value to write, in signed big-endian byte order. - /// - /// the 9 most sigificant bits are all set --OR-- - /// the 9 most sigificant bits are all unset - /// - /// The writer has been Disposed. - public void WriteInteger(ReadOnlySpan value) - { - WriteIntegerCore(Asn1Tag.Integer, value); - } - - /// - /// Write an Integer value with a specified tag. - /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, long value) + public void WriteInteger(long value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerCore(tag.AsPrimitive(), value); + WriteIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, ulong value) + [CLSCompliant(false)] + public void WriteInteger(ulong value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteNonNegativeIntegerCore(tag.AsPrimitive(), value); + WriteNonNegativeIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The value to write. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, BigInteger value) + public void WriteInteger(BigInteger value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerCore(tag.AsPrimitive(), value); + WriteIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The integer value to write, in signed big-endian byte order. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// - /// the 9 most sigificant bits are all set --OR-- - /// the 9 most sigificant bits are all unset + /// + /// the 9 most significant bits are all set. + /// + /// -or- + /// + /// the 9 most significant bits are all unset. /// - /// The writer has been Disposed. - public void WriteInteger(Asn1Tag tag, ReadOnlySpan value) + public void WriteInteger(ReadOnlySpan value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerCore(tag.AsPrimitive(), value); - } - - /// - /// Write an Integer value with tag UNIVERSAL 2. - /// - /// The integer value to write, in unsigned big-endian byte order. - /// - /// the 9 most sigificant bits are all unset - /// - /// The writer has been Disposed. - public void WriteIntegerUnsigned(ReadOnlySpan value) - { - WriteIntegerUnsignedCore(Asn1Tag.Integer, value); + WriteIntegerCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } /// /// Write an Integer value with a specified tag. /// - /// The tag to write. /// The integer value to write, in unsigned big-endian byte order. + /// The tag to write, or for the default tag (Universal 2). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// - /// the 9 most sigificant bits are all unset + /// + /// the 9 most significant bits are all unset. /// - /// The writer has been Disposed. - public void WriteIntegerUnsigned(Asn1Tag tag, ReadOnlySpan value) + public void WriteIntegerUnsigned(ReadOnlySpan value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Integer); - WriteIntegerUnsignedCore(tag.AsPrimitive(), value); + WriteIntegerUnsignedCore(tag?.AsPrimitive() ?? Asn1Tag.Integer, value); } // T-REC-X.690-201508 sec 8.3 @@ -278,13 +220,13 @@ namespace System.Security.Cryptography.Asn1 { 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 // If the first 9 bits are all 0 or are all 1, the value is invalid. if (masked == 0 || masked == RedundancyMask) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + throw new ArgumentException(SR.Argument_IntegerRedundantByte, nameof(value)); } } diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs new file mode 100644 index 0000000..9008475 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.NamedBitList.cs @@ -0,0 +1,199 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write a [] enum value as a NamedBitList with + /// a specified tag. + /// + /// The boxed enumeration value to write + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not a boxed enum value. + /// + /// -or- + /// + /// the unboxed type of is not declared []. + /// + /// + /// is . + /// + public void WriteNamedBitList(Enum value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + WriteNamedBitList(tag, value.GetType(), value); + } + + /// + /// Write a [] enum value as a NamedBitList with + /// a specified tag. + /// + /// The enumeration value to write + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// -or- + /// + /// is not an enum value. + /// + /// -or- + /// + /// is not declared []. + /// + public void WriteNamedBitList(TEnum value, Asn1Tag? tag = null) where TEnum : Enum + { + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + WriteNamedBitList(tag, typeof(TEnum), value); + } + + /// + /// Write a bit array value as a NamedBitList with a specified tag. + /// + /// The bits to write + /// The tag to write, or for the default tag (Universal 3). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// is . + /// + /// + /// The index of the bit array corresponds to the bit number in the encoded format, which is + /// different than the value produced by with a byte array. + /// For example, the bit array { false, true, true } encodes as 0b0110_0000 with 5 + /// unused bits. + /// + public void WriteNamedBitList(BitArray value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + CheckUniversalTag(tag, UniversalTagNumber.BitString); + + WriteBitArray(value, tag); + } + + private void WriteNamedBitList(Asn1Tag? tag, Type tEnum, Enum value) + { + Type backingType = tEnum.GetEnumUnderlyingType(); + + if (!tEnum.IsDefined(typeof(FlagsAttribute), false)) + { + throw new ArgumentException( + SR.Argument_NamedBitListRequiresFlagsEnum, + nameof(tEnum)); + } + + ulong integralValue; + + if (backingType == typeof(ulong)) + { + integralValue = Convert.ToUInt64(value); + } + else + { + // All other types fit in a (signed) long. + long numericValue = Convert.ToInt64(value); + integralValue = unchecked((ulong)numericValue); + } + + WriteNamedBitList(tag, integralValue); + } + + // T-REC-X.680-201508 sec 22 + // T-REC-X.690-201508 sec 8.6, 11.2.2 + private void WriteNamedBitList(Asn1Tag? tag, ulong integralValue) + { + Span temp = stackalloc byte[sizeof(ulong)]; + // Reset to all zeros, since we're just going to or-in bits we need. + temp.Clear(); + + int indexOfHighestSetBit = -1; + + for (int i = 0; integralValue != 0; integralValue >>= 1, i++) + { + if ((integralValue & 1) != 0) + { + temp[i / 8] |= (byte)(0x80 >> (i % 8)); + indexOfHighestSetBit = i; + } + } + + if (indexOfHighestSetBit < 0) + { + // No bits were set; this is an empty bit string. + // T-REC-X.690-201508 sec 11.2.2-note2 + WriteBitString(ReadOnlySpan.Empty, tag: tag); + } + else + { + // At least one bit was set. + // Determine the shortest length necessary to represent the bit string. + + // Since "bit 0" gets written down 0 => 1. + // Since "bit 8" is in the second byte 8 => 2. + // That makes the formula ((bit / 8) + 1) instead of ((bit + 7) / 8). + int byteLen = (indexOfHighestSetBit / 8) + 1; + int unusedBitCount = 7 - (indexOfHighestSetBit % 8); + + WriteBitString( + temp.Slice(0, byteLen), + unusedBitCount, + tag); + } + } + + private void WriteBitArray(BitArray value, Asn1Tag? tag) + { + if (value.Count == 0) + { + // No bits were set; this is an empty bit string. + // T-REC-X.690-201508 sec 11.2.2-note2 + WriteBitString(ReadOnlySpan.Empty, tag: tag); + return; + } + + int requiredBytes = checked((value.Count + 7) / 8); + int unusedBits = requiredBytes * 8 - value.Count; + byte[] rented = CryptoPool.Rent(requiredBytes); + + // Export the BitArray to a byte array. + // While bits 0-7 are in the first byte, they are numbered 76543210, + // but our wire form is 01234567, so we'll need to reverse the bits on each byte. + value.CopyTo(rented, 0); + + Span valueSpan = rented.AsSpan(0, requiredBytes); + AsnDecoder.ReverseBitsPerByte(valueSpan); + + WriteBitString(valueSpan, unusedBits, tag); + CryptoPool.Return(rented, requiredBytes); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Null.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs similarity index 58% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Null.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs index de2bc92..dbe3670 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Null.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Null.cs @@ -4,35 +4,25 @@ using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Write NULL with tag UNIVERSAL 5. - /// - /// The writer has been Disposed. - public void WriteNull() - { - WriteNullCore(Asn1Tag.Null); - } - - /// /// Write NULL with a specified tag. /// - /// The tag to write. + /// The tag to write, or for the default tag (Universal 5). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteNull(Asn1Tag tag) + public void WriteNull(Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.Null); - WriteNullCore(tag.AsPrimitive()); + WriteNullCore(tag?.AsPrimitive() ?? Asn1Tag.Null); } // T-REC-X.690-201508 sec 8.8 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.OctetString.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs similarity index 58% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.OctetString.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs index 1dc9b0a..e65053b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.OctetString.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.OctetString.cs @@ -4,39 +4,79 @@ using System.Diagnostics; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Write an Octet String with tag UNIVERSAL 4. + /// Begin writing an Octet String value with a specified tag. /// - /// The value to write. - /// The writer has been Disposed. - /// - public void WriteOctetString(ReadOnlySpan octetString) + /// + /// A disposable value which will automatically call . + /// + /// + /// This method is just an accelerator for writing an Octet String value where the + /// contents are also ASN.1 data encoded under the same encoding system. + /// When is called the entire nested contents are + /// normalized as a single Octet String value, encoded correctly for the current encoding + /// rules. + /// This method does not necessarily create a Constructed encoding, and it is not invalid to + /// write values other than Octet String inside this Push/Pop. + /// + /// The tag to write, or for the default tag (Universal 4). + /// + public Scope PushOctetString(Asn1Tag? tag = null) { - WriteOctetString(Asn1Tag.PrimitiveOctetString, octetString); + CheckUniversalTag(tag, UniversalTagNumber.OctetString); + + return PushTag( + tag?.AsConstructed() ?? Asn1Tag.ConstructedOctetString, + UniversalTagNumber.OctetString); + } + + /// + /// Indicate that the open Octet String with the tag UNIVERSAL 4 is closed, + /// returning the writer to the parent context. + /// + /// The tag to write, or for the default tag (Universal 4). + /// + /// In and modes + /// the encoded contents will remain in a single primitive Octet String. + /// In mode the contents will be broken up into + /// multiple segments, when required. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// the writer is not currently positioned within an Octet String with the specified tag. + /// + public void PopOctetString(Asn1Tag? tag = default) + { + CheckUniversalTag(tag, UniversalTagNumber.OctetString); + PopTag(tag?.AsConstructed() ?? Asn1Tag.ConstructedOctetString, UniversalTagNumber.OctetString); } /// /// Write an Octet String value with a specified tag. /// - /// The tag to write. - /// The value to write. + /// The value to write. + /// The tag to write, or for the default tag (Universal 4). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. /// - /// The writer has been Disposed. - public void WriteOctetString(Asn1Tag tag, ReadOnlySpan octetString) + public void WriteOctetString(ReadOnlySpan value, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.OctetString); // Primitive or constructed, doesn't matter. - WriteOctetStringCore(tag, octetString); + WriteOctetStringCore(tag ?? Asn1Tag.PrimitiveOctetString, value); } // T-REC-X.690-201508 sec 8.7 diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs similarity index 55% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Oid.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs index 6363c76..2ce7d54 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.Oid.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Oid.cs @@ -2,169 +2,74 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers; using System.Diagnostics; using System.Numerics; +using System.Security.Cryptography; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// /// Write an Object Identifier with a specified tag. /// - /// The object identifier to write. - /// - /// is null - /// - /// - /// . is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Oid oid) - { - if (oid == null) - throw new ArgumentNullException(nameof(oid)); - - CheckDisposed(); - - if (oid.Value == null) - throw new CryptographicException(SR.Argument_InvalidOidValue); - - WriteObjectIdentifier(oid.Value); - } - - /// - /// Write an Object Identifier with a specified tag. - /// - /// The object identifier to write. - /// - /// is null - /// - /// - /// is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(string oidValue) - { - if (oidValue == null) - throw new ArgumentNullException(nameof(oidValue)); - - WriteObjectIdentifier(oidValue.AsSpan()); - } - - /// - /// Write an Object Identifier with tag UNIVERSAL 6. - /// - /// The object identifier to write. - /// - /// is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(ReadOnlySpan oidValue) - { - WriteObjectIdentifierCore(Asn1Tag.ObjectIdentifier, oidValue); - } - - /// - /// Write an Object Identifier with a specified tag. - /// - /// The tag to write. - /// The object identifier to write. - /// - /// . is - /// , but - /// . is not correct for - /// the method - /// - /// - /// is null - /// - /// - /// . is not a valid dotted decimal - /// object identifier - /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Asn1Tag tag, Oid oid) - { - if (oid == null) - throw new ArgumentNullException(nameof(oid)); - - CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier); - CheckDisposed(); - - if (oid.Value == null) - throw new CryptographicException(SR.Argument_InvalidOidValue); - - WriteObjectIdentifier(tag, oid.Value); - } - - /// - /// Write an Object Identifier with a specified tag. - /// - /// The tag to write. /// The object identifier to write. + /// The tag to write, or for the default tag (Universal 6). /// /// . is /// , but /// . is not correct for - /// the method + /// the method. + /// + /// -or- + /// + /// is not a valid dotted decimal + /// object identifier. /// /// - /// is null - /// - /// - /// is not a valid dotted decimal - /// object identifier + /// is . /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Asn1Tag tag, string oidValue) + public void WriteObjectIdentifier(string oidValue, Asn1Tag? tag = null) { if (oidValue == null) throw new ArgumentNullException(nameof(oidValue)); - WriteObjectIdentifier(tag, oidValue.AsSpan()); + WriteObjectIdentifier(oidValue.AsSpan(), tag); } /// /// Write an Object Identifier with a specified tag. /// - /// The tag to write. /// The object identifier to write. + /// The tag to write, or for the default tag (Universal 6). /// /// . is /// , but /// . is not correct for - /// the method - /// - /// + /// the method. + /// + /// -or- + /// /// is not a valid dotted decimal - /// object identifier + /// object identifier. /// - /// The writer has been Disposed. - public void WriteObjectIdentifier(Asn1Tag tag, ReadOnlySpan oidValue) + public void WriteObjectIdentifier(ReadOnlySpan oidValue, Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier); - WriteObjectIdentifierCore(tag.AsPrimitive(), oidValue); + WriteObjectIdentifierCore(tag?.AsPrimitive() ?? Asn1Tag.ObjectIdentifier, oidValue); } - // T-REC-X.690-201508 sec 8.19 private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan oidValue) { - CheckDisposed(); - // T-REC-X.690-201508 sec 8.19.4 // The first character is in { 0, 1, 2 }, the second will be a '.', and a third (digit) // will also exist. if (oidValue.Length < 3) - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)); if (oidValue[1] != '.') - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)); // The worst case is "1.1.1.1.1", which takes 4 bytes (5 components, with the first two condensed) // Longer numbers get smaller: "2.1.127" is only 2 bytes. (81d (0x51) and 127 (0x7F)) @@ -179,7 +84,7 @@ namespace System.Security.Cryptography.Asn1 '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 @@ namespace System.Security.Cryptography.Asn1 } 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 return c - '0'; } - throw new CryptographicException(SR.Argument_InvalidOidValue); + throw new ArgumentException(SR.Argument_InvalidOidValue, "oidValue"); } // ITU-T-X.690-201508 sec 8.19.5 diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs new file mode 100644 index 0000000..885ea20 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Sequence.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Begin writing a Sequence with a specified tag. + /// + /// The tag to write, or for the default tag (Universal 16). + /// + /// A disposable value which will automatically call . + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + public Scope PushSequence(Asn1Tag? tag = null) + { + CheckUniversalTag(tag, UniversalTagNumber.Sequence); + + // Assert the constructed flag, in case it wasn't. + return PushSequenceCore(tag?.AsConstructed() ?? Asn1Tag.Sequence); + } + + /// + /// Indicate that the open Sequence with the specified tag is closed, + /// returning the writer to the parent context. + /// + /// The tag to write, or for the default tag (Universal 16). + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + /// + /// the writer is not currently positioned within a Sequence with the specified tag. + /// + /// + public void PopSequence(Asn1Tag? tag = null) + { + // PopSequence shouldn't be used to pop a SetOf. + CheckUniversalTag(tag, UniversalTagNumber.Sequence); + + // Assert the constructed flag, in case it wasn't. + PopSequenceCore(tag?.AsConstructed() ?? Asn1Tag.Sequence); + } + + // T-REC-X.690-201508 sec 8.9, 8.10 + private Scope PushSequenceCore(Asn1Tag tag) + { + Debug.Assert(tag.IsConstructed); + return PushTag(tag, UniversalTagNumber.Sequence); + } + + // T-REC-X.690-201508 sec 8.9, 8.10 + private void PopSequenceCore(Asn1Tag tag) + { + Debug.Assert(tag.IsConstructed); + PopTag(tag, UniversalTagNumber.Sequence); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.SetOf.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs similarity index 51% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.SetOf.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs index d1add9c..33f1296 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.SetOf.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.SetOf.cs @@ -2,29 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +using System.Diagnostics; + +namespace System.Formats.Asn1 { - internal sealed partial class AsnWriter + public sealed partial class AsnWriter { /// - /// Begin writing a Set-Of with a tag UNIVERSAL 17. - /// - /// - /// In and modes - /// the writer will sort the Set-Of elements when the tag is closed. - /// - /// The writer has been Disposed. - /// - /// - public void PushSetOf() - { - PushSetOf(Asn1Tag.SetOf); - } - - /// /// Begin writing a Set-Of with a specified tag. /// - /// The tag to write. + /// The tag to write, or for the default tag (Universal 17). + /// + /// A disposable value which will automatically call . + /// /// /// In and modes /// the writer will sort the Set-Of elements when the tag is closed. @@ -33,42 +23,22 @@ namespace System.Security.Cryptography.Asn1 /// . 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 @@ namespace System.Security.Cryptography.Asn1 /// . is /// , but /// . is not correct for - /// the method + /// the method. /// /// - /// the writer is not currently positioned within a Set-Of with the specified tag + /// the writer is not currently positioned within a Set-Of with the specified tag. /// - /// The writer has been Disposed. - /// - public void PopSetOf(Asn1Tag tag) + /// + public void PopSetOf(Asn1Tag? tag = null) { CheckUniversalTag(tag, UniversalTagNumber.SetOf); // Assert the constructed flag, in case it wasn't. - PopSetOfCore(tag.AsConstructed()); + PopSetOfCore(tag?.AsConstructed() ?? Asn1Tag.SetOf); } // T-REC-X.690-201508 sec 8.12 // The writer claims SetOf, and not Set, so as to avoid the field // ordering clause of T-REC-X.690-201508 sec 9.3 - private void PushSetOfCore(Asn1Tag tag) + private Scope PushSetOfCore(Asn1Tag tag) { - PushTag(tag, UniversalTagNumber.SetOf); + Debug.Assert(tag.IsConstructed); + return PushTag(tag, UniversalTagNumber.SetOf); } // T-REC-X.690-201508 sec 8.12 private void PopSetOfCore(Asn1Tag tag) { + Debug.Assert(tag.IsConstructed); + // T-REC-X.690-201508 sec 11.6 bool sortContents = RuleSet == AsnEncodingRules.CER || RuleSet == AsnEncodingRules.DER; diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs new file mode 100644 index 0000000..e791fb4 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.Text.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + public sealed partial class AsnWriter + { + /// + /// Write the provided string using the specified encoding type using the specified + /// tag corresponding to the encoding type. + /// + /// + /// One of the enumeration values representing the encoding to use. + /// + /// The string to write. + /// + /// The tag to write, or for the universal tag that is appropriate to + /// the requested encoding type. + /// + /// is + /// + /// is not a restricted character string encoding type. + /// + /// -or- + /// + /// is a restricted character string encoding type that is not + /// currently supported by this method. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public void WriteCharacterString(UniversalTagNumber encodingType, string value, Asn1Tag? tag = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + WriteCharacterString(encodingType, value.AsSpan(), tag); + } + + /// + /// Write the provided string using the specified encoding type using the specified + /// tag corresponding to the encoding type. + /// + /// + /// One of the enumeration values representing the encoding to use. + /// + /// The string to write. + /// + /// The tag to write, or for the universal tag that is appropriate to + /// the requested encoding type. + /// + /// + /// is not a restricted character string encoding type. + /// + /// -or- + /// + /// is a restricted character string encoding type that is not + /// currently supported by this method. + /// + /// + /// . is + /// , but + /// . is not correct for + /// the method. + /// + public void WriteCharacterString(UniversalTagNumber encodingType, ReadOnlySpan str, Asn1Tag? tag = null) + { + CheckUniversalTag(tag, encodingType); + + Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); + WriteCharacterStringCore(tag ?? new Asn1Tag(encodingType), encoding, str); + } + + // T-REC-X.690-201508 sec 8.23 + private void WriteCharacterStringCore(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str) + { + int size = encoding.GetByteCount(str); + + // T-REC-X.690-201508 sec 9.2 + if (RuleSet == AsnEncodingRules.CER) + { + // If it exceeds the primitive segment size, use the constructed encoding. + if (size > AsnReader.MaxCERSegmentSize) + { + WriteConstructedCerCharacterString(tag, encoding, str, size); + return; + } + } + + // Clear the constructed tag, if present. + WriteTag(tag.AsPrimitive()); + WriteLength(size); + Span dest = _buffer.AsSpan(_offset, size); + + int written = encoding.GetBytes(str, dest); + + if (written != size) + { + Debug.Fail( + $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); + } + + _offset += size; + } + + private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan str, int size) + { + Debug.Assert(size > AsnReader.MaxCERSegmentSize); + + byte[] tmp = CryptoPool.Rent(size); + int written = encoding.GetBytes(str, tmp); + + if (written != size) + { + Debug.Fail( + $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})"); + throw new InvalidOperationException(); + } + + WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size)); + CryptoPool.Return(tmp, size); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.UtcTime.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs similarity index 59% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.UtcTime.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs index 60c1d71..429c41b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.UtcTime.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.UtcTime.cs @@ -6,94 +6,56 @@ using System.Buffers; 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 !Utf8Formatter.TryFormat(second, baseSpan.Slice(10, 2), out _, format)) { Debug.Fail($"Utf8Formatter.TryFormat failed to build components of {normalized:O}"); - throw new CryptographicException(); + throw new InvalidOperationException(); } _buffer[_offset + 12] = (byte)'Z'; diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs similarity index 55% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs index e9c7af3..dcd7ead 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnWriter.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs @@ -6,26 +6,27 @@ // abusing the normal EnsureWriteCapacity + ArrayPool behaviors of rounding up. //#define CHECK_ACCURATE_ENSURE -using System.Buffers; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.InteropServices; +using System.Security.Cryptography; -#nullable enable -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// A writer for BER-, CER-, and DER-encoded ASN.1 data. /// - internal sealed partial class AsnWriter : IDisposable + public sealed partial class AsnWriter { private byte[] _buffer = null!; private int _offset; - private Stack<(Asn1Tag, int, UniversalTagNumber)>? _nestingStack; + private Stack? _nestingStack; /// - /// The in use by this writer. + /// Gets the encoding rules in use by this writer. /// + /// + /// The encoding rules in use by this writer. + /// public AsnEncodingRules RuleSet { get; } /// @@ -48,33 +49,10 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 /// /// 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 /// 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } /// - /// 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 @@ namespace System.Security.Cryptography.Asn1 _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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 // 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 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 @@ namespace System.Security.Cryptography.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Asn1 return value; } } + + private readonly struct StackFrame : IEquatable + { + public Asn1Tag Tag { get; } + public int Offset { get; } + public UniversalTagNumber ItemType { get; } + + internal StackFrame(Asn1Tag tag, int offset, UniversalTagNumber itemType) + { + Tag = tag; + Offset = offset; + ItemType = itemType; + } + + public void Deconstruct(out Asn1Tag tag, out int offset, out UniversalTagNumber itemType) + { + tag = Tag; + offset = Offset; + itemType = ItemType; + } + + public bool Equals(StackFrame other) + { + return Tag.Equals(other.Tag) && Offset == other.Offset && ItemType == other.ItemType; + } + + public override bool Equals(object? obj) => obj is StackFrame other && Equals(other); + + public override int GetHashCode() + { + return (Tag, Offset, ItemType).GetHashCode(); + } + + public static bool operator ==(StackFrame left, StackFrame right) => left.Equals(right); + + public static bool operator !=(StackFrame left, StackFrame right) => !left.Equals(right); + } + + public readonly struct Scope : IDisposable + { + private readonly AsnWriter _writer; + private readonly StackFrame _frame; + private readonly int _depth; + + internal Scope(AsnWriter writer) + { + Debug.Assert(writer._nestingStack != null); + + _writer = writer; + _frame = _writer._nestingStack.Peek(); + _depth = _writer._nestingStack.Count; + } + + public void Dispose() + { + if (_writer == null || _writer._nestingStack.Count == 0) + { + return; + } + + if (_writer._nestingStack.Peek() == _frame) + { + switch (_frame.ItemType) + { + case UniversalTagNumber.SetOf: + _writer.PopSetOf(_frame.Tag); + break; + case UniversalTagNumber.Sequence: + _writer.PopSequence(_frame.Tag); + break; + case UniversalTagNumber.OctetString: + _writer.PopOctetString(_frame.Tag); + break; + default: + Debug.Fail($"No handler for {_frame.ItemType}"); + throw new InvalidOperationException(); + } + } + else if (_writer._nestingStack.Count > _depth && + _writer._nestingStack.Contains(_frame)) + { + // Another frame was pushed when we got disposed. + // Report the imbalance. + throw new InvalidOperationException(SR.AsnWriter_PopWrongTag); + } + } + } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/SetOfValueComparer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs similarity index 91% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/SetOfValueComparer.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs index ad9a9b8..3f33f2b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/SetOfValueComparer.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { internal class SetOfValueComparer : IComparer> { @@ -42,12 +42,7 @@ namespace System.Security.Cryptography.Asn1 // which will make diff != 0. diff = x.Length - y.Length; - if (diff != 0) - { - return diff; - } - - return 0; + return diff; } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/TagClass.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs similarity index 91% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/TagClass.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs index 87b7249..81cb7c8 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/TagClass.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/TagClass.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// The tag class for a particular ASN.1 tag. /// // Uses a masked overlay of the tag class encoding. // T-REC-X.690-201508 sec 8.1.2.2 - internal enum TagClass : byte + public enum TagClass { /// /// The Universal tag class diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/UniversalTagNumber.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs similarity index 98% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/UniversalTagNumber.cs rename to src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs index c5c2ad1..8709f1c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/UniversalTagNumber.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/UniversalTagNumber.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Security.Cryptography.Asn1 +namespace System.Formats.Asn1 { /// /// Tag assignments for the UNIVERSAL class in ITU-T X.680. /// // ITU-T-REC.X.680-201508 sec 8.6 - internal enum UniversalTagNumber + public enum UniversalTagNumber { /// /// The reserved identifier for the End-of-Contents marker in an indefinite diff --git a/src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs b/src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs new file mode 100644 index 0000000..ef723f6 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Asn1TagTests.cs @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests +{ + public sealed class Asn1TagTests + { + [Fact] + public static void Universal15UndefinedFromEnum() + { + AssertExtensions.Throws( + "universalTagNumber", + () => new Asn1Tag((UniversalTagNumber)15)); + } + + [Fact] + public static void Universal15OKFromVerbose() + { + Asn1Tag tag = new Asn1Tag(TagClass.Universal, 15); + Span encoded = stackalloc byte[1]; + + Assert.Equal(1, tag.CalculateEncodedSize()); + tag.Encode(encoded); + + Assert.Equal("0F", encoded.ByteArrayToHex()); + } + + [Theory] + [InlineData(-1)] + [InlineData(-2)] + [InlineData(int.MinValue)] + [InlineData(37)] + [InlineData(38)] + [InlineData(int.MaxValue)] + public static void UniversalValuesLimitedToEnum(int value) + { + AssertExtensions.Throws( + "universalTagNumber", + () => new Asn1Tag((UniversalTagNumber)value)); + } + + [Theory] + [InlineData(-1)] + [InlineData(-2)] + [InlineData(int.MinValue)] + [InlineData(4)] + [InlineData(5)] + [InlineData(int.MaxValue)] + public static void TagClassIsVerified(int value) + { + AssertExtensions.Throws( + "tagClass", + () => new Asn1Tag((TagClass)value, 1)); + } + + [Theory] + [InlineData(TagClass.Universal, -1)] + [InlineData(TagClass.ContextSpecific, -1)] + [InlineData(TagClass.Application, -1)] + [InlineData(TagClass.Private, -1)] + [InlineData(TagClass.Universal, -2)] + [InlineData(TagClass.ContextSpecific, -2)] + [InlineData(TagClass.Application, -2)] + [InlineData(TagClass.Private, -2)] + [InlineData(TagClass.Universal, int.MinValue)] + [InlineData(TagClass.ContextSpecific, int.MinValue)] + [InlineData(TagClass.Application, int.MinValue)] + [InlineData(TagClass.Private, int.MinValue)] + public static void NoNegativeTagNumbers(TagClass tagClass, int value) + { + AssertExtensions.Throws( + "tagValue", + () => new Asn1Tag(tagClass, value)); + } + + [Fact] + public static void EqualsIsExact() + { + Assert.False(Asn1Tag.PrimitiveOctetString.Equals(Asn1Tag.ConstructedOctetString)); + Assert.False(Asn1Tag.PrimitiveOctetString.Equals((object)Asn1Tag.ConstructedOctetString)); + Assert.True(Asn1Tag.PrimitiveOctetString.Equals(Asn1Tag.PrimitiveOctetString)); + Assert.True(Asn1Tag.PrimitiveOctetString.Equals((object)Asn1Tag.PrimitiveOctetString)); + } + + [Fact] + public static void HasSameClassAndValueIsSoft() + { + Assert.True(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(Asn1Tag.ConstructedOctetString)); + Assert.True(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(Asn1Tag.PrimitiveOctetString)); + Assert.False(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(Asn1Tag.PrimitiveBitString)); + Assert.False(Asn1Tag.PrimitiveOctetString.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))); + } + + public static IEnumerable EncodeDecodeCases { get; } = + new[] + { + new object[] { TagClass.Universal, 4, false, "04" }, + new object[] { TagClass.Universal, 4, true, "24" }, + new object[] { TagClass.Application, 4, false, "44" }, + new object[] { TagClass.Application, 4, true, "64" }, + new object[] { TagClass.ContextSpecific, 4, false, "84" }, + new object[] { TagClass.ContextSpecific, 4, true, "A4" }, + new object[] { TagClass.Private, 4, false, "C4" }, + new object[] { TagClass.Private, 4, true, "E4" }, + new object[] { TagClass.Universal, 30, false, "1E" }, + new object[] { TagClass.Universal, 30, true, "3E" }, + new object[] { TagClass.Application, 30, false, "5E" }, + new object[] { TagClass.Application, 30, true, "7E" }, + new object[] { TagClass.ContextSpecific, 30, false, "9E" }, + new object[] { TagClass.ContextSpecific, 30, true, "BE" }, + new object[] { TagClass.Private, 30, false, "DE" }, + new object[] { TagClass.Private, 30, true, "FE" }, + new object[] { TagClass.Universal, 31, false, "1F1F" }, + new object[] { TagClass.Universal, 31, true, "3F1F" }, + new object[] { TagClass.Application, 31, false, "5F1F" }, + new object[] { TagClass.Application, 31, true, "7F1F" }, + new object[] { TagClass.ContextSpecific, 31, false, "9F1F" }, + new object[] { TagClass.ContextSpecific, 31, true, "BF1F" }, + new object[] { TagClass.Private, 31, false, "DF1F" }, + new object[] { TagClass.Private, 31, true, "FF1F" }, + new object[] { TagClass.Universal, 127, false, "1F7F" }, + new object[] { TagClass.Universal, 127, true, "3F7F" }, + new object[] { TagClass.Application, 127, false, "5F7F" }, + new object[] { TagClass.Application, 127, true, "7F7F" }, + new object[] { TagClass.ContextSpecific, 127, false, "9F7F" }, + new object[] { TagClass.ContextSpecific, 127, true, "BF7F" }, + new object[] { TagClass.Private, 127, false, "DF7F" }, + new object[] { TagClass.Private, 127, true, "FF7F" }, + new object[] { TagClass.Universal, 128, false, "1F8100" }, + new object[] { TagClass.Universal, 128, true, "3F8100" }, + new object[] { TagClass.Application, 128, false, "5F8100" }, + new object[] { TagClass.Application, 128, true, "7F8100" }, + new object[] { TagClass.ContextSpecific, 128, false, "9F8100" }, + new object[] { TagClass.ContextSpecific, 128, true, "BF8100" }, + new object[] { TagClass.Private, 128, false, "DF8100" }, + new object[] { TagClass.Private, 128, true, "FF8100" }, + new object[] { TagClass.Universal, 16383, false, "1FFF7F" }, + new object[] { TagClass.Universal, 16383, true, "3FFF7F" }, + new object[] { TagClass.Application, 16383, false, "5FFF7F" }, + new object[] { TagClass.Application, 16383, true, "7FFF7F" }, + new object[] { TagClass.ContextSpecific, 16383, false, "9FFF7F" }, + new object[] { TagClass.ContextSpecific, 16383, true, "BFFF7F" }, + new object[] { TagClass.Private, 16383, false, "DFFF7F" }, + new object[] { TagClass.Private, 16383, true, "FFFF7F" }, + new object[] { TagClass.Universal, 16384, false, "1F818000" }, + new object[] { TagClass.Universal, 16384, true, "3F818000" }, + new object[] { TagClass.Application, 16384, false, "5F818000" }, + new object[] { TagClass.Application, 16384, true, "7F818000" }, + new object[] { TagClass.ContextSpecific, 16384, false, "9F818000" }, + new object[] { TagClass.ContextSpecific, 16384, true, "BF818000" }, + new object[] { TagClass.Private, 16384, false, "DF818000" }, + new object[] { TagClass.Private, 16384, true, "FF818000" }, + new object[] { TagClass.Universal, int.MaxValue, false, "1F87FFFFFF7F" }, + new object[] { TagClass.Universal, int.MaxValue, true, "3F87FFFFFF7F" }, + new object[] { TagClass.Application, int.MaxValue, false, "5F87FFFFFF7F" }, + new object[] { TagClass.Application, int.MaxValue, true, "7F87FFFFFF7F" }, + new object[] { TagClass.ContextSpecific, int.MaxValue, false, "9F87FFFFFF7F" }, + new object[] { TagClass.ContextSpecific, int.MaxValue, true, "BF87FFFFFF7F" }, + new object[] { TagClass.Private, int.MaxValue, false, "DF87FFFFFF7F" }, + new object[] { TagClass.Private, int.MaxValue, true, "FF87FFFFFF7F" }, + }; + + [Theory] + [MemberData(nameof(EncodeDecodeCases))] + public static void VerifyEncode(TagClass tagClass, int tagValue, bool constructed, string expectedHex) + { + Asn1Tag tag = new Asn1Tag(tagClass, tagValue, constructed); + Span buf = stackalloc byte[10]; + + Assert.False(tag.TryEncode(Span.Empty, out int written)); + Assert.Equal(0, written); + + int expectedSize = expectedHex.Length / 2; + Assert.Equal(expectedSize, tag.CalculateEncodedSize()); + + Assert.False(tag.TryEncode(buf.Slice(0, expectedSize - 1), out written)); + Assert.Equal(0, written); + + AssertExtensions.Throws( + "destination", + () => + { + Span tmp = stackalloc byte[expectedSize - 1]; + return tag.Encode(tmp); + }); + + Assert.True(tag.TryEncode(buf, out written)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(0, written).ByteArrayToHex()); + + written = tag.Encode(buf.Slice(1)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(1, written).ByteArrayToHex()); + + Assert.True(tag.TryEncode(buf.Slice(0, expectedSize), out written)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(0, written).ByteArrayToHex()); + + written = tag.Encode(buf.Slice(1, expectedSize)); + Assert.Equal(expectedSize, written); + Assert.Equal(expectedHex, buf.Slice(1, written).ByteArrayToHex()); + } + + [Theory] + [MemberData(nameof(EncodeDecodeCases))] + public static void VerifyDecode(TagClass tagClass, int tagValue, bool constructed, string inputHex) + { + Asn1Tag expectedTag = new Asn1Tag(tagClass, tagValue, constructed); + byte[] input = inputHex.HexToByteArray(); + byte[] padded = input; + Array.Resize(ref padded, input.Length + 3); + + int consumed; + Asn1Tag tag; + + Assert.False(Asn1Tag.TryDecode(input.AsSpan(0, input.Length - 1), out tag, out consumed)); + Assert.Equal(0, consumed); + Assert.Equal(default(Asn1Tag), tag); + + Assert.Throws(() => Asn1Tag.Decode(input.AsSpan(0, input.Length - 1), out consumed)); + Assert.Equal(0, consumed); + + Assert.True(Asn1Tag.TryDecode(padded, out tag, out consumed)); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + + Assert.True(Asn1Tag.TryDecode(input, out tag, out consumed)); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + + tag = Asn1Tag.Decode(padded, out consumed); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + + tag = Asn1Tag.Decode(input, out consumed); + Assert.Equal(input.Length, consumed); + Assert.Equal(expectedTag, tag); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs similarity index 93% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs index 2291e69..7640b13 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ComprehensiveReadTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ComprehensiveReadTests.cs @@ -4,13 +4,11 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; +using X509KeyUsageCSharpStyle=System.Formats.Asn1.Tests.Reader.ReadNamedBitList.X509KeyUsageCSharpStyle; -using X509KeyUsageCSharpStyle=System.Security.Cryptography.Tests.Asn1.ReadNamedBitList.X509KeyUsageCSharpStyle; - -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { public static class ComprehensiveReadTests { @@ -27,7 +25,7 @@ namespace System.Security.Cryptography.Tests.Asn1 AsnReader sigAlgReader = certReader.ReadSequence(); Assert.True( - certReader.TryReadPrimitiveBitStringValue( + certReader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory signature), "certReader.TryReadPrimitiveBitStringValue"); @@ -46,7 +44,7 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 { 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 0000000..ccc3693 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/OverlappedReads.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public static class OverlappedReads + { + private delegate bool TryWriteMethod( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int bytesConsumed, + out int bytesWritten); + + [Fact] + public static void NoOverlappedBitStrings() + { + static bool Method( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int consumed, + out int written) + { + bool ret = AsnDecoder.TryReadBitString( + source, + destination, + ruleSet, + out int unusedBitCount, + out consumed, + out written); + + if (ret) + { + Assert.Equal(2, unusedBitCount); + } + + return ret; + } + + byte[] input = { 0x00, 0x00, 0x03, 0x02, 0x02, 0x3C, 0x00 }; + + NoOverlappedReads( + input, + encodedValueOffset: 2, + encodedValueLength: 4, + copyLength: 1, + Method); + } + + [Fact] + public static void NoOverlappedOctetStrings() + { + static bool Method( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int consumed, + out int written) + { + return AsnDecoder.TryReadOctetString( + source, + destination, + ruleSet, + out consumed, + out written); + } + + byte[] input = { 0x00, 0x00, 0x04, 0x01, 0x21, 0x00 }; + + NoOverlappedReads( + input, + encodedValueOffset: 2, + encodedValueLength: 3, + copyLength: 1, + Method); + } + + [Fact] + public static void NoOverlappedTextStrings() + { + static bool Method( + ReadOnlySpan source, + Span destination, + AsnEncodingRules ruleSet, + out int consumed, + out int written) + { + return AsnDecoder.TryReadCharacterStringBytes( + source, + destination, + ruleSet, + new Asn1Tag(UniversalTagNumber.UTF8String), + out consumed, + out written); + } + + byte[] input = { 0x00, 0x00, 0x0C, 0x01, 0x30, 0x00 }; + + NoOverlappedReads( + input, + encodedValueOffset: 2, + encodedValueLength: 3, + copyLength: 1, + Method); + } + + private static void NoOverlappedReads( + byte[] input, + int encodedValueOffset, + int encodedValueLength, + int copyLength, + TryWriteMethod tryWriteMethod) + { + // The write starts beyond the portion of source that will be read, + // but that hasn't yet been determined. + AssertExtensions.Throws( + "destination", + () => tryWriteMethod( + input.AsSpan(encodedValueOffset), + input.AsSpan(encodedValueOffset + encodedValueLength), + AsnEncodingRules.BER, + out _, + out _)); + + // The CopyTo would actually end up with source == dest for this one + AssertExtensions.Throws( + "destination", + () => tryWriteMethod( + input.AsSpan(encodedValueOffset), + input.AsSpan(encodedValueOffset + encodedValueLength - copyLength, copyLength), + AsnEncodingRules.BER, + out _, + out _)); + + // destination[1] is source[0], but there isn't actually an overwrite because + // the value length isn't long enough. + AssertExtensions.Throws( + "destination", + () => tryWriteMethod( + input.AsSpan(encodedValueOffset), + input.AsSpan(encodedValueOffset - copyLength, copyLength + 1), + AsnEncodingRules.BER, + out _, + out _)); + + Assert.True( + tryWriteMethod( + input.AsSpan(encodedValueOffset, encodedValueLength), + input.AsSpan(encodedValueOffset + encodedValueLength, copyLength), + AsnEncodingRules.BER, + out int bytesConsumed, + out int bytesWritten)); + + Assert.Equal(encodedValueLength, bytesConsumed); + Assert.Equal(copyLength, bytesWritten); + Assert.Equal( + input[encodedValueOffset + encodedValueLength - copyLength], + input[encodedValueOffset + encodedValueLength]); + + Assert.True( + tryWriteMethod( + input.AsSpan(encodedValueOffset, encodedValueLength), + input.AsSpan(0, copyLength), + AsnEncodingRules.BER, + out bytesConsumed, + out bytesWritten)); + + Assert.Equal(encodedValueLength, bytesConsumed); + Assert.Equal(copyLength, bytesWritten); + Assert.Equal(input[encodedValueOffset + encodedValueLength - copyLength], input[0]); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs similarity index 58% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs index 15e5c69..5042a4c 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ParseTag.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ParseTag.cs @@ -2,49 +2,48 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ParseTag : Asn1ReaderTests + public sealed class ParseTag { [Theory] - [InlineData(PublicTagClass.Universal, false, 0, "00")] - [InlineData(PublicTagClass.Universal, false, 1, "01")] - [InlineData(PublicTagClass.Application, true, 1, "61")] - [InlineData(PublicTagClass.ContextSpecific, false, 1, "81")] - [InlineData(PublicTagClass.ContextSpecific, true, 1, "A1")] - [InlineData(PublicTagClass.Private, false, 1, "C1")] - [InlineData(PublicTagClass.Universal, false, 30, "1E")] - [InlineData(PublicTagClass.Application, false, 30, "5E")] - [InlineData(PublicTagClass.ContextSpecific, false, 30, "9E")] - [InlineData(PublicTagClass.Private, false, 30, "DE")] - [InlineData(PublicTagClass.Universal, false, 31, "1F1F")] - [InlineData(PublicTagClass.Application, false, 31, "5F1F")] - [InlineData(PublicTagClass.ContextSpecific, false, 31, "9F1F")] - [InlineData(PublicTagClass.Private, false, 31, "DF1F")] - [InlineData(PublicTagClass.Private, false, 127, "DF7F")] - [InlineData(PublicTagClass.Private, false, 128, "DF8100")] - [InlineData(PublicTagClass.Private, false, 253, "DF817D")] - [InlineData(PublicTagClass.Private, false, 255, "DF817F")] - [InlineData(PublicTagClass.Private, false, 256, "DF8200")] - [InlineData(PublicTagClass.Private, false, 1 << 9, "DF8400")] - [InlineData(PublicTagClass.Private, false, 1 << 10, "DF8800")] - [InlineData(PublicTagClass.Private, false, 0b0011_1101_1110_0111, "DFFB67")] - [InlineData(PublicTagClass.Private, false, 1 << 14, "DF818000")] - [InlineData(PublicTagClass.Private, false, 1 << 18, "DF908000")] - [InlineData(PublicTagClass.Private, false, 1 << 18 | 1 << 9, "DF908400")] - [InlineData(PublicTagClass.Private, false, 1 << 20, "DFC08000")] - [InlineData(PublicTagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] - [InlineData(PublicTagClass.Private, false, 1 << 21, "DF81808000")] - [InlineData(PublicTagClass.Private, false, 1 << 27, "DFC0808000")] - [InlineData(PublicTagClass.Private, false, 1 << 28, "DF8180808000")] - [InlineData(PublicTagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] - [InlineData(PublicTagClass.Universal, false, 119, "1F77")] + [InlineData(TagClass.Universal, false, 0, "00")] + [InlineData(TagClass.Universal, false, 1, "01")] + [InlineData(TagClass.Application, true, 1, "61")] + [InlineData(TagClass.ContextSpecific, false, 1, "81")] + [InlineData(TagClass.ContextSpecific, true, 1, "A1")] + [InlineData(TagClass.Private, false, 1, "C1")] + [InlineData(TagClass.Universal, false, 30, "1E")] + [InlineData(TagClass.Application, false, 30, "5E")] + [InlineData(TagClass.ContextSpecific, false, 30, "9E")] + [InlineData(TagClass.Private, false, 30, "DE")] + [InlineData(TagClass.Universal, false, 31, "1F1F")] + [InlineData(TagClass.Application, false, 31, "5F1F")] + [InlineData(TagClass.ContextSpecific, false, 31, "9F1F")] + [InlineData(TagClass.Private, false, 31, "DF1F")] + [InlineData(TagClass.Private, false, 127, "DF7F")] + [InlineData(TagClass.Private, false, 128, "DF8100")] + [InlineData(TagClass.Private, false, 253, "DF817D")] + [InlineData(TagClass.Private, false, 255, "DF817F")] + [InlineData(TagClass.Private, false, 256, "DF8200")] + [InlineData(TagClass.Private, false, 1 << 9, "DF8400")] + [InlineData(TagClass.Private, false, 1 << 10, "DF8800")] + [InlineData(TagClass.Private, false, 0b0011_1101_1110_0111, "DFFB67")] + [InlineData(TagClass.Private, false, 1 << 14, "DF818000")] + [InlineData(TagClass.Private, false, 1 << 18, "DF908000")] + [InlineData(TagClass.Private, false, 1 << 18 | 1 << 9, "DF908400")] + [InlineData(TagClass.Private, false, 1 << 20, "DFC08000")] + [InlineData(TagClass.Private, false, 0b0001_1110_1010_0111_0000_0001, "DFFACE01")] + [InlineData(TagClass.Private, false, 1 << 21, "DF81808000")] + [InlineData(TagClass.Private, false, 1 << 27, "DFC0808000")] + [InlineData(TagClass.Private, false, 1 << 28, "DF8180808000")] + [InlineData(TagClass.Private, true, int.MaxValue, "FF87FFFFFF7F")] + [InlineData(TagClass.Universal, false, 119, "1F77")] public static void ParseValidTag( - PublicTagClass tagClass, + TagClass tagClass, bool isConstructed, int tagValue, string inputHex) @@ -55,7 +54,7 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 Assert.True(parsed, "Asn1Tag.TryDecode"); Assert.Equal(inputHex.Length / 2, bytesRead); - Assert.Equal((TagClass)tagClass, tag.TagClass); + Assert.Equal(tagClass, tag.TagClass); Assert.Equal(tagValue, tag.TagValue); if (isConstructed) diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs similarity index 84% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs index 1ed4370..4ccb24c 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/PeekTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/PeekTests.cs @@ -4,13 +4,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class PeekTests : Asn1ReaderTests + public sealed class PeekTests { [Fact] public static void ReaderPeekTag_Valid() @@ -32,14 +31,7 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 byte[] data = (EncodedValue + "0500").HexToByteArray(); - Assert.Throws( + Assert.Throws( () => { AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); @@ -128,7 +120,7 @@ namespace System.Security.Cryptography.Tests.Asn1 byte[] data = (EncodedValue + "0500").HexToByteArray(); - Assert.Throws( + Assert.Throws( () => { AsnReader reader = new AsnReader(data, AsnEncodingRules.BER); @@ -209,6 +201,20 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 AsnReader reader = new AsnReader(badLength, AsnEncodingRules.BER); - Assert.Throws(() => reader.PeekContentBytes()); - Assert.Throws( - () => - { - AsnValueReader valueReader = new AsnValueReader(badLength, AsnEncodingRules.BER); - valueReader.PeekContentBytes(); - }); + Assert.Throws(() => reader.PeekContentBytes()); } } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs similarity index 57% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs index 48afbb1..751802f 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBMPString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBMPString.cs @@ -5,80 +5,79 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadBMPString : Asn1ReaderTests + public sealed class ReadBMPString { public static IEnumerable ValidEncodingData { get; } = new object[][] { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1E1A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1E1A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1E1A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E80" + "041A004A006F0068006E00200051002E00200053006D006900740068" + "0000", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E1C" + "041A004A006F0068006E00200051002E00200053006D006900740068", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1E00", "", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1E00", "", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1E00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E80" + "0000", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3E80" + "2480" + // "Dr." @@ -123,12 +122,12 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyBMPStringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedString) { @@ -182,7 +181,7 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 out ReadOnlyMemory contents) { return reader.TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber.BMPString, + new Asn1Tag(UniversalTagNumber.BMPString), out contents); } @@ -731,7 +730,6 @@ namespace System.Security.Cryptography.Tests.Asn1 { return reader.TryReadPrimitiveCharacterStringBytes( expectedTag, - UniversalTagNumber.BMPString, out contents); } @@ -740,9 +738,9 @@ namespace System.Security.Cryptography.Tests.Asn1 Span destination, out int bytesWritten) { - return reader.TryCopyCharacterStringBytes( - UniversalTagNumber.BMPString, + return reader.TryReadCharacterStringBytes( destination, + new Asn1Tag(UniversalTagNumber.BMPString), out bytesWritten); } @@ -751,9 +749,9 @@ namespace System.Security.Cryptography.Tests.Asn1 Span destination, out int charsWritten) { - return reader.TryCopyCharacterString( - UniversalTagNumber.BMPString, + return reader.TryReadCharacterString( destination, + UniversalTagNumber.BMPString, out charsWritten); } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs similarity index 50% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs index 6282bbd..4366748 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBitString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBitString.cs @@ -4,31 +4,30 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadBitString : Asn1ReaderTests + public static class ReadBitString { [Theory] - [InlineData("Uncleared unused bit", PublicEncodingRules.BER, "030201FF")] - [InlineData("Constructed Payload", PublicEncodingRules.BER, "2302030100")] - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.BER, "238003010000")] + [InlineData("Uncleared unused bit", AsnEncodingRules.BER, "030201FF")] + [InlineData("Constructed Payload", AsnEncodingRules.BER, "2302030100")] + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.BER, "238003010000")] // This value is actually invalid CER, but it returns false since it's not primitive and // it isn't worth preempting the descent to find out it was invalid. - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.CER, "238003010000")] - public static void TryGetBitStringBytes_Fails( + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.CER, "238003010000")] + public static void TryReadPrimitiveBitStringValue_Fails( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveBitStringValue( + bool didRead = reader.TryReadPrimitiveBitString( out int unusedBitCount, out ReadOnlyMemory contents); @@ -38,21 +37,21 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 "00", 0)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "230C" + "2380" + "2380" + @@ -212,7 +235,7 @@ namespace System.Security.Cryptography.Tests.Asn1 "00", 0)] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2380" + "2308" + "030200FA" + @@ -233,104 +256,113 @@ namespace System.Security.Cryptography.Tests.Asn1 "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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 int unusedBitCount; Assert.True( - reader.TryCopyBitStringBytes(Span.Empty, out unusedBitCount, out bytesWritten)); + reader.TryReadBitString(Span.Empty, out unusedBitCount, out bytesWritten)); Assert.Equal(0, bytesWritten); Assert.Equal(0, unusedBitCount); + + reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + byte[] output = reader.ReadBitString(out unusedBitCount); + Assert.Equal(0, unusedBitCount); + + // It's Same (ReferenceEqual) on .NET Core, but just Equal on .NET Framework + Assert.Equal(Array.Empty(), output); } } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs new file mode 100644 index 0000000..75eab9e --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadBoolean.cs @@ -0,0 +1,226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadBoolean + { + [Theory] + [InlineData(AsnEncodingRules.BER, false, 3, "010100")] + [InlineData(AsnEncodingRules.BER, true, 3, "010101")] + // Padded length + [InlineData(AsnEncodingRules.BER, true, 4, "01810101")] + [InlineData(AsnEncodingRules.BER, true, 3, "0101FF0500")] + [InlineData(AsnEncodingRules.CER, false, 3, "0101000500")] + [InlineData(AsnEncodingRules.CER, true, 3, "0101FF")] + [InlineData(AsnEncodingRules.DER, false, 3, "010100")] + [InlineData(AsnEncodingRules.DER, true, 3, "0101FF0500")] + // Context Specific 0 + [InlineData(AsnEncodingRules.DER, true, 3, "8001FF0500")] + // Application 31 + [InlineData(AsnEncodingRules.DER, true, 4, "5F1F01FF0500")] + // Private 253 + [InlineData(AsnEncodingRules.CER, false, 5, "DF817D01000500")] + public static void ReadBoolean_Success( + AsnEncodingRules ruleSet, + bool expectedValue, + int expectedBytesRead, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Asn1Tag tag = reader.PeekTag(); + bool value; + + if (tag.TagClass == TagClass.Universal) + { + value = reader.ReadBoolean(); + } + else + { + value = reader.ReadBoolean(tag); + } + + if (inputData.Length == expectedBytesRead) + { + Assert.False(reader.HasData, "reader.HasData"); + } + else + { + Assert.True(reader.HasData, "reader.HasData"); + } + + if (expectedValue) + { + Assert.True(value, "value"); + } + else + { + Assert.False(value, "value"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 1, 1, 0 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBoolean(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + bool value = reader.ReadBoolean(); + Assert.False(value, "value"); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x80, 1, 0xFF }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadBoolean(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadBoolean()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + bool value = reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0)); + Assert.True(value, "value"); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0101FF", TagClass.Universal, 1)] + [InlineData(AsnEncodingRules.CER, "0101FF", TagClass.Universal, 1)] + [InlineData(AsnEncodingRules.DER, "0101FF", TagClass.Universal, 1)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + bool val1 = reader.ReadBoolean(new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, ruleSet); + bool val2 = reader.ReadBoolean(new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + + [Theory] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("TagOnly", AsnEncodingRules.BER, "01")] + [InlineData("TagOnly", AsnEncodingRules.CER, "01")] + [InlineData("TagOnly", AsnEncodingRules.DER, "01")] + [InlineData("MultiByte TagOnly", AsnEncodingRules.DER, "9F1F")] + [InlineData("MultiByte TagOnly", AsnEncodingRules.CER, "9F1F")] + [InlineData("MultiByte TagOnly", AsnEncodingRules.BER, "9F1F")] + [InlineData("TagAndLength", AsnEncodingRules.BER, "0101")] + [InlineData("Tag and MultiByteLength", AsnEncodingRules.BER, "01820001")] + [InlineData("TagAndLength", AsnEncodingRules.CER, "8001")] + [InlineData("TagAndLength", AsnEncodingRules.DER, "C001")] + [InlineData("MultiByteTagAndLength", AsnEncodingRules.DER, "9F2001")] + [InlineData("MultiByteTagAndLength", AsnEncodingRules.CER, "9F2001")] + [InlineData("MultiByteTagAndLength", AsnEncodingRules.BER, "9F2001")] + [InlineData("MultiByteTagAndMultiByteLength", AsnEncodingRules.BER, "9F28200001")] + [InlineData("TooShort", AsnEncodingRules.BER, "0100")] + [InlineData("TooShort", AsnEncodingRules.CER, "8000")] + [InlineData("TooShort", AsnEncodingRules.DER, "0100")] + [InlineData("TooLong", AsnEncodingRules.DER, "C0020000")] + [InlineData("TooLong", AsnEncodingRules.CER, "01020000")] + [InlineData("TooLong", AsnEncodingRules.BER, "C081020000")] + [InlineData("MissingContents", AsnEncodingRules.BER, "C001")] + [InlineData("MissingContents", AsnEncodingRules.CER, "0101")] + [InlineData("MissingContents", AsnEncodingRules.DER, "8001")] + [InlineData("NonCanonical", AsnEncodingRules.DER, "0101FE")] + [InlineData("NonCanonical", AsnEncodingRules.CER, "800101")] + [InlineData("Constructed", AsnEncodingRules.BER, "2103010101")] + [InlineData("Constructed", AsnEncodingRules.CER, "2103010101")] + [InlineData("Constructed", AsnEncodingRules.DER, "2103010101")] + [InlineData("WrongTag", AsnEncodingRules.DER, "0400")] + [InlineData("WrongTag", AsnEncodingRules.CER, "0400")] + [InlineData("WrongTag", AsnEncodingRules.BER, "0400")] + [InlineData("IndefiniteLength", AsnEncodingRules.BER, "01800101FF00")] + [InlineData("IndefiniteLength", AsnEncodingRules.CER, "01800101FF00")] + [InlineData("IndefiniteLength", AsnEncodingRules.DER, "01800101FF00")] + public static void ReadBoolean_Failure( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + Asn1Tag tag = default(Asn1Tag); + + if (inputData.Length > 0) + { + tag = reader.PeekTag(); + } + + if (tag.TagClass == TagClass.Universal) + { + Assert.Throws(() => reader.ReadBoolean()); + } + else + { + Assert.Throws(() => reader.ReadBoolean(tag)); + } + + if (inputData.Length == 0) + { + // If we started with nothing, where did the data come from? + Assert.False(reader.HasData, "reader.HasData"); + } + else + { + // Nothing should have moved + Assert.True(reader.HasData, "reader.HasData"); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs new file mode 100644 index 0000000..d19fbe6 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadEnumerated.cs @@ -0,0 +1,802 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadEnumerated + { + public enum ByteBacked : byte + { + Zero = 0, + NotFluffy = 11, + Fluff = 12, + } + + public enum SByteBacked : sbyte + { + Zero = 0, + Fluff = 83, + Pillow = -17, + } + + public enum ShortBacked : short + { + Zero = 0, + Fluff = 521, + Pillow = -1024, + } + + public enum UShortBacked : ushort + { + Zero = 0, + Fluff = 32768, + } + + public enum IntBacked : int + { + Zero = 0, + Fluff = 0x010001, + Pillow = -Fluff, + } + + public enum UIntBacked : uint + { + Zero = 0, + Fluff = 0x80000005, + } + + public enum LongBacked : long + { + Zero = 0, + Fluff = 0x0200000441, + Pillow = -0x100000000L, + } + + public enum ULongBacked : ulong + { + Zero = 0, + Fluff = 0xFACEF00DCAFEBEEF, + } + + private static void GetExpectedValue( + AsnEncodingRules ruleSet, + TEnum expectedValue, + string inputHex) + where TEnum : Enum + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + TEnum value = reader.ReadEnumeratedValue(); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, ByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, ByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, ByteBacked.Fluff, "0A010C")] + [InlineData(AsnEncodingRules.CER, ByteBacked.Fluff, "0A010C")] + [InlineData(AsnEncodingRules.DER, ByteBacked.Fluff, "0A010C")] + [InlineData(AsnEncodingRules.BER, (ByteBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (ByteBacked)128, "0A020080")] + [InlineData(AsnEncodingRules.DER, (ByteBacked)129, "0A020081")] + [InlineData(AsnEncodingRules.BER, (ByteBacked)254, "0A82000200FE")] + public static void GetExpectedValue_ByteBacked( + AsnEncodingRules ruleSet, + ByteBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, SByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, SByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, SByteBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, SByteBacked.Fluff, "0A0153")] + [InlineData(AsnEncodingRules.CER, SByteBacked.Fluff, "0A0153")] + [InlineData(AsnEncodingRules.DER, SByteBacked.Fluff, "0A0153")] + [InlineData(AsnEncodingRules.BER, SByteBacked.Pillow, "0A01EF")] + [InlineData(AsnEncodingRules.CER, (SByteBacked)sbyte.MinValue, "0A0180")] + [InlineData(AsnEncodingRules.DER, (SByteBacked)sbyte.MinValue + 1, "0A0181")] + [InlineData(AsnEncodingRules.BER, SByteBacked.Pillow, "0A820001EF")] + public static void GetExpectedValue_SByteBacked( + AsnEncodingRules ruleSet, + SByteBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, ShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, ShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, ShortBacked.Fluff, "0A020209")] + [InlineData(AsnEncodingRules.CER, ShortBacked.Fluff, "0A020209")] + [InlineData(AsnEncodingRules.DER, ShortBacked.Fluff, "0A020209")] + [InlineData(AsnEncodingRules.BER, ShortBacked.Pillow, "0A02FC00")] + [InlineData(AsnEncodingRules.CER, (ShortBacked)short.MinValue, "0A028000")] + [InlineData(AsnEncodingRules.DER, (ShortBacked)short.MinValue + 1, "0A028001")] + [InlineData(AsnEncodingRules.BER, ShortBacked.Pillow, "0A820002FC00")] + public static void GetExpectedValue_ShortBacked( + AsnEncodingRules ruleSet, + ShortBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, UShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, UShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, UShortBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, UShortBacked.Fluff, "0A03008000")] + [InlineData(AsnEncodingRules.CER, UShortBacked.Fluff, "0A03008000")] + [InlineData(AsnEncodingRules.DER, UShortBacked.Fluff, "0A03008000")] + [InlineData(AsnEncodingRules.BER, (UShortBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (UShortBacked)256, "0A020100")] + [InlineData(AsnEncodingRules.DER, (UShortBacked)0x7FED, "0A027FED")] + [InlineData(AsnEncodingRules.BER, (UShortBacked)ushort.MaxValue, "0A82000300FFFF")] + [InlineData(AsnEncodingRules.BER, (UShortBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_UShortBacked( + AsnEncodingRules ruleSet, + UShortBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, IntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, IntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, IntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, IntBacked.Fluff, "0A03010001")] + [InlineData(AsnEncodingRules.CER, IntBacked.Fluff, "0A03010001")] + [InlineData(AsnEncodingRules.DER, IntBacked.Fluff, "0A03010001")] + [InlineData(AsnEncodingRules.BER, IntBacked.Pillow, "0A03FEFFFF")] + [InlineData(AsnEncodingRules.CER, (IntBacked)int.MinValue, "0A0480000000")] + [InlineData(AsnEncodingRules.DER, (IntBacked)int.MinValue + 1, "0A0480000001")] + [InlineData(AsnEncodingRules.BER, IntBacked.Pillow, "0A820003FEFFFF")] + public static void GetExpectedValue_IntBacked( + AsnEncodingRules ruleSet, + IntBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, UIntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, UIntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, UIntBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(AsnEncodingRules.CER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(AsnEncodingRules.DER, UIntBacked.Fluff, "0A050080000005")] + [InlineData(AsnEncodingRules.BER, (UIntBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (UIntBacked)256, "0A020100")] + [InlineData(AsnEncodingRules.DER, (UIntBacked)0x7FED, "0A027FED")] + [InlineData(AsnEncodingRules.BER, (UIntBacked)uint.MaxValue, "0A82000500FFFFFFFF")] + [InlineData(AsnEncodingRules.BER, (UIntBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_UIntBacked( + AsnEncodingRules ruleSet, + UIntBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, LongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, LongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, LongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, LongBacked.Fluff, "0A050200000441")] + [InlineData(AsnEncodingRules.CER, LongBacked.Fluff, "0A050200000441")] + [InlineData(AsnEncodingRules.DER, LongBacked.Fluff, "0A050200000441")] + [InlineData(AsnEncodingRules.BER, LongBacked.Pillow, "0A05FF00000000")] + [InlineData(AsnEncodingRules.CER, (LongBacked)short.MinValue, "0A028000")] + [InlineData(AsnEncodingRules.DER, (LongBacked)short.MinValue + 1, "0A028001")] + [InlineData(AsnEncodingRules.BER, LongBacked.Pillow, "0A820005FF00000000")] + public static void GetExpectedValue_LongBacked( + AsnEncodingRules ruleSet, + LongBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ULongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.CER, ULongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.DER, ULongBacked.Zero, "0A0100")] + [InlineData(AsnEncodingRules.BER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.CER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.DER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)255, "0A0200FF")] + [InlineData(AsnEncodingRules.CER, (ULongBacked)256, "0A020100")] + [InlineData(AsnEncodingRules.DER, (ULongBacked)0x7FED, "0A027FED")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)uint.MaxValue, "0A82000500FFFFFFFF")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)ulong.MaxValue, "0A82000900FFFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, (ULongBacked)0x8123, "0A820003008123")] + public static void GetExpectedValue_ULongBacked( + AsnEncodingRules ruleSet, + ULongBacked expectedValue, + string inputHex) + { + GetExpectedValue(ruleSet, expectedValue, inputHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A020102")] + [InlineData(AsnEncodingRules.CER, "0A020102")] + [InlineData(AsnEncodingRules.DER, "0A020102")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Byte(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A020102")] + [InlineData(AsnEncodingRules.CER, "0A020102")] + [InlineData(AsnEncodingRules.DER, "0A020102")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_SByte(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Short(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A03010203")] + [InlineData(AsnEncodingRules.CER, "0A03010203")] + [InlineData(AsnEncodingRules.DER, "0A03010203")] + [InlineData(AsnEncodingRules.BER, "0A0401020304")] + [InlineData(AsnEncodingRules.CER, "0A0401020304")] + [InlineData(AsnEncodingRules.DER, "0A0401020304")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_UShort(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Int(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A050102030405")] + [InlineData(AsnEncodingRules.CER, "0A050102030405")] + [InlineData(AsnEncodingRules.DER, "0A050102030405")] + [InlineData(AsnEncodingRules.BER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.CER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.DER, "0A080102030405060708")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_UInt(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A02FF80")] + [InlineData(AsnEncodingRules.CER, "0A02FF80")] + [InlineData(AsnEncodingRules.DER, "0A02FF80")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_Long(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A01FF")] + [InlineData(AsnEncodingRules.CER, "0A01FF")] + [InlineData(AsnEncodingRules.DER, "0A01FF")] + [InlineData(AsnEncodingRules.BER, "0A02007F")] + [InlineData(AsnEncodingRules.CER, "0A02007F")] + [InlineData(AsnEncodingRules.DER, "0A02007F")] + [InlineData(AsnEncodingRules.BER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0A09010203040506070809")] + [InlineData(AsnEncodingRules.BER, "2A030A0100")] + public static void ReadEnumeratedValue_Invalid_ULong(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedValue_RequiresTypeArg(AsnEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + AssertExtensions.Throws( + "enumType", + () => reader.ReadEnumeratedValue(null!)); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedValue_NonEnumType(AsnEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedValue(typeof(Guid))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedValue_FlagsEnum(AsnEncodingRules ruleSet) + { + byte[] data = { 0x0A, 0x01, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + AssertExtensions.Throws( + "enumType", + () => reader.ReadEnumeratedValue()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEnumeratedBytes(AsnEncodingRules ruleSet) + { + const string Payload = "0102030405060708090A0B0C0D0E0F10"; + + // ENUMERATED (payload) followed by INTEGER (0) + byte[] data = ("0A10" + Payload + "020100").HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + ReadOnlyMemory contents = reader.ReadEnumeratedBytes(); + Assert.Equal(0x10, contents.Length); + Assert.Equal(Payload, contents.ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "")] + [InlineData(AsnEncodingRules.CER, "")] + [InlineData(AsnEncodingRules.DER, "")] + [InlineData(AsnEncodingRules.BER, "0A")] + [InlineData(AsnEncodingRules.CER, "0A")] + [InlineData(AsnEncodingRules.DER, "0A")] + [InlineData(AsnEncodingRules.BER, "0A00")] + [InlineData(AsnEncodingRules.CER, "0A00")] + [InlineData(AsnEncodingRules.DER, "0A00")] + [InlineData(AsnEncodingRules.BER, "0A01")] + [InlineData(AsnEncodingRules.CER, "0A01")] + [InlineData(AsnEncodingRules.DER, "0A01")] + [InlineData(AsnEncodingRules.BER, "010100")] + [InlineData(AsnEncodingRules.CER, "010100")] + [InlineData(AsnEncodingRules.DER, "010100")] + [InlineData(AsnEncodingRules.BER, "9F00")] + [InlineData(AsnEncodingRules.CER, "9F00")] + [InlineData(AsnEncodingRules.DER, "9F00")] + [InlineData(AsnEncodingRules.BER, "0A81")] + [InlineData(AsnEncodingRules.CER, "0A81")] + [InlineData(AsnEncodingRules.DER, "0A81")] + public static void ReadEnumeratedBytes_Throws(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadEnumeratedBytes()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x0A, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadEnumeratedValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + ShortBacked value = reader.ReadEnumeratedValue(); + Assert.Equal((ShortBacked)0x7E, value); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadEnumeratedValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadEnumeratedValue()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + ShortBacked value = reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.Equal((ShortBacked)0x80, value); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0A01FF", TagClass.Universal, 10)] + [InlineData(AsnEncodingRules.CER, "0A01FF", TagClass.Universal, 10)] + [InlineData(AsnEncodingRules.DER, "0A01FF", TagClass.Universal, 10)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + Asn1Tag primitiveTag = new Asn1Tag(tagClass, tagValue, false); + Asn1Tag constructedTag = new Asn1Tag(tagClass, tagValue, true); + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + ShortBacked val1 = reader.ReadEnumeratedValue(constructedTag); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + ShortBacked val2 = reader.ReadEnumeratedValue(primitiveTag); + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + + reader = new AsnReader(inputData, ruleSet); + ShortBacked val3 = (ShortBacked)reader.ReadEnumeratedValue(typeof(ShortBacked), constructedTag); + Assert.False(reader.HasData); + + Assert.Equal(val1, val3); + + reader = new AsnReader(inputData, ruleSet); + ShortBacked val4 = (ShortBacked)reader.ReadEnumeratedValue(typeof(ShortBacked), primitiveTag); + Assert.False(reader.HasData); + + Assert.Equal(val1, val4); + + reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory bytes1 = reader.ReadEnumeratedBytes(constructedTag); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory bytes2 = reader.ReadEnumeratedBytes(primitiveTag); + Assert.False(reader.HasData); + + Assert.Equal(bytes1.ByteArrayToHex(), bytes2.ByteArrayToHex()); + Assert.Equal("FF", bytes1.ByteArrayToHex()); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs similarity index 65% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs index 294e55e..86abaf6 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadGeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadGeneralizedTime.cs @@ -3,73 +3,72 @@ // See the LICENSE file in the project root for more information. using System.Linq; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadGeneralizedTime : Asn1ReaderTests + public sealed class ReadGeneralizedTime { [Theory] // yyyyMMddHH (2017090821) - [InlineData(PublicEncodingRules.BER, "180A32303137303930383231", 2017, 9, 8, 21, 0, 0, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180A32303137303930383231", 2017, 9, 8, 21, 0, 0, 0, null, 0)] // yyyyMMddHHZ (2017090821Z) - [InlineData(PublicEncodingRules.BER, "180B323031373039303832315A", 2017, 9, 8, 21, 0, 0, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "180B323031373039303832315A", 2017, 9, 8, 21, 0, 0, 0, 0, 0)] // yyyyMMddHH-HH (2017090821-01) - [InlineData(PublicEncodingRules.BER, "180D323031373039303832312D3031", 2017, 9, 8, 21, 0, 0, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "180D323031373039303832312D3031", 2017, 9, 8, 21, 0, 0, 0, -1, 0)] // yyyyMMddHH+HHmm (2017090821+0118) - [InlineData(PublicEncodingRules.BER, "180F323031373039303832312B30313138", 2017, 9, 8, 21, 0, 0, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "180F323031373039303832312B30313138", 2017, 9, 8, 21, 0, 0, 0, 1, 18)] // yyyyMMddHH,hourFrac (2017090821.1) - [InlineData(PublicEncodingRules.BER, "180C323031373039303832312C31", 2017, 9, 8, 21, 6, 0, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180C323031373039303832312C31", 2017, 9, 8, 21, 6, 0, 0, null, 0)] // yyyyMMddHH.hourFracZ (2017090821.2010Z) - [InlineData(PublicEncodingRules.BER, "1810323031373039303832312E323031305A", 2017, 9, 8, 21, 12, 3, 600, 0, 0)] + [InlineData(AsnEncodingRules.BER, "1810323031373039303832312E323031305A", 2017, 9, 8, 21, 12, 3, 600, 0, 0)] // yyyyMMddHH,hourFrac-HH (2017090821,3099-01) - [InlineData(PublicEncodingRules.BER, "1812323031373039303832312C333039392D3031", 2017, 9, 8, 21, 18, 35, 640, -1, 0)] + [InlineData(AsnEncodingRules.BER, "1812323031373039303832312C333039392D3031", 2017, 9, 8, 21, 18, 35, 640, -1, 0)] // yyyyMMddHH.hourFrac+HHmm (2017090821.201+0118) - [InlineData(PublicEncodingRules.BER, "1813323031373039303832312E3230312B30313138", 2017, 9, 8, 21, 12, 3, 600, 1, 18)] + [InlineData(AsnEncodingRules.BER, "1813323031373039303832312E3230312B30313138", 2017, 9, 8, 21, 12, 3, 600, 1, 18)] // yyyyMMddHHmm (201709082358) - [InlineData(PublicEncodingRules.BER, "180C323031373039303832333538", 2017, 9, 8, 23, 58, 0, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180C323031373039303832333538", 2017, 9, 8, 23, 58, 0, 0, null, 0)] // yyyyMMddHHmmZ (201709082358Z) - [InlineData(PublicEncodingRules.BER, "180D3230313730393038323335385A", 2017, 9, 8, 23, 58, 0, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "180D3230313730393038323335385A", 2017, 9, 8, 23, 58, 0, 0, 0, 0)] // yyyyMMddHHmm-HH (201709082358-01) - [InlineData(PublicEncodingRules.BER, "180F3230313730393038323335382D3031", 2017, 9, 8, 23, 58, 0, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "180F3230313730393038323335382D3031", 2017, 9, 8, 23, 58, 0, 0, -1, 0)] // yyyyMMddHHmm+HHmm (201709082358+0118) - [InlineData(PublicEncodingRules.BER, "18113230313730393038323335382B30313138", 2017, 9, 8, 23, 58, 0, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "18113230313730393038323335382B30313138", 2017, 9, 8, 23, 58, 0, 0, 1, 18)] // yyyyMMddHHmm.minuteFrac (201709082358.01) - [InlineData(PublicEncodingRules.BER, "180F3230313730393038323335382E3031", 2017, 9, 8, 23, 58, 0, 600, null, 0)] + [InlineData(AsnEncodingRules.BER, "180F3230313730393038323335382E3031", 2017, 9, 8, 23, 58, 0, 600, null, 0)] // yyyyMMddHHmm,minuteFracZ (201709082358,11Z) - [InlineData(PublicEncodingRules.BER, "18103230313730393038323335382C31315A", 2017, 9, 8, 23, 58, 6, 600, 0, 0)] + [InlineData(AsnEncodingRules.BER, "18103230313730393038323335382C31315A", 2017, 9, 8, 23, 58, 6, 600, 0, 0)] // yyyyMMddHHmm.minuteFrac-HH (201709082358.05-01) - [InlineData(PublicEncodingRules.BER, "18123230313730393038323335382E30352D3031", 2017, 9, 8, 23, 58, 3, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "18123230313730393038323335382E30352D3031", 2017, 9, 8, 23, 58, 3, 0, -1, 0)] // yyyyMMddHHmm,minuteFrac+HHmm (201709082358,007+0118) - [InlineData(PublicEncodingRules.BER, "18153230313730393038323335382C3030372B30313138", 2017, 9, 8, 23, 58, 0, 420, 1, 18)] + [InlineData(AsnEncodingRules.BER, "18153230313730393038323335382C3030372B30313138", 2017, 9, 8, 23, 58, 0, 420, 1, 18)] // yyyyMMddHHmmss (20161106012345) - Ambiguous time due to DST "fall back" in US & Canada - [InlineData(PublicEncodingRules.BER, "180E3230313631313036303132333435", 2016, 11, 6, 1, 23, 45, 0, null, 0)] + [InlineData(AsnEncodingRules.BER, "180E3230313631313036303132333435", 2016, 11, 6, 1, 23, 45, 0, null, 0)] // yyyyMMddHHmmssZ (20161106012345Z) - [InlineData(PublicEncodingRules.BER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] // yyyyMMddHHmmss-HH (20161106012345-01) - [InlineData(PublicEncodingRules.BER, "181132303136313130363031323334352D3031", 2016, 11, 6, 1, 23, 45, 0, -1, 0)] + [InlineData(AsnEncodingRules.BER, "181132303136313130363031323334352D3031", 2016, 11, 6, 1, 23, 45, 0, -1, 0)] // yyyyMMddHHmmss+HHmm (20161106012345+0118) - [InlineData(PublicEncodingRules.BER, "181332303136313130363031323334352B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "181332303136313130363031323334352B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] // yyyyMMddHHmmss.secondFrac (20161106012345.6789) - Ambiguous time due to DST "fall back" in US & Canada - [InlineData(PublicEncodingRules.BER, "181332303136313130363031323334352E36373839", 2016, 11, 6, 1, 23, 45, 678, null, 0)] + [InlineData(AsnEncodingRules.BER, "181332303136313130363031323334352E36373839", 2016, 11, 6, 1, 23, 45, 678, null, 0)] // yyyyMMddHHmmss,secondFracZ (20161106012345,7654Z) - [InlineData(PublicEncodingRules.BER, "181432303136313130363031323334352C373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(AsnEncodingRules.BER, "181432303136313130363031323334352C373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] // yyyyMMddHHmmss.secondFrac-HH (20161106012345.001-01) - [InlineData(PublicEncodingRules.BER, "181532303136313130363031323334352E3030312D3031", 2016, 11, 6, 1, 23, 45, 1, -1, 0)] + [InlineData(AsnEncodingRules.BER, "181532303136313130363031323334352E3030312D3031", 2016, 11, 6, 1, 23, 45, 1, -1, 0)] // yyyyMMddHHmmss,secondFrac+HHmm (20161106012345,0009+0118) - [InlineData(PublicEncodingRules.BER, "181832303136313130363031323334352C303030392B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] + [InlineData(AsnEncodingRules.BER, "181832303136313130363031323334352C303030392B30313138", 2016, 11, 6, 1, 23, 45, 0, 1, 18)] // yyyyMMddHHmmssZ (20161106012345Z) - [InlineData(PublicEncodingRules.CER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] - [InlineData(PublicEncodingRules.DER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(AsnEncodingRules.CER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] + [InlineData(AsnEncodingRules.DER, "180F32303136313130363031323334355A", 2016, 11, 6, 1, 23, 45, 0, 0, 0)] // yyyyMMddHHmmss.secondFracZ (20161106012345,7654Z) - [InlineData(PublicEncodingRules.CER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] - [InlineData(PublicEncodingRules.DER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(AsnEncodingRules.CER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] + [InlineData(AsnEncodingRules.DER, "181432303136313130363031323334352E373635345A", 2016, 11, 6, 1, 23, 45, 765, 0, 0)] public static void ParseTime_Valid( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, int year, int month, @@ -83,7 +82,7 @@ namespace System.Security.Cryptography.Tests.Asn1 { 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [Theory] - [InlineData(PublicEncodingRules.BER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] - [InlineData(PublicEncodingRules.CER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] - [InlineData(PublicEncodingRules.DER, "180F32303136313130363031323334355A", PublicTagClass.Universal, 24)] - [InlineData(PublicEncodingRules.BER, "800F31393530303130323132333435365A", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C0F31393530303130323132333435365A", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A460F31393530303130323132333435365A", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "180F32303136313130363031323334355A", TagClass.Universal, 24)] + [InlineData(AsnEncodingRules.CER, "180F32303136313130363031323334355A", TagClass.Universal, 24)] + [InlineData(AsnEncodingRules.DER, "180F32303136313130363031323334355A", TagClass.Universal, 24)] + [InlineData(AsnEncodingRules.BER, "800F31393530303130323132333435365A", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C0F31393530303130323132333435365A", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A460F31393530303130323132333435365A", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - DateTimeOffset val1 = reader.ReadGeneralizedTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); + DateTimeOffset val1 = reader.ReadGeneralizedTime(new Asn1Tag(tagClass, tagValue, true)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); - DateTimeOffset val2 = reader.ReadGeneralizedTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); + DateTimeOffset val2 = reader.ReadGeneralizedTime(new Asn1Tag(tagClass, tagValue, false)); Assert.False(reader.HasData); diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs similarity index 57% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs index 9fe1323..ae7c6ed 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadIA5String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadIA5String.cs @@ -5,80 +5,80 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; +using System.Text; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadIA5String : Asn1ReaderTests + public sealed class ReadIA5String { public static IEnumerable ValidEncodingData { get; } = new object[][] { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "160D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "160D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "160D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3680" + "040D4A6F686E20512E20536D697468" + "0000", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "360F" + "040D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "1600", "", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "1600", "", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "1600", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3600", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3680" + "0000", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "3680" + "2480" + // "Dr." @@ -119,12 +119,12 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyIA5StringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedString) { @@ -176,7 +176,7 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 out ReadOnlyMemory contents) { return reader.TryReadPrimitiveCharacterStringBytes( - UniversalTagNumber.IA5String, + new Asn1Tag(UniversalTagNumber.IA5String), out contents); } @@ -697,7 +746,6 @@ namespace System.Security.Cryptography.Tests.Asn1 { return reader.TryReadPrimitiveCharacterStringBytes( expectedTag, - UniversalTagNumber.IA5String, out contents); } @@ -706,9 +754,9 @@ namespace System.Security.Cryptography.Tests.Asn1 Span destination, out int bytesWritten) { - return reader.TryCopyCharacterStringBytes( - UniversalTagNumber.IA5String, + return reader.TryReadCharacterStringBytes( destination, + new Asn1Tag(UniversalTagNumber.IA5String), out bytesWritten); } @@ -717,9 +765,9 @@ namespace System.Security.Cryptography.Tests.Asn1 Span destination, out int charsWritten) { - return reader.TryCopyCharacterString( - UniversalTagNumber.IA5String, + return reader.TryReadCharacterString( destination, + UniversalTagNumber.IA5String, out charsWritten); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs new file mode 100644 index 0000000..351455e --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadInteger.cs @@ -0,0 +1,448 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadInteger + { + [Theory] + [InlineData("Constructed Encoding", AsnEncodingRules.BER, "2203020100")] + [InlineData("Constructed Encoding-Indefinite", AsnEncodingRules.BER, "228002010000")] + [InlineData("Constructed Encoding-Indefinite", AsnEncodingRules.CER, "228002010000")] + [InlineData("Constructed Encoding", AsnEncodingRules.DER, "2203020100")] + [InlineData("Wrong Universal Tag", AsnEncodingRules.BER, "030100")] + [InlineData("Bad Length", AsnEncodingRules.BER, "02030102")] + [InlineData("Incorrect Zero Encoding", AsnEncodingRules.BER, "0200")] + [InlineData("Incorrect Zero Encoding", AsnEncodingRules.CER, "0200")] + [InlineData("Incorrect Zero Encoding", AsnEncodingRules.DER, "0200")] + [InlineData("Redundant Leading 0x00", AsnEncodingRules.BER, "0202007F")] + [InlineData("Redundant Leading 0x00", AsnEncodingRules.CER, "0202007F")] + [InlineData("Redundant Leading 0x00", AsnEncodingRules.DER, "0202007F")] + [InlineData("Redundant Leading 0xFF", AsnEncodingRules.BER, "0202FF80")] + [InlineData("Redundant Leading 0xFF", AsnEncodingRules.CER, "0202FF80")] + [InlineData("Redundant Leading 0xFF", AsnEncodingRules.DER, "0202FF80")] + public static void InvalidData( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + Assert.Throws(() => reader.ReadIntegerBytes()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "020100", 0)] + [InlineData(AsnEncodingRules.DER, "020100", 0)] + [InlineData(AsnEncodingRules.DER, "0201FF", -1)] + [InlineData(AsnEncodingRules.CER, "0202FEFF", unchecked((int)0xFFFF_FEFF))] + [InlineData(AsnEncodingRules.BER, "028102FEEF", unchecked((int)0xFFFF_FEEF))] + [InlineData(AsnEncodingRules.BER, "02810480000000", int.MinValue)] + [InlineData(AsnEncodingRules.CER, "020480000000", int.MinValue)] + [InlineData(AsnEncodingRules.DER, "02047FFFFFFF", int.MaxValue)] + [InlineData(AsnEncodingRules.DER, "02026372", 0x6372)] + [InlineData(AsnEncodingRules.CER, "0203008ACE", 0x8ACE)] + [InlineData(AsnEncodingRules.BER, "0203FACE01", unchecked((int)0xFFFA_CE01))] + [InlineData(AsnEncodingRules.BER, "02820003FACE01", unchecked((int)0xFFFA_CE01))] + public static void ReadInt32_Success( + AsnEncodingRules ruleSet, + string inputHex, + int expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt32(out int value); + + Assert.True(didRead, "reader.TryReadInt32"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "02050102030405")] + [InlineData(AsnEncodingRules.CER, "02050102030405")] + [InlineData(AsnEncodingRules.DER, "02050102030405")] + public static void ReadInt32_TooMuchData( + AsnEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt32(out int value); + + Assert.False(didRead, "reader.TryReadInt32"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "02020080", 0x80)] + [InlineData(AsnEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(AsnEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(AsnEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(AsnEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(AsnEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(AsnEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.BER, "02830000050083828180", 0x83828180)] + public static void ReadUInt32_Success( + AsnEncodingRules ruleSet, + string inputHex, + uint expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt32(out uint value); + + Assert.True(didRead, "reader.TryReadUInt32"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020180")] + [InlineData(AsnEncodingRules.CER, "020180")] + [InlineData(AsnEncodingRules.DER, "020180")] + [InlineData(AsnEncodingRules.BER, "0201FF")] + [InlineData(AsnEncodingRules.CER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "02028000")] + [InlineData(AsnEncodingRules.DER, "0203800000")] + [InlineData(AsnEncodingRules.DER, "020480000000")] + [InlineData(AsnEncodingRules.DER, "02050100000000")] + public static void ReadUInt32_Failure(AsnEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt32(out uint value); + + Assert.False(didRead, "reader.TryReadUInt32"); + Assert.Equal((uint)0, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "02020080", 0x80)] + [InlineData(AsnEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(AsnEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(AsnEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(AsnEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(AsnEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(AsnEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.BER, "02830000050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.DER, "02050183828180", 0x0183828180)] + [InlineData(AsnEncodingRules.DER, "0206018483828180", 0x018483828180)] + [InlineData(AsnEncodingRules.DER, "020701858483828180", 0x01858483828180)] + [InlineData(AsnEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] + [InlineData(AsnEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] + [InlineData(AsnEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] + [InlineData(AsnEncodingRules.DER, "0201FF", -1)] + [InlineData(AsnEncodingRules.DER, "0201FE", -2)] + [InlineData(AsnEncodingRules.DER, "02028012", unchecked((long)0xFFFFFFFF_FFFF8012))] + [InlineData(AsnEncodingRules.DER, "0203818012", unchecked((long)0xFFFFFFFF_FF818012))] + [InlineData(AsnEncodingRules.DER, "020482818012", unchecked((long)0xFFFFFFFF_82818012))] + [InlineData(AsnEncodingRules.DER, "02058382818012", unchecked((long)0xFFFFFF83_82818012))] + [InlineData(AsnEncodingRules.DER, "0206848382818012", unchecked((long)0xFFFF8483_82818012))] + [InlineData(AsnEncodingRules.DER, "020785848382818012", unchecked((long)0xFF858483_82818012))] + [InlineData(AsnEncodingRules.DER, "02088685848382818012", unchecked((long)0x86858483_82818012))] + [InlineData(AsnEncodingRules.DER, "02088000000000000000", long.MinValue)] + [InlineData(AsnEncodingRules.BER, "028800000000000000088000000000000000", long.MinValue)] + public static void ReadInt64_Success( + AsnEncodingRules ruleSet, + string inputHex, + long expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt64(out long value); + + Assert.True(didRead, "reader.TryReadInt64"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.CER, "0209010203040506070809")] + [InlineData(AsnEncodingRules.DER, "0209010203040506070809")] + public static void ReadInt64_TooMuchData( + AsnEncodingRules ruleSet, + string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadInt64(out long value); + + Assert.False(didRead, "reader.TryReadInt64"); + Assert.Equal(0, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020100", 0)] + [InlineData(AsnEncodingRules.CER, "02020080", 0x80)] + [InlineData(AsnEncodingRules.DER, "02027F80", 0x7F80)] + [InlineData(AsnEncodingRules.DER, "0203008180", 0x8180)] + [InlineData(AsnEncodingRules.DER, "02030A8180", 0xA8180)] + [InlineData(AsnEncodingRules.DER, "020400828180", 0x828180)] + [InlineData(AsnEncodingRules.DER, "020475828180", 0x75828180)] + [InlineData(AsnEncodingRules.DER, "02050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.BER, "02830000050083828180", 0x83828180)] + [InlineData(AsnEncodingRules.DER, "02050183828180", 0x0183828180)] + [InlineData(AsnEncodingRules.DER, "0206018483828180", 0x018483828180)] + [InlineData(AsnEncodingRules.DER, "020701858483828180", 0x01858483828180)] + [InlineData(AsnEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] + [InlineData(AsnEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] + [InlineData(AsnEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] + [InlineData(AsnEncodingRules.DER, "0209008000000000000000", 0x80000000_00000000)] + [InlineData(AsnEncodingRules.DER, "020900FFFFFFFFFFFFFFFF", ulong.MaxValue)] + public static void ReadUInt64_Success( + AsnEncodingRules ruleSet, + string inputHex, + ulong expectedValue) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt64(out ulong value); + + Assert.True(didRead, "reader.TryReadUInt64"); + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "020180")] + [InlineData(AsnEncodingRules.CER, "020180")] + [InlineData(AsnEncodingRules.DER, "020180")] + [InlineData(AsnEncodingRules.BER, "0201FF")] + [InlineData(AsnEncodingRules.CER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "0201FF")] + [InlineData(AsnEncodingRules.DER, "02028000")] + [InlineData(AsnEncodingRules.DER, "0203800000")] + [InlineData(AsnEncodingRules.DER, "020480000000")] + [InlineData(AsnEncodingRules.DER, "02058000000000")] + [InlineData(AsnEncodingRules.DER, "0206800000000000")] + [InlineData(AsnEncodingRules.DER, "020780000000000000")] + [InlineData(AsnEncodingRules.DER, "02088000000000000000")] + [InlineData(AsnEncodingRules.DER, "0209010000000000000000")] + public static void ReadUInt64_Failure(AsnEncodingRules ruleSet, string inputHex) + { + byte[] data = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(data, ruleSet); + + bool didRead = reader.TryReadUInt64(out ulong value); + + Assert.False(didRead, "reader.TryReadUInt64"); + Assert.Equal((uint)0, value); + } + + [Fact] + public static void ReadIntegerBytes() + { + const string Payload = "0102030405060708090A0B0C0D0E0F10"; + + // INTEGER (payload) followed by INTEGER (0) + byte[] data = ("0210" + Payload + "020100").HexToByteArray(); + AsnReader reader = new AsnReader(data, AsnEncodingRules.DER); + + ReadOnlyMemory contents = reader.ReadIntegerBytes(); + Assert.Equal(0x10, contents.Length); + Assert.Equal(Payload, contents.ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetBigInteger(AsnEncodingRules ruleSet) + { + byte[] inputData = ( + "0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B4D").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "2075455505718444046766086325128514549301113944667492053189486607" + + "5638152321436011512404808361119326026027238444019992518081753153" + + "5965931647037093368608713442955529617501657176146245891571745113" + + "4028700771890451167051818999837042261788828826028681595867897235" + + "7967091503500375497498573022675671178275171110498592645868107163" + + "8525996766798322809764200941677343791419428587801897366593842552" + + "7272226864578661449281241619675217353931828233756506947863330597" + + "8338073826285687331647183058971791153730741973483420110408271570" + + "1367336140572971505716740825623220507359429297584634909330541150" + + "79473593821332264673455059897928082590541"); + + AsnReader reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadInteger()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetNegativeBigInteger(AsnEncodingRules ruleSet) + { + // This uses a length that doesn't line up with the array pool size so + // the fill code gets tested on netstandard. + // It's the same data as above, minus the padding and lead byte (and the + // next byte changed from 0x68 to 0x88) + byte[] inputData = ( + "0281FF8861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B4D").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "-" + + "5898547409447487884446992857601985651316300515844052200158198046" + + "7814538020408452501006415149581619776188797413593169277984980446" + + "1302361382932378450492052337986628823880000831383555853860116718" + + "5361729331647715885538858385106930514758305144777880150203212976" + + "6715081632226412951106013360243549075631850526067219857352295397" + + "2308328327377769665309386917336850273904442596855844458638806936" + + "6169824439111394938336579524651037239551388910737675470211780509" + + "8035477769907389338548451561341965157059382875181284370047601682" + + "6924486017215979427815833587119797658480104671279234402026468966" + + "86109928634475300812601680679147599027"); + + AsnReader reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadInteger()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void GetDiminutiveBigInteger(AsnEncodingRules ruleSet) + { + // GetBigInteger with the last byte removed. + // Since it is no longer an ArrayPool alignment size the fill code gets tested on netstandard. + byte[] inputData = ( + "0282010000A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + + "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + + "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + + "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + + "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + + "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + + "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + + "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + + "DA13810B").HexToByteArray(); + + BigInteger expected = BigInteger.Parse( + "2075455505718444046766086325128514549301113944667492053189486607" + + "5638152321436011512404808361119326026027238444019992518081753153" + + "5965931647037093368608713442955529617501657176146245891571745113" + + "4028700771890451167051818999837042261788828826028681595867897235" + + "7967091503500375497498573022675671178275171110498592645868107163" + + "8525996766798322809764200941677343791419428587801897366593842552" + + "7272226864578661449281241619675217353931828233756506947863330597" + + "8338073826285687331647183058971791153730741973483420110408271570" + + "1367336140572971505716740825623220507359429297584634909330541150" + + "79473593821332264673455059897928082590541") >> 8; + + AsnReader reader = new AsnReader(inputData, ruleSet); + Assert.Equal(expected, reader.ReadInteger()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 2, 1, 0x7E }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + ReadOnlyMemory value = reader.ReadIntegerBytes(); + Assert.Equal("7E", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 0, 0x80 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadIntegerBytes(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadIntegerBytes()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + ReadOnlyMemory value = reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.Equal("0080", value.ByteArrayToHex()); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0201FF", TagClass.Universal, 2)] + [InlineData(AsnEncodingRules.CER, "0201FF", TagClass.Universal, 2)] + [InlineData(AsnEncodingRules.DER, "0201FF", TagClass.Universal, 2)] + [InlineData(AsnEncodingRules.BER, "8001FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C01FF", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4601FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory val1 = reader.ReadIntegerBytes(new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + reader = new AsnReader(inputData, ruleSet); + ReadOnlyMemory val2 = reader.ReadIntegerBytes(new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs new file mode 100644 index 0000000..c0a8587 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadLength + { + private delegate Asn1Tag ReadTagAndLengthDelegate( + ReadOnlySpan source, + AsnEncodingRules ruleSet, + out int? parsedLength, + out int bytesRead); + + private static ReadTagAndLengthDelegate ReadTagAndLength = (ReadTagAndLengthDelegate) + typeof(AsnDecoder).GetMethod("ReadTagAndLength", BindingFlags.Static | BindingFlags.NonPublic) + .CreateDelegate(typeof(ReadTagAndLengthDelegate)); + + [Theory] + [InlineData(4, 0, "0400")] + [InlineData(1, 1, "0101")] + [InlineData(4, 127, "047F")] + [InlineData(4, 128, "048180")] + [InlineData(4, 255, "0481FF")] + [InlineData(2, 256, "02820100")] + [InlineData(4, int.MaxValue, "04847FFFFFFF")] + public static void MinimalPrimitiveLength(int tagValue, int length, string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + foreach (AsnEncodingRules rules in Enum.GetValues(typeof(AsnEncodingRules))) + { + Asn1Tag tag = ReadTagAndLength(inputBytes, rules, out int? parsedLength, out int bytesRead); + + Assert.Equal(inputBytes.Length, bytesRead); + Assert.False(tag.IsConstructed, "tag.IsConstructed"); + Assert.Equal(tagValue, tag.TagValue); + Assert.Equal(length, parsedLength.Value); + } + } + + [Theory] + [InlineData(-1)] + [InlineData(3)] + public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) + { + byte[] data = { 0x05, 0x00 }; + + Assert.Throws( + () => new AsnReader(data, (AsnEncodingRules)invalidRuleSetValue)); + } + + [Theory] + [InlineData("")] + [InlineData("05")] + [InlineData("0481")] + [InlineData("048201")] + [InlineData("04830102")] + [InlineData("0484010203")] + public static void ReadWithInsufficientData(string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + Assert.Throws( + () => ReadTagAndLength(inputData, AsnEncodingRules.DER, out _, out _)); + } + + [Theory] + [InlineData("DER indefinite constructed", AsnEncodingRules.DER, "3080" + "0500" + "0000")] + [InlineData("0xFF-BER", AsnEncodingRules.BER, "04FF")] + [InlineData("0xFF-CER", AsnEncodingRules.CER, "04FF")] + [InlineData("0xFF-DER", AsnEncodingRules.DER, "04FF")] + [InlineData("CER definite constructed", AsnEncodingRules.CER, "30820500")] + [InlineData("BER indefinite primitive", AsnEncodingRules.BER, "0480" + "0000")] + [InlineData("CER indefinite primitive", AsnEncodingRules.CER, "0480" + "0000")] + [InlineData("DER indefinite primitive", AsnEncodingRules.DER, "0480" + "0000")] + [InlineData("DER non-minimal 0", AsnEncodingRules.DER, "048100")] + [InlineData("DER non-minimal 7F", AsnEncodingRules.DER, "04817F")] + [InlineData("DER non-minimal 80", AsnEncodingRules.DER, "04820080")] + [InlineData("CER non-minimal 0", AsnEncodingRules.CER, "048100")] + [InlineData("CER non-minimal 7F", AsnEncodingRules.CER, "04817F")] + [InlineData("CER non-minimal 80", AsnEncodingRules.CER, "04820080")] + [InlineData("BER too large", AsnEncodingRules.BER, "048480000000")] + [InlineData("CER too large", AsnEncodingRules.CER, "048480000000")] + [InlineData("DER too large", AsnEncodingRules.DER, "048480000000")] + [InlineData("BER padded too large", AsnEncodingRules.BER, "0486000080000000")] + [InlineData("BER uint.MaxValue", AsnEncodingRules.BER, "0484FFFFFFFF")] + [InlineData("CER uint.MaxValue", AsnEncodingRules.CER, "0484FFFFFFFF")] + [InlineData("DER uint.MaxValue", AsnEncodingRules.DER, "0484FFFFFFFF")] + [InlineData("BER padded uint.MaxValue", AsnEncodingRules.BER, "048800000000FFFFFFFF")] + [InlineData("BER 5 byte spread", AsnEncodingRules.BER, "04850100000000")] + [InlineData("CER 5 byte spread", AsnEncodingRules.CER, "04850100000000")] + [InlineData("DER 5 byte spread", AsnEncodingRules.DER, "04850100000000")] + [InlineData("BER padded 5 byte spread", AsnEncodingRules.BER, "0486000100000000")] + public static void InvalidLengths( + string description, + AsnEncodingRules rules, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, rules); + + Assert.Throws( + () => ReadTagAndLength(inputData, rules, out _, out _)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void IndefiniteLength(AsnEncodingRules ruleSet) + { + // SEQUENCE (indefinite) + // NULL + // End-of-Contents + byte[] data = { 0x30, 0x80, 0x05, 0x00, 0x00, 0x00 }; + AsnReader reader = new AsnReader(data, ruleSet); + + Asn1Tag tag = ReadTagAndLength( + data, + ruleSet, + out int? length, + out int bytesRead); + + Assert.Equal(2, bytesRead); + Assert.False(length.HasValue, "length.HasValue"); + Assert.Equal((int)UniversalTagNumber.Sequence, tag.TagValue); + Assert.True(tag.IsConstructed, "tag.IsConstructed"); + } + + [Theory] + [InlineData(0, "0483000000")] + [InlineData(1, "048A00000000000000000001")] + [InlineData(128, "049000000000000000000000000000000080")] + public static void BerNonMinimalLength(int expectedLength, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Asn1Tag tag = ReadTagAndLength( + inputData, + AsnEncodingRules.BER, + out int? length, + out int bytesRead); + + Assert.Equal(inputData.Length, bytesRead); + Assert.Equal(expectedLength, length.Value); + // ReadTagAndLength doesn't move the _data span forward. + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 4, 0, 5, "0483000000" + "0500")] + [InlineData(AsnEncodingRules.DER, 1, 1, 2, "0101" + "FF")] + [InlineData(AsnEncodingRules.CER, 0x10, null, 2, "3080" + "0500" + "0000")] + public static void ReadWithDataRemaining( + AsnEncodingRules ruleSet, + int tagValue, + int? expectedLength, + int expectedBytesRead, + string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + + Asn1Tag tag = ReadTagAndLength( + inputData, + ruleSet, + out int? length, + out int bytesRead); + + Assert.Equal(expectedBytesRead, bytesRead); + Assert.Equal(tagValue, tag.TagValue); + Assert.Equal(expectedLength, length); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs new file mode 100644 index 0000000..4aa9e53 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNamedBitList.cs @@ -0,0 +1,518 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadNamedBitList + { + [Flags] + public enum X509KeyUsageCSharpStyle + { + None = 0, + DigitalSignature = 1, + NonRepudiation = 1 << 1, + KeyEncipherment = 1 << 2, + DataEncipherment = 1 << 3, + KeyAgreement = 1 << 4, + KeyCertSign = 1 << 5, + CrlSign = 1 << 6, + EncipherOnly = 1 << 7, + DecipherOnly = 1 << 8, + } + + [Flags] + public enum ULongFlags : ulong + { + None = 0, + Min = 1, + Mid = 1L << 32, + AlmostMax = 1L << 62, + Max = 1UL << 63, + } + + [Flags] + public enum LongFlags : long + { + None = 0, + Mid = 1L << 32, + Max = 1L << 62, + Min = long.MinValue, + } + + [Theory] + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.None), + "030100")] + [InlineData( + AsnEncodingRules.CER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), + "0303070480")] + [InlineData( + AsnEncodingRules.DER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.KeyAgreement), + "03020308")] + [InlineData( + AsnEncodingRules.BER, + typeof(LongFlags), + (long)(LongFlags.Mid | LongFlags.Max), + "0309010000000080000002")] + [InlineData( + AsnEncodingRules.CER, + typeof(LongFlags), + (long)(LongFlags.Mid | LongFlags.Min), + "0309000000000080000001")] + [InlineData( + AsnEncodingRules.DER, + typeof(LongFlags), + (long)(LongFlags.Min | LongFlags.Max), + "0309000000000000000003")] + // BER: Unused bits are unmapped, regardless of value. + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), + "030307048F")] + // BER: Trailing zeros are permitted. + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), + "03050014800000")] + // BER: Trailing 0-bits don't have to be declared "unused" + [InlineData( + AsnEncodingRules.BER, + typeof(X509KeyUsageCSharpStyle), + (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), + "0303001480")] + public static void VerifyReadNamedBitListEncodings( + AsnEncodingRules ruleSet, + Type enumType, + long enumValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputBytes, ruleSet); + Enum readValue = reader.ReadNamedBitListValue(enumType); + + Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); + } + + [Theory] + [InlineData( + AsnEncodingRules.BER, + typeof(ULongFlags), + (ulong)(ULongFlags.Mid | ULongFlags.Max), + "0309000000000080000001")] + [InlineData( + AsnEncodingRules.CER, + typeof(ULongFlags), + (ulong)(ULongFlags.Min | ULongFlags.Mid), + "0306078000000080")] + [InlineData( + AsnEncodingRules.DER, + typeof(ULongFlags), + (ulong)(ULongFlags.Min | ULongFlags.Max), + "0309008000000000000001")] + public static void VerifyReadNamedBitListEncodings_ULong( + AsnEncodingRules ruleSet, + Type enumType, + ulong enumValue, + string inputHex) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputBytes, ruleSet); + Enum readValue = reader.ReadNamedBitListValue(enumType); + + Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyGenericReadNamedBitList(AsnEncodingRules ruleSet) + { + string inputHex = "0306078000000080" + "0309010000000080000002"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + ULongFlags uLongFlags = reader.ReadNamedBitListValue(); + LongFlags longFlags = reader.ReadNamedBitListValue(); + + Assert.False(reader.HasData); + Assert.Equal(ULongFlags.Mid | ULongFlags.Min, uLongFlags); + Assert.Equal(LongFlags.Mid | LongFlags.Max, longFlags); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_RequiresTypeArg(AsnEncodingRules ruleSet) + { + string inputHex = "030100"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + AssertExtensions.Throws( + "flagsEnumType", + () => reader.ReadNamedBitListValue(null!)); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_RequiresFlags(AsnEncodingRules ruleSet) + { + string inputHex = "030100"; + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + AssertExtensions.Throws( + "flagsEnumType", + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_DataOutOfRange(AsnEncodingRules ruleSet) + { + string inputHex = "0309000000000100000001"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBytes(AsnEncodingRules ruleSet) + { + string inputHex = "03050014800000"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_ExcessiveBits(AsnEncodingRules ruleSet) + { + string inputHex = "0303061480"; + + AsnReader reader = new AsnReader(inputHex.HexToByteArray(), ruleSet); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 3, 2, 1, 2 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNamedBitListValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + X509KeyUsageCSharpStyle.CrlSign, + reader.ReadNamedBitListValue()); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 2, 2, 4 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNamedBitListValue(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadNamedBitListValue()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + X509KeyUsageCSharpStyle.KeyCertSign, + reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0303070080", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.CER, "0303070080", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.DER, "0303070080", TagClass.Universal, 3)] + [InlineData(AsnEncodingRules.BER, "8003070080", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C03070080", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4603070080", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Equal( + X509KeyUsageCSharpStyle.DecipherOnly, + reader.ReadNamedBitListValue( + new Asn1Tag(tagClass, tagValue, true))); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + Assert.Equal( + X509KeyUsageCSharpStyle.DecipherOnly, + reader.ReadNamedBitListValue( + new Asn1Tag(tagClass, tagValue, false))); + + Assert.False(reader.HasData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray(AsnEncodingRules ruleSet) + { + byte[] inputData = "440406400100".HexToByteArray(); + bool[] expected = new bool[18]; + expected[1] = expected[15] = true; + + AsnReader reader = new AsnReader(inputData, ruleSet); + + BitArray bits = reader.ReadNamedBitList(new Asn1Tag(TagClass.Application, 4)); + Assert.Equal(expected.Length, bits.Length); + Assert.False(reader.HasData); + + bool[] actual = new bool[expected.Length]; + bits.CopyTo(actual, 0); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_Empty(AsnEncodingRules ruleSet) + { + byte[] inputData = "030100".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + BitArray bits = reader.ReadNamedBitList(); + Assert.Equal(0, bits.Length); + Assert.False(reader.HasData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_EveryPattern(AsnEncodingRules ruleSet) + { + const string InputHex = + // Tag + "DF836B" + + // Length + "820101" + + // Unused bit count + "00" + + // Reversed bits for byte patterns 0x00-0x1F + "008040C020A060E0109050D030B070F0088848C828A868E8189858D838B878F8" + + // Reversed bits for byte patterns 0x20-0x3F + "048444C424A464E4149454D434B474F40C8C4CCC2CAC6CEC1C9C5CDC3CBC7CFC" + + // Reversed bits for byte patterns 0x40-0x5F + "028242C222A262E2129252D232B272F20A8A4ACA2AAA6AEA1A9A5ADA3ABA7AFA" + + // Reversed bits for byte patterns 0x60-0x7F + "068646C626A666E6169656D636B676F60E8E4ECE2EAE6EEE1E9E5EDE3EBE7EFE" + + // Reversed bits for byte patterns 0x80-0x9F + "018141C121A161E1119151D131B171F1098949C929A969E9199959D939B979F9" + + // Reversed bits for byte patterns 0xA0-0xBF + "058545C525A565E5159555D535B575F50D8D4DCD2DAD6DED1D9D5DDD3DBD7DFD" + + // Reversed bits for byte patterns 0xC0-0xDF + "038343C323A363E3139353D333B373F30B8B4BCB2BAB6BEB1B9B5BDB3BBB7BFB" + + // Reversed bits for byte patterns 0xE0-0xFF + "078747C727A767E7179757D737B777F70F8F4FCF2FAF6FEF1F9F5FDF3FBF7FFF"; + + byte[] inputData = InputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + byte[] allTheBytes = new byte[256]; + + for (int i = 0; i < allTheBytes.Length; i++) + { + allTheBytes[i] = (byte)i; + } + + BitArray bits = reader.ReadNamedBitList(new Asn1Tag(TagClass.Private, 491)); + Assert.Equal(allTheBytes.Length * 8, bits.Length); + Assert.False(reader.HasData); + + byte[] actual = new byte[allTheBytes.Length]; + bits.CopyTo(actual, 0); + + Assert.Equal(actual, actual); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_7992Bits(AsnEncodingRules ruleSet) + { + string inputHex = "848203E80008" + new string('0', 1994) + "02"; + byte[] inputData = inputHex.HexToByteArray(); + + BitArray expected = new BitArray(7992); + expected.Set(4, true); + expected.Set(7990, true); + + AsnReader reader = new AsnReader(inputData, ruleSet); + BitArray actual = reader.ReadNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 4)); + Assert.False(reader.HasData); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadNamedBitList_BitArray_7993Bits(AsnEncodingRules ruleSet) + { + string inputHex; + + if (ruleSet == AsnEncodingRules.CER) + { + inputHex = "A580038203E8" + new string('0', 2000) + "03020780" + "0000"; + } + else + { + inputHex = "858203E907" + new string('0', 1998) + "80"; + } + + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + BitArray actual = reader.ReadNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.False(reader.HasData); + + BitArray expected = new BitArray(7993); + expected.Set(7992, true); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyReadNamedBitList_KeyUsage_OneByte(AsnEncodingRules ruleSet) + { + // KeyUsage ::= BIT STRING { + // digitalSignature (0), + // nonRepudiation (1), + // keyEncipherment (2), + // dataEncipherment (3), + // keyAgreement (4), + // keyCertSign (5), + // cRLSign (6), + // encipherOnly (7), + // decipherOnly (8) } + + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, + critical: false); + + BitArray expected = new BitArray(7); + expected.Set(6, true); + expected.Set(5, true); + + AsnReader reader = new AsnReader(kuExt.RawData, ruleSet); + BitArray actual = reader.ReadNamedBitList(); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyReadNamedBitList_KeyUsage_TwoByte(AsnEncodingRules ruleSet) + { + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyAgreement | X509KeyUsageFlags.DecipherOnly, + critical: false); + + BitArray expected = new BitArray(9); + expected.Set(4, true); + expected.Set(8, true); + + AsnReader reader = new AsnReader(kuExt.RawData, ruleSet); + BitArray actual = reader.ReadNamedBitList(); + + Assert.Equal(expected.Cast(), actual.Cast()); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs new file mode 100644 index 0000000..f71e379 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadNull.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadNull + { + [Theory] + [InlineData(AsnEncodingRules.BER, "0500")] + [InlineData(AsnEncodingRules.CER, "0500")] + [InlineData(AsnEncodingRules.DER, "0500")] + [InlineData(AsnEncodingRules.BER, "0583000000")] + public static void ReadNull_Success(AsnEncodingRules ruleSet, string inputHex) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + reader.ReadNull(); + Assert.False(reader.HasData, "reader.HasData"); + } + + [Theory] + [InlineData("Long length", AsnEncodingRules.CER, "0583000000")] + [InlineData("Long length", AsnEncodingRules.DER, "0583000000")] + [InlineData("Constructed definite length", AsnEncodingRules.BER, "2500")] + [InlineData("Constructed definite length", AsnEncodingRules.DER, "2500")] + [InlineData("Constructed indefinite length", AsnEncodingRules.BER, "25800000")] + [InlineData("Constructed indefinite length", AsnEncodingRules.CER, "25800000")] + [InlineData("No length", AsnEncodingRules.BER, "05")] + [InlineData("No length", AsnEncodingRules.CER, "05")] + [InlineData("No length", AsnEncodingRules.DER, "05")] + [InlineData("No data", AsnEncodingRules.BER, "")] + [InlineData("No data", AsnEncodingRules.CER, "")] + [InlineData("No data", AsnEncodingRules.DER, "")] + [InlineData("NonEmpty", AsnEncodingRules.BER, "050100")] + [InlineData("NonEmpty", AsnEncodingRules.CER, "050100")] + [InlineData("NonEmpty", AsnEncodingRules.DER, "050100")] + [InlineData("Incomplete length", AsnEncodingRules.BER, "0581")] + public static void ReadNull_Throws(string description, AsnEncodingRules ruleSet, string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadNull()); + } + + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = { 5, 0 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + reader.ReadNull(); + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + byte[] inputData = { 0x87, 0 }; + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadNull()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.Application, 0))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 1))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 7)); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0500", TagClass.Universal, 5)] + [InlineData(AsnEncodingRules.CER, "0500", TagClass.Universal, 5)] + [InlineData(AsnEncodingRules.DER, "0500", TagClass.Universal, 5)] + [InlineData(AsnEncodingRules.BER, "8000", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C00", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A4600", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + reader.ReadNull(new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + reader.ReadNull(new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs similarity index 51% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs index 8ff87d1..50a3276 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadObjectIdentifier.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs @@ -2,74 +2,73 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using System.Text; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadObjectIdentifier : Asn1ReaderTests + public sealed class ReadObjectIdentifier { [Theory] - [InlineData("Wrong tag", PublicEncodingRules.BER, "010100")] - [InlineData("Wrong tag", PublicEncodingRules.CER, "010100")] - [InlineData("Wrong tag", PublicEncodingRules.DER, "010100")] - [InlineData("Overreaching length", PublicEncodingRules.BER, "0608883703")] - [InlineData("Overreaching length", PublicEncodingRules.CER, "0608883703")] - [InlineData("Overreaching length", PublicEncodingRules.DER, "0608883703")] - [InlineData("Zero length", PublicEncodingRules.BER, "0600")] - [InlineData("Zero length", PublicEncodingRules.CER, "0600")] - [InlineData("Zero length", PublicEncodingRules.DER, "0600")] - [InlineData("Constructed Definite Form", PublicEncodingRules.BER, "2605" + "0603883703")] - [InlineData("Constructed Indefinite Form", PublicEncodingRules.BER, "2680" + "0603883703" + "0000")] - [InlineData("Constructed Indefinite Form", PublicEncodingRules.CER, "2680" + "0603883703" + "0000")] - [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.BER, "060188")] - [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.CER, "060188")] - [InlineData("Unresolved carry-bit (first sub-identifier)", PublicEncodingRules.DER, "060188")] - [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.BER, "0603883781")] - [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.CER, "0603883781")] - [InlineData("Unresolved carry-bit (later sub-identifier)", PublicEncodingRules.DER, "0603883781")] - [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.BER, "060488378001")] - [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.CER, "060488378001")] - [InlineData("Sub-Identifier with leading 0x80", PublicEncodingRules.DER, "060488378001")] + [InlineData("Wrong tag", AsnEncodingRules.BER, "010100")] + [InlineData("Wrong tag", AsnEncodingRules.CER, "010100")] + [InlineData("Wrong tag", AsnEncodingRules.DER, "010100")] + [InlineData("Overreaching length", AsnEncodingRules.BER, "0608883703")] + [InlineData("Overreaching length", AsnEncodingRules.CER, "0608883703")] + [InlineData("Overreaching length", AsnEncodingRules.DER, "0608883703")] + [InlineData("Zero length", AsnEncodingRules.BER, "0600")] + [InlineData("Zero length", AsnEncodingRules.CER, "0600")] + [InlineData("Zero length", AsnEncodingRules.DER, "0600")] + [InlineData("Constructed Definite Form", AsnEncodingRules.BER, "2605" + "0603883703")] + [InlineData("Constructed Indefinite Form", AsnEncodingRules.BER, "2680" + "0603883703" + "0000")] + [InlineData("Constructed Indefinite Form", AsnEncodingRules.CER, "2680" + "0603883703" + "0000")] + [InlineData("Unresolved carry-bit (first sub-identifier)", AsnEncodingRules.BER, "060188")] + [InlineData("Unresolved carry-bit (first sub-identifier)", AsnEncodingRules.CER, "060188")] + [InlineData("Unresolved carry-bit (first sub-identifier)", AsnEncodingRules.DER, "060188")] + [InlineData("Unresolved carry-bit (later sub-identifier)", AsnEncodingRules.BER, "0603883781")] + [InlineData("Unresolved carry-bit (later sub-identifier)", AsnEncodingRules.CER, "0603883781")] + [InlineData("Unresolved carry-bit (later sub-identifier)", AsnEncodingRules.DER, "0603883781")] + [InlineData("Sub-Identifier with leading 0x80", AsnEncodingRules.BER, "060488378001")] + [InlineData("Sub-Identifier with leading 0x80", AsnEncodingRules.CER, "060488378001")] + [InlineData("Sub-Identifier with leading 0x80", AsnEncodingRules.DER, "060488378001")] public static void ReadObjectIdentifier_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws(() => reader.ReadObjectIdentifier()); + Assert.Throws(() => reader.ReadObjectIdentifier()); } [Theory] - [InlineData(PublicEncodingRules.BER, "0603883703", "2.999.3")] - [InlineData(PublicEncodingRules.CER, "06028837", "2.999")] - [InlineData(PublicEncodingRules.DER, "06068837C27B0302", "2.999.8571.3.2")] - [InlineData(PublicEncodingRules.BER, "0603550406", "2.5.4.6")] - [InlineData(PublicEncodingRules.CER, "06092A864886F70D010105", "1.2.840.113549.1.1.5")] - [InlineData(PublicEncodingRules.DER, "060100", "0.0")] - [InlineData(PublicEncodingRules.BER, "06080992268993F22C63", "0.9.2342.19200300.99")] + [InlineData(AsnEncodingRules.BER, "0603883703", "2.999.3")] + [InlineData(AsnEncodingRules.CER, "06028837", "2.999")] + [InlineData(AsnEncodingRules.DER, "06068837C27B0302", "2.999.8571.3.2")] + [InlineData(AsnEncodingRules.BER, "0603550406", "2.5.4.6")] + [InlineData(AsnEncodingRules.CER, "06092A864886F70D010105", "1.2.840.113549.1.1.5")] + [InlineData(AsnEncodingRules.DER, "060100", "0.0")] + [InlineData(AsnEncodingRules.BER, "06080992268993F22C63", "0.9.2342.19200300.99")] [InlineData( - PublicEncodingRules.DER, + AsnEncodingRules.DER, "0616824F83F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 // this is // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } "2.255.329800735698586629295641978511506172918.3")] - public static void ReadObjectIdentifierAsString_Success( - PublicEncodingRules ruleSet, + public static void ReadObjectIdentifier_Success( + AsnEncodingRules ruleSet, string inputHex, string expectedValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - string oidValue = reader.ReadObjectIdentifierAsString(); + string oidValue = reader.ReadObjectIdentifier(); Assert.Equal(expectedValue, oidValue); } @@ -103,33 +102,18 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 "352068092908376276711465745599868114846199290762088390824060" + "56034224"; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - string oidString = reader.ReadObjectIdentifierAsString(); + string oidString = reader.ReadObjectIdentifier(); Assert.Equal(ExpectedOid, oidString); } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs similarity index 50% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs index 8919d94..57bc752 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadOctetString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadOctetString.cs @@ -4,79 +4,78 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadOctetString : Asn1ReaderTests + public sealed class ReadOctetString { [Theory] - [InlineData("Constructed Payload", PublicEncodingRules.BER, "2402040100")] - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.BER, "248004010000")] + [InlineData("Constructed Payload", AsnEncodingRules.BER, "2402040100")] + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.BER, "248004010000")] // This value is actually invalid CER, but it returns false since it's not primitive and // it isn't worth preempting the descent to find out it was invalid. - [InlineData("Constructed Payload-Indefinite", PublicEncodingRules.CER, "248004010000")] - public static void TryGetOctetStringBytes_Fails( + [InlineData("Constructed Payload-Indefinite", AsnEncodingRules.CER, "248004010000")] + public static void TryReadPrimitiveOctetStringBytes_Fails( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + bool didRead = reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents); Assert.False(didRead, "reader.TryReadOctetStringBytes"); Assert.Equal(0, contents.Length); } [Theory] - [InlineData(PublicEncodingRules.BER, 0, "0400")] - [InlineData(PublicEncodingRules.BER, 1, "040100")] - [InlineData(PublicEncodingRules.BER, 2, "040201FE")] - [InlineData(PublicEncodingRules.CER, 5, "040502FEEFF00C")] - [InlineData(PublicEncodingRules.DER, 2, "04020780")] - [InlineData(PublicEncodingRules.DER, 5, "040500FEEFF00D" + "0500")] - public static void TryGetOctetStringBytes_Success( - PublicEncodingRules ruleSet, + [InlineData(AsnEncodingRules.BER, 0, "0400")] + [InlineData(AsnEncodingRules.BER, 1, "040100")] + [InlineData(AsnEncodingRules.BER, 2, "040201FE")] + [InlineData(AsnEncodingRules.CER, 5, "040502FEEFF00C")] + [InlineData(AsnEncodingRules.DER, 2, "04020780")] + [InlineData(AsnEncodingRules.DER, 5, "040500FEEFF00D" + "0500")] + public static void TryReadPrimitiveOctetStringBytes_Success( + AsnEncodingRules ruleSet, int expectedLength, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - bool didRead = reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents); + bool didRead = reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents); Assert.True(didRead, "reader.TryReadOctetStringBytes"); Assert.Equal(expectedLength, contents.Length); } [Theory] - [InlineData("Wrong Tag", PublicEncodingRules.BER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.CER, "0500")] - [InlineData("Wrong Tag", PublicEncodingRules.DER, "0500")] - [InlineData("Bad Length", PublicEncodingRules.BER, "040200")] - [InlineData("Bad Length", PublicEncodingRules.CER, "040200")] - [InlineData("Bad Length", PublicEncodingRules.DER, "040200")] - [InlineData("Constructed Form", PublicEncodingRules.DER, "2403040100")] - public static void TryGetOctetStringBytes_Throws( + [InlineData("Wrong Tag", AsnEncodingRules.BER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.CER, "0500")] + [InlineData("Wrong Tag", AsnEncodingRules.DER, "0500")] + [InlineData("Bad Length", AsnEncodingRules.BER, "040200")] + [InlineData("Bad Length", AsnEncodingRules.CER, "040200")] + [InlineData("Bad Length", AsnEncodingRules.DER, "040200")] + [InlineData("Constructed Form", AsnEncodingRules.DER, "2403040100")] + public static void TryReadPrimitiveOctetStringBytes_Throws( string description, - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex) { _ = description; byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); - Assert.Throws( - () => reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory contents)); + Assert.Throws( + () => reader.TryReadPrimitiveOctetString(out ReadOnlyMemory contents)); } [Fact] - public static void TryGetOctetStringBytes_Throws_CER_TooLong() + public static void TryReadPrimitiveOctetStringBytes_Throws_CER_TooLong() { // CER says that the maximum encoding length for an OctetString primitive // is 1000. @@ -92,12 +91,17 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 "0000", "0005")] [InlineData( - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2480" + "2406" + "0401FA" + @@ -192,74 +196,93 @@ namespace System.Security.Cryptography.Tests.Asn1 "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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 int bytesWritten; - Assert.True(reader.TryCopyOctetStringBytes(Span.Empty, out bytesWritten)); + Assert.True(reader.TryReadOctetString(Span.Empty, out bytesWritten)); Assert.Equal(0, bytesWritten); + + reader = new AsnReader(dataBytes, AsnEncodingRules.BER); + byte[] output2 = reader.ReadOctetString(); + + // It's Same (ReferenceEqual) on .NET Core, but just Equal on .NET Framework + Assert.Equal(Array.Empty(), output2); } } } diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs new file mode 100644 index 0000000..ed43a25 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSequence.cs @@ -0,0 +1,410 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadSequence + { + [Theory] + [InlineData(AsnEncodingRules.BER, "3000", false, -1)] + [InlineData(AsnEncodingRules.BER, "30800000", false, -1)] + [InlineData(AsnEncodingRules.BER, "3083000000", false, -1)] + [InlineData(AsnEncodingRules.CER, "30800000", false, -1)] + [InlineData(AsnEncodingRules.DER, "3000", false, -1)] + [InlineData(AsnEncodingRules.BER, "3000" + "0500", true, -1)] + [InlineData(AsnEncodingRules.BER, "3002" + "0500", false, 5)] + [InlineData(AsnEncodingRules.CER, "3080" + "0500" + "0000", false, 5)] + [InlineData(AsnEncodingRules.CER, "3080" + "010100" + "0000" + "0500", true, 1)] + [InlineData(AsnEncodingRules.DER, "3005" + "0500" + "0101FF", false, 5)] + public static void ReadSequence_Success( + AsnEncodingRules ruleSet, + string inputHex, + bool expectDataRemaining, + int expectedSequenceTagNumber) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader sequence = reader.ReadSequence(); + + if (expectDataRemaining) + { + Assert.True(reader.HasData, "reader.HasData"); + } + else + { + Assert.False(reader.HasData, "reader.HasData"); + } + + if (expectedSequenceTagNumber < 0) + { + Assert.False(sequence.HasData, "sequence.HasData"); + } + else + { + Assert.True(sequence.HasData, "sequence.HasData"); + + Asn1Tag firstTag = sequence.PeekTag(); + Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); + } + } + + [Theory] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "30")] + [InlineData("Missing Length", AsnEncodingRules.CER, "30")] + [InlineData("Missing Length", AsnEncodingRules.DER, "30")] + [InlineData("Primitive Encoding", AsnEncodingRules.BER, "1000")] + [InlineData("Primitive Encoding", AsnEncodingRules.CER, "1000")] + [InlineData("Primitive Encoding", AsnEncodingRules.DER, "1000")] + [InlineData("Definite Length Encoding", AsnEncodingRules.CER, "3000")] + [InlineData("Indefinite Length Encoding", AsnEncodingRules.DER, "3080" + "0000")] + [InlineData("Missing Content", AsnEncodingRules.BER, "3001")] + [InlineData("Missing Content", AsnEncodingRules.DER, "3001")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.BER, "3005" + "010100")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.DER, "3005" + "010100")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.BER, "3080")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.CER, "3080")] + [InlineData("Missing EoC", AsnEncodingRules.BER, "3080" + "010100")] + [InlineData("Missing EoC", AsnEncodingRules.CER, "3080" + "010100")] + [InlineData("Missing Outer EoC", AsnEncodingRules.BER, "3080" + "010100" + ("3080" + "0000"))] + [InlineData("Missing Outer EoC", AsnEncodingRules.CER, "3080" + "010100" + ("3080" + "0000"))] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.BER, "3100")] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.DER, "3100")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.BER, "3180" + "0000")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.CER, "3180" + "0000")] + public static void ReadSequence_Throws( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadSequence()); + } + + private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) + { + AsnReader mainReader = new AsnReader(inputData, ruleSet); + + AsnReader spkiReader = mainReader.ReadSequence(); + Assert.False(mainReader.HasData, "mainReader.HasData after reading SPKI"); + + AsnReader algorithmReader = spkiReader.ReadSequence(); + Assert.True(spkiReader.HasData, "spkiReader.HasData after reading algorithm"); + + ReadOnlyMemory publicKeyValue; + int unusedBitCount; + + if (!spkiReader.TryReadPrimitiveBitString(out unusedBitCount, out publicKeyValue)) + { + // The correct answer is 65 bytes. + for (int i = 10; ; i *= 2) + { + byte[] buf = new byte[i]; + + if (spkiReader.TryReadBitString(buf, out unusedBitCount, out int bytesWritten)) + { + publicKeyValue = new ReadOnlyMemory(buf, 0, bytesWritten); + break; + } + } + } + + Assert.False(spkiReader.HasData, "spkiReader.HasData after reading subjectPublicKey"); + Assert.True(algorithmReader.HasData, "algorithmReader.HasData before reading"); + + string algorithmOid = algorithmReader.ReadObjectIdentifier(); + Assert.True(algorithmReader.HasData, "algorithmReader.HasData after reading first OID"); + + Assert.Equal("1.2.840.10045.2.1", algorithmOid); + + string curveOid = algorithmReader.ReadObjectIdentifier(); + Assert.False(algorithmReader.HasData, "algorithmReader.HasData after reading second OID"); + + Assert.Equal("1.2.840.10045.3.1.7", curveOid); + + const string PublicKeyValue = + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + Assert.Equal(PublicKeyValue, publicKeyValue.ByteArrayToHex()); + Assert.Equal(0, unusedBitCount); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadEcPublicKey_DefiniteLength(AsnEncodingRules ruleSet) + { + const string InputHex = + "3059" + + "3013" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0342" + + "00" + + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; + + byte[] inputData = InputHex.HexToByteArray(); + ReadEcPublicKey(ruleSet, inputData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void ReadEcPublicKey_IndefiniteLength(AsnEncodingRules ruleSet) + { + const string InputHex = + "3080" + + "3080" + + "06072A8648CE3D0201" + + "06082A8648CE3D030107" + + "0000" + + "0342" + + "00" + + "04" + + "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + + "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13" + + "0000"; + + byte[] inputData = InputHex.HexToByteArray(); + ReadEcPublicKey(ruleSet, inputData); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "30020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSequence(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "308005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSequence(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A5020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSequence()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A58005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSequence(Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSequence()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "30030101FF", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.BER, "30800101000000", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.CER, "30800101000000", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.DER, "30030101FF", TagClass.Universal, 16)] + [InlineData(AsnEncodingRules.BER, "A0030101FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.BER, "A1800101000000", TagClass.ContextSpecific, 1)] + [InlineData(AsnEncodingRules.CER, "6C800101000000", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "FF8A46030101FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AsnReader val1 = reader.ReadSequence(new Asn1Tag(tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + AsnReader val2 = reader.ReadSequence(new Asn1Tag(tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadSequenceOf_PreservesOptions(AsnEncodingRules ruleSet) + { + // [5] (UtcTime) 500102123456Z + // UtcTime 120102235959Z + // + // They're sorted backwards, though. + const string PayloadHex = + "850D3530303130323132333435365A" + + "170D3132303130323233353935395A"; + + byte[] inputData; + + // Build the rule-specific form of SEQUENCE { [PRIVATE 9] SEQUENCE { SET-OF { dates }, NULL } } + // The outer Set-Of is also invalid, because the NULL should be first. + if (ruleSet == AsnEncodingRules.DER) + { + inputData = ("3024" + "E922" + "A21E" + PayloadHex + "0500").HexToByteArray(); + } + else + { + string inputHex = "3080" + "E980" + "A280" + PayloadHex + "0000" + "0500" + "0000" + "0000"; + inputData = inputHex.HexToByteArray(); + } + + AsnReaderOptions options = new AsnReaderOptions + { + SkipSetSortOrderVerification = true, + UtcTimeTwoDigitYearMax = 2011, + }; + + AsnReader initial = new AsnReader(inputData, ruleSet, options); + AsnReader outer = initial.ReadSequence(); + Assert.False(initial.HasData); + AsnReader inner = outer.ReadSequence(new Asn1Tag(TagClass.Private, 9)); + Assert.False(outer.HasData); + + Asn1Tag setTag = new Asn1Tag(TagClass.ContextSpecific, 2); + + if (ruleSet != AsnEncodingRules.BER) + { + Assert.Throws(() => inner.ReadSetOf(false, setTag)); + } + + // This confirms that we've passed SkipSetOrderVerification this far. + AsnReader setOf = inner.ReadSetOf(setTag); + Assert.True(inner.HasData); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + setOf.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); + + // This confirms that we've passed UtcTimeTwoDigitYearMax, + // the default would call this 2012. + Assert.Equal( + new DateTimeOffset(1912, 1, 2, 23, 59, 59, TimeSpan.Zero), + setOf.ReadUtcTime()); + + Assert.False(setOf.HasData); + + inner.ReadNull(); + Assert.False(inner.HasData); + + setOf.ThrowIfNotEmpty(); + inner.ThrowIfNotEmpty(); + outer.ThrowIfNotEmpty(); + initial.ThrowIfNotEmpty(); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs new file mode 100644 index 0000000..2dfb2af --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadSetOf.cs @@ -0,0 +1,395 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadSetOf + { + [Theory] + [InlineData(AsnEncodingRules.BER, "3100", false, -1)] + [InlineData(AsnEncodingRules.BER, "31800000", false, -1)] + [InlineData(AsnEncodingRules.BER, "3183000000", false, -1)] + [InlineData(AsnEncodingRules.CER, "31800000", false, -1)] + [InlineData(AsnEncodingRules.DER, "3100", false, -1)] + [InlineData(AsnEncodingRules.BER, "3100" + "0500", true, -1)] + [InlineData(AsnEncodingRules.BER, "3102" + "0500", false, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "0500" + "0000", false, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0000" + "0500", true, 1)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", false, 1)] + [InlineData(AsnEncodingRules.DER, "3105" + "0101FF" + "0500", false, 1)] + public static void ReadSetOf_Success( + AsnEncodingRules ruleSet, + string inputHex, + bool expectDataRemaining, + int expectedSequenceTagNumber) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader sequence = reader.ReadSetOf(); + + if (expectDataRemaining) + { + Assert.True(reader.HasData, "reader.HasData"); + } + else + { + Assert.False(reader.HasData, "reader.HasData"); + } + + if (expectedSequenceTagNumber < 0) + { + Assert.False(sequence.HasData, "sequence.HasData"); + } + else + { + Assert.True(sequence.HasData, "sequence.HasData"); + + Asn1Tag firstTag = sequence.PeekTag(); + Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); + } + } + + [Theory] + [InlineData("Empty", AsnEncodingRules.BER, "")] + [InlineData("Empty", AsnEncodingRules.CER, "")] + [InlineData("Empty", AsnEncodingRules.DER, "")] + [InlineData("Incomplete Tag", AsnEncodingRules.BER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.CER, "1F")] + [InlineData("Incomplete Tag", AsnEncodingRules.DER, "1F")] + [InlineData("Missing Length", AsnEncodingRules.BER, "31")] + [InlineData("Missing Length", AsnEncodingRules.CER, "31")] + [InlineData("Missing Length", AsnEncodingRules.DER, "31")] + [InlineData("Primitive Encoding", AsnEncodingRules.BER, "1100")] + [InlineData("Primitive Encoding", AsnEncodingRules.CER, "1100")] + [InlineData("Primitive Encoding", AsnEncodingRules.DER, "1100")] + [InlineData("Definite Length Encoding", AsnEncodingRules.CER, "3100")] + [InlineData("Indefinite Length Encoding", AsnEncodingRules.DER, "3180" + "0000")] + [InlineData("Missing Content", AsnEncodingRules.BER, "3101")] + [InlineData("Missing Content", AsnEncodingRules.DER, "3101")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.BER, "3105" + "010100")] + [InlineData("Length Out Of Bounds", AsnEncodingRules.DER, "3105" + "010100")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.BER, "3180")] + [InlineData("Missing Content - Indefinite", AsnEncodingRules.CER, "3180")] + [InlineData("Missing EoC", AsnEncodingRules.BER, "3180" + "010100")] + [InlineData("Missing EoC", AsnEncodingRules.CER, "3180" + "010100")] + [InlineData("Missing Outer EoC", AsnEncodingRules.BER, "3180" + "010100" + ("3180" + "0000"))] + [InlineData("Missing Outer EoC", AsnEncodingRules.CER, "3180" + "010100" + ("3180" + "0000"))] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.BER, "3000")] + [InlineData("Wrong Tag - Definite", AsnEncodingRules.DER, "3000")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.BER, "3080" + "0000")] + [InlineData("Wrong Tag - Indefinite", AsnEncodingRules.CER, "3080" + "0000")] + public static void ReadSetOf_Throws( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadSetOf()); + } + + [Theory] + // BER can read out of order (indefinite) + [InlineData(AsnEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] + // BER can read out of order (definite) + [InlineData(AsnEncodingRules.BER, "3106" + "0101FF" + "010100", true, 1)] + // CER will not read out of order + [InlineData(AsnEncodingRules.CER, "3180" + "0500" + "010100" + "0000", false, 1)] + [InlineData(AsnEncodingRules.CER, "3180" + "0101FF" + "010100" + "0000", false, 1)] + // CER is happy in order: + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0500" + "0000", true, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", true, 5)] + [InlineData(AsnEncodingRules.CER, "3180" + "010100" + "010100" + "0500" + "0000", true, 5)] + // DER will not read out of order + [InlineData(AsnEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] + [InlineData(AsnEncodingRules.DER, "3105" + "0500" + "010100", false, 1)] + // DER is happy in order: + [InlineData(AsnEncodingRules.DER, "3105" + "010100" + "0500", true, 5)] + [InlineData(AsnEncodingRules.DER, "3108" + "010100" + "0101FF" + "0500", true, 5)] + [InlineData(AsnEncodingRules.DER, "3108" + "010100" + "010100" + "0500", true, 5)] + public static void ReadSetOf_DataSorting( + AsnEncodingRules ruleSet, + string inputHex, + bool expectSuccess, + int lastTagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + AsnReader setOf; + + AsnReader laxReader = new AsnReader( + inputData, + ruleSet, + new AsnReaderOptions { SkipSetSortOrderVerification = true }); + + if (expectSuccess) + { + setOf = reader.ReadSetOf(); + } + else + { + AsnReader alsoReader = new AsnReader(inputData, ruleSet); + Assert.Throws(() => alsoReader.ReadSetOf()); + Assert.Throws(() => laxReader.ReadSetOf(false)); + + setOf = reader.ReadSetOf(skipSortOrderValidation: true); + } + + int lastTag = -1; + + while (setOf.HasData) + { + Asn1Tag tag = setOf.PeekTag(); + lastTag = tag.TagValue; + + // Ignore the return, just drain it. + setOf.ReadEncodedValue(); + } + + Assert.Equal(lastTagValue, lastTag); + + setOf = laxReader.ReadSetOf(); + lastTag = -1; + + while (setOf.HasData) + { + Asn1Tag tag = setOf.PeekTag(); + lastTag = tag.TagValue; + + // Ignore the return, just drain it. + setOf.ReadEncodedValue(); + } + + Assert.Equal(lastTagValue, lastTag); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "31020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSetOf(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Universal_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "318005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + AsnReader seq = reader.ReadSetOf(); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom_Definite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A5020500".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSetOf()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + public static void TagMustBeCorrect_Custom_Indefinite(AsnEncodingRules ruleSet) + { + byte[] inputData = "A58005000000".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadSetOf(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadSetOf()); + + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.Application, 5))); + + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + AsnReader seq = reader.ReadSetOf(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5)); + Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); + + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "31030101FF", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.BER, "31800101000000", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.CER, "31800101000000", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.DER, "31030101FF", TagClass.Universal, 17)] + [InlineData(AsnEncodingRules.BER, "A0030101FF", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.BER, "A1800101000000", TagClass.ContextSpecific, 1)] + [InlineData(AsnEncodingRules.CER, "6C800101000000", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "FF8A46030101FF", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AsnReader val1 = reader.ReadSetOf(expectedTag: new Asn1Tag(tagClass, tagValue, true)); + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + AsnReader val2 = reader.ReadSetOf(expectedTag: new Asn1Tag(tagClass, tagValue, false)); + Assert.False(reader.HasData); + + Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void ReadSetOf_PreservesOptions(AsnEncodingRules ruleSet) + { + // [5] (UtcTime) 500102123456Z + // UtcTime 120102235959Z + // + // They're sorted backwards, though. + const string PayloadHex = + "850D3530303130323132333435365A" + + "170D3132303130323233353935395A"; + + byte[] inputData; + + // Build the rule-specific form of SET-OF { SET-OF { dates }, NULL } + // The outer Set-Of is also invalid, because the NULL should be first. + if (ruleSet == AsnEncodingRules.DER) + { + inputData = ("3122" + "A21E" + PayloadHex + "0500").HexToByteArray(); + } + else + { + inputData = ("3180" + "A280" + PayloadHex + "0000" + "0500" + "0000").HexToByteArray(); + } + + AsnReaderOptions options = new AsnReaderOptions + { + SkipSetSortOrderVerification = true, + UtcTimeTwoDigitYearMax = 2011, + }; + + AsnReader initial = new AsnReader(inputData, ruleSet, options); + + if (ruleSet != AsnEncodingRules.BER) + { + Assert.Throws(() => initial.ReadSetOf(false)); + } + + AsnReader outer = initial.ReadSetOf(); + Assert.False(initial.HasData); + + Asn1Tag innerTag = new Asn1Tag(TagClass.ContextSpecific, 2); + + if (ruleSet != AsnEncodingRules.BER) + { + Assert.Throws(() => outer.ReadSetOf(false, innerTag)); + } + + // This confirms that we've passed SkipSetOrderVerification this far. + AsnReader inner = outer.ReadSetOf(innerTag); + Assert.True(outer.HasData); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + inner.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); + + // This confirms that we've passed UtcTimeTwoDigitYearMax, + // the default would call this 2012. + Assert.Equal( + new DateTimeOffset(1912, 1, 2, 23, 59, 59, TimeSpan.Zero), + inner.ReadUtcTime()); + + Assert.False(inner.HasData); + + outer.ReadNull(); + Assert.False(outer.HasData); + + inner.ThrowIfNotEmpty(); + outer.ThrowIfNotEmpty(); + initial.ThrowIfNotEmpty(); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs similarity index 79% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs index 4c6cff5..056785a 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadT61String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadT61String.cs @@ -3,15 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadT61String : Asn1ReaderTests + public sealed class ReadT61String { public static IEnumerable ValidEncodingData { get; } = new object[][] @@ -19,47 +16,47 @@ namespace System.Security.Cryptography.Tests.Asn1 // 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 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 { 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 @@ namespace System.Security.Cryptography.Tests.Asn1 Assert.Equal('a', output[0]); } - copied = reader.TryCopyCharacterString( - UniversalTagNumber.T61String, + copied = reader.TryReadCharacterString( output, + UniversalTagNumber.T61String, out charsWritten); Assert.True(copied, "reader.TryCopyT61String"); diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs similarity index 55% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs index 4b9464e..c09de11 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUTF8String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs @@ -5,80 +5,79 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { - public sealed class ReadUTF8String : Asn1ReaderTests + public sealed class ReadUTF8String { public static IEnumerable ValidEncodingData { get; } = new object[][] { new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "0C0D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "0C0D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "0C0D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C80" + "040D4A6F686E20512E20536D697468" + "0000", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C0F" + "040D4A6F686E20512E20536D697468", "John Q. Smith", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "0C00", "", }, new object[] { - PublicEncodingRules.CER, + AsnEncodingRules.CER, "0C00", "", }, new object[] { - PublicEncodingRules.DER, + AsnEncodingRules.DER, "0C00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C00", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C80" + "0000", "", }, new object[] { - PublicEncodingRules.BER, + AsnEncodingRules.BER, "2C80" + "2480" + // "Dr." @@ -123,12 +122,12 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 [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 @@ namespace System.Security.Cryptography.Tests.Asn1 { 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 [Theory] [MemberData(nameof(ValidEncodingData))] public static void TryCopyUTF8StringBytes( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, string expectedString) { @@ -184,7 +183,7 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 { 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [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 @@ namespace System.Security.Cryptography.Tests.Asn1 } [Theory] - [InlineData(PublicEncodingRules.BER, "0C026869", PublicTagClass.Universal, 12)] - [InlineData(PublicEncodingRules.CER, "0C026869", PublicTagClass.Universal, 12)] - [InlineData(PublicEncodingRules.DER, "0C026869", PublicTagClass.Universal, 12)] - [InlineData(PublicEncodingRules.BER, "80023132", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C023132", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A46023132", PublicTagClass.Private, 1350)] + [InlineData(AsnEncodingRules.BER, "0C026869", TagClass.Universal, 12)] + [InlineData(AsnEncodingRules.CER, "0C026869", TagClass.Universal, 12)] + [InlineData(AsnEncodingRules.DER, "0C026869", TagClass.Universal, 12)] + [InlineData(AsnEncodingRules.BER, "80023132", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C023132", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A46023132", TagClass.Private, 1350)] public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, + AsnEncodingRules ruleSet, string inputHex, - PublicTagClass tagClass, + TagClass tagClass, int tagValue) { byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + AsnReader reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryReadPrimitiveCharacterStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, true), - UniversalTagNumber.UTF8String, + new Asn1Tag(tagClass, tagValue, true), out ReadOnlyMemory val1)); Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); + reader = new AsnReader(inputData, ruleSet); Assert.True( reader.TryReadPrimitiveCharacterStringBytes( - new Asn1Tag((TagClass)tagClass, tagValue, false), - UniversalTagNumber.UTF8String, + new Asn1Tag(tagClass, tagValue, false), out ReadOnlyMemory val2)); Assert.False(reader.HasData); diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs new file mode 100644 index 0000000..ff9e333 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadUtcTime.cs @@ -0,0 +1,299 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Reader +{ + public sealed class ReadUtcTime + { + [Theory] + // A, B2, C2 + [InlineData(AsnEncodingRules.BER, "17113137303930383130333530332D30373030", 2017, 9, 8, 10, 35, 3, -7, 0)] + [InlineData(AsnEncodingRules.BER, "17113137303930383130333530332D30303530", 2017, 9, 8, 10, 35, 3, 0, -50)] + [InlineData(AsnEncodingRules.BER, "17113137303930383130333530332B30373030", 2017, 9, 8, 10, 35, 3, 7, 0)] + [InlineData(AsnEncodingRules.BER, "17113030303130313030303030302B30303030", 2000, 1, 1, 0, 0, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "17113030303130313030303030302D31343030", 2000, 1, 1, 0, 0, 0, -14, 0)] + // A, B2, C1 (only legal form for CER or DER) + [InlineData(AsnEncodingRules.BER, "170D3132303130323233353935395A", 2012, 1, 2, 23, 59, 59, 0, 0)] + [InlineData(AsnEncodingRules.CER, "170D3439313233313233353935395A", 2049, 12, 31, 23, 59, 59, 0, 0)] + [InlineData(AsnEncodingRules.DER, "170D3530303130323132333435365A", 1950, 1, 2, 12, 34, 56, 0, 0)] + // A, B1, C2 + [InlineData(AsnEncodingRules.BER, "170F313730393038313033352D30373030", 2017, 9, 8, 10, 35, 0, -7, 0)] + [InlineData(AsnEncodingRules.BER, "170F323730393038313033352B30393132", 2027, 9, 8, 10, 35, 0, 9, 12)] + // A, B1, C1 + [InlineData(AsnEncodingRules.BER, "170B313230313032323335395A", 2012, 1, 2, 23, 59, 0, 0, 0)] + [InlineData(AsnEncodingRules.BER, "170B343931323331323335395A", 2049, 12, 31, 23, 59, 0, 0, 0)] + // BER Constructed form + [InlineData( + AsnEncodingRules.BER, + "3780" + + "04023132" + + "04023031" + + "2480" + "040130" + "040132" + "0000" + + "040432333539" + + "04830000015A" + + "0000", + 2012, 1, 2, 23, 59, 0, 0, 0)] + public static void ParseTime_Valid( + AsnEncodingRules ruleSet, + string inputHex, + int year, + int month, + int day, + int hour, + int minute, + int second, + int offsetHour, + int offsetMinute) + { + byte[] inputData = inputHex.HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, ruleSet); + DateTimeOffset value = reader.ReadUtcTime(); + + Assert.Equal(year, value.Year); + Assert.Equal(month, value.Month); + Assert.Equal(day, value.Day); + Assert.Equal(hour, value.Hour); + Assert.Equal(minute, value.Minute); + Assert.Equal(second, value.Second); + Assert.Equal(0, value.Millisecond); + Assert.Equal(new TimeSpan(offsetHour, offsetMinute, 0), value.Offset); + } + + [Fact] + public static void ParseTime_InvalidValue_LegalString() + { + byte[] inputData = "17113030303030303030303030302D31353030".HexToByteArray(); + + var exception = Assert.Throws( + () => + { + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + reader.ReadUtcTime(); + }); + + Assert.NotNull(exception.InnerException); + Assert.IsType(exception.InnerException); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum(int maximum, int interpretedYear) + { + byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); + + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + DateTimeOffset value = reader.ReadUtcTime(maximum); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum_FromOptions(int maximum, int interpretedYear) + { + byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); + + AsnReaderOptions options = new AsnReaderOptions { UtcTimeTwoDigitYearMax = maximum }; + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER, options); + DateTimeOffset value = reader.ReadUtcTime(maximum); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData(2011, 1912)] + [InlineData(2012, 2012)] + [InlineData(2013, 2012)] + [InlineData(2111, 2012)] + [InlineData(2112, 2112)] + [InlineData(2113, 2112)] + [InlineData(12, 12)] + [InlineData(99, 12)] + [InlineData(111, 12)] + public static void ReadUtcTime_TwoYearMaximum_FromOptions_CustomTag(int maximum, int interpretedYear) + { + byte[] inputData = "820D3132303130323233353935395A".HexToByteArray(); + + AsnReaderOptions options = new AsnReaderOptions { UtcTimeTwoDigitYearMax = maximum }; + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER, options); + DateTimeOffset value = reader.ReadUtcTime(maximum, new Asn1Tag(TagClass.ContextSpecific, 2)); + + Assert.Equal(interpretedYear, value.Year); + } + + [Theory] + [InlineData("A,B2,C2", AsnEncodingRules.CER, "17113137303930383130333530332D30373030")] + [InlineData("A,B2,C2", AsnEncodingRules.DER, "17113137303930383130333530332D30373030")] + [InlineData("A,B1,C2", AsnEncodingRules.CER, "170F313730393038313033352D30373030")] + [InlineData("A,B1,C2", AsnEncodingRules.DER, "170F313730393038313033352D30373030")] + [InlineData("A,B1,C1", AsnEncodingRules.CER, "170B313230313032323335395A")] + [InlineData("A,B1,C1", AsnEncodingRules.DER, "170B313230313032323335395A")] + [InlineData("A,B1,C1-NotZ", AsnEncodingRules.BER, "170B313230313032323335392B")] + [InlineData("A,B1,C2-NotPlusMinus", AsnEncodingRules.BER, "170F313730393038313033352C30373030")] + [InlineData("A,B2,C2-NotPlusMinus", AsnEncodingRules.BER, "17113137303930383130333530332C30373030")] + [InlineData("A,B2,C2-MinuteOutOfRange", AsnEncodingRules.BER, "17113030303030303030303030302D31353630")] + [InlineData("A,B1,C2-MinuteOutOfRange", AsnEncodingRules.BER, "170F303030303030303030302D31353630")] + [InlineData("A1,B2,C1-NotZ", AsnEncodingRules.DER, "170D3530303130323132333435365B")] + [InlineData("A,B2,C2-MissingDigit", AsnEncodingRules.BER, "17103137303930383130333530332C303730")] + [InlineData("A,B2,C2-TooLong", AsnEncodingRules.BER, "17123137303930383130333530332B3037303030")] + [InlineData("WrongTag", AsnEncodingRules.BER, "1A0D3132303130323233353935395A")] + public static void ReadUtcTime_Throws( + string description, + AsnEncodingRules ruleSet, + string inputHex) + { + _ = description; + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + Assert.Throws(() => reader.ReadUtcTime()); + } + + [Fact] + public static void ReadUtcTime_WayTooBig_Throws() + { + // Need to exceed the length that the shared pool will return for 17: + byte[] inputData = new byte[4097+4]; + inputData[0] = 0x17; + inputData[1] = 0x82; + inputData[2] = 0x10; + inputData[3] = 0x01; + + AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); + + Assert.Throws(() => reader.ReadUtcTime()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Universal(AsnEncodingRules ruleSet) + { + byte[] inputData = "170D3530303130323132333435365A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadUtcTime(expectedTag: Asn1Tag.Null)); + + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 0))); + + Assert.True(reader.HasData, "HasData after wrong tag"); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.ReadUtcTime()); + + Assert.False(reader.HasData, "HasData after read"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet) + { + const int TwoDigitYearMax = 2052; + byte[] inputData = "850D3530303130323132333435365A".HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadUtcTime(expectedTag: Asn1Tag.Null)); + Assert.True(reader.HasData, "HasData after bad universal tag"); + + AssertExtensions.Throws( + "expectedTag", + () => reader.ReadUtcTime(TwoDigitYearMax, expectedTag: Asn1Tag.Null)); + Assert.True(reader.HasData, "HasData after bad universal tag"); + + Assert.Throws(() => reader.ReadUtcTime()); + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws(() => reader.ReadUtcTime(TwoDigitYearMax)); + Assert.True(reader.HasData, "HasData after default tag"); + + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.Application, 5))); + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadUtcTime(TwoDigitYearMax, expectedTag: new Asn1Tag(TagClass.Application, 5))); + Assert.True(reader.HasData, "HasData after wrong custom class"); + + Assert.Throws( + () => reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Throws( + () => reader.ReadUtcTime(TwoDigitYearMax, expectedTag: new Asn1Tag(TagClass.ContextSpecific, 7))); + Assert.True(reader.HasData, "HasData after wrong custom tag value"); + + Assert.Equal( + new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.ReadUtcTime(expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5))); + Assert.False(reader.HasData, "HasData after reading value"); + + reader = new AsnReader(inputData, ruleSet); + + Assert.Equal( + new DateTimeOffset(2050, 1, 2, 12, 34, 56, TimeSpan.Zero), + reader.ReadUtcTime(TwoDigitYearMax, expectedTag: new Asn1Tag(TagClass.ContextSpecific, 5))); + Assert.False(reader.HasData, "HasData after reading value"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "170D3530303130323132333435365A", TagClass.Universal, 23)] + [InlineData(AsnEncodingRules.CER, "170D3530303130323132333435365A", TagClass.Universal, 23)] + [InlineData(AsnEncodingRules.DER, "170D3530303130323132333435365A", TagClass.Universal, 23)] + [InlineData(AsnEncodingRules.BER, "800D3530303130323132333435365A", TagClass.ContextSpecific, 0)] + [InlineData(AsnEncodingRules.CER, "4C0D3530303130323132333435365A", TagClass.Application, 12)] + [InlineData(AsnEncodingRules.DER, "DF8A460D3530303130323132333435365A", TagClass.Private, 1350)] + public static void ExpectedTag_IgnoresConstructed( + AsnEncodingRules ruleSet, + string inputHex, + TagClass tagClass, + int tagValue) + { + byte[] inputData = inputHex.HexToByteArray(); + AsnReader reader = new AsnReader(inputData, ruleSet); + + DateTimeOffset val1 = reader.ReadUtcTime(expectedTag: new Asn1Tag(tagClass, tagValue, true)); + + Assert.False(reader.HasData); + + reader = new AsnReader(inputData, ruleSet); + + DateTimeOffset val2 = reader.ReadUtcTime(expectedTag: new Asn1Tag(tagClass, tagValue, false)); + + Assert.False(reader.HasData); + + Assert.Equal(val1, val2); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs similarity index 85% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs rename to src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs index 0cd9ef5..058213e 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReaderStateTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReaderStateTests.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Reader { public static class ReaderStateTests { @@ -14,7 +13,7 @@ namespace System.Security.Cryptography.Tests.Asn1 { AsnReader reader = new AsnReader(new byte[] { 0x01, 0x01, 0x00 }, AsnEncodingRules.BER); Assert.True(reader.HasData); - Assert.Throws(() => reader.ThrowIfNotEmpty()); + Assert.Throws(() => reader.ThrowIfNotEmpty()); // Consume the current value and move on. reader.ReadEncodedValue(); diff --git a/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj new file mode 100644 index 0000000..beb4397 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj @@ -0,0 +1,55 @@ + + + true + $(NetCoreAppCurrent);$(NetFrameworkCurrent) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CommonTest\System\Security\Cryptography\ByteUtils.cs + + + diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs b/src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs similarity index 81% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs index 08d3a52..c20633d 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/Asn1WriterTests.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/Asn1WriterTests.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Security.Cryptography.Asn1; using Test.Cryptography; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { - public abstract partial class Asn1WriterTests : Asn1ReaderTests + public abstract partial class Asn1WriterTests { internal static void Verify(AsnWriter writer, string expectedHex) { @@ -43,6 +42,14 @@ namespace System.Security.Cryptography.Tests.Asn1 Assert.Equal(encoded.Length, bytesWritten); Assert.True(dest.Slice(0, bytesWritten).SequenceEqual(encoded), "dest.SequenceEqual(encoded2) (overly big)"); Assert.Equal(254, encoded2[encoded.Length]); + + Assert.True(writer.EncodedValueEquals(encoded)); + Assert.False(writer.EncodedValueEquals(encoded2)); + Assert.True(writer.EncodedValueEquals(encoded2.AsSpan(0, encoded.Length))); + Assert.False(writer.EncodedValueEquals(encoded2.AsSpan(1, encoded.Length))); + + encoded2[encoded.Length - 1] ^= 0xFF; + Assert.False(writer.EncodedValueEquals(encoded2.AsSpan(0, encoded.Length))); } internal static unsafe string Stringify(Asn1Tag tag) diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs b/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs new file mode 100644 index 0000000..03c1eeb --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/ComprehensiveWriteTest.cs @@ -0,0 +1,322 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Formats.Asn1.Tests.Reader; +using System.Numerics; +using Test.Cryptography; +using Xunit; +using X509KeyUsageCSharpStyle = System.Formats.Asn1.Tests.Reader.ReadNamedBitList.X509KeyUsageCSharpStyle; + +namespace System.Formats.Asn1.Tests.Writer +{ + public static class ComprehensiveWriteTest + { + [Fact] + public static void WriteMicrosoftDotComCert() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + + // Certificate + using (writer.PushSequence()) + { + // tbsCertificate + using (writer.PushSequence()) + { + // version ([0] EXPLICIT INTEGER) + Asn1Tag context0 = new Asn1Tag(TagClass.ContextSpecific, 0, true); + + using (writer.PushSequence(context0)) + { + writer.WriteInteger(2); + } + + BigInteger serialValue = BigInteger.Parse("82365655871428336739211871484630851433"); + writer.WriteInteger(serialValue); + + // signature (algorithm) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + } + + // issuer + using (writer.PushSequence()) + { + WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.10", "Symantec Corporation", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.11", "Symantec Trust Network", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.3", "Symantec Class 3 EV SSL CA - G3", UniversalTagNumber.PrintableString); + } + + // validity + using (writer.PushSequence()) + { + writer.WriteUtcTime(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero)); + writer.WriteUtcTime(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero)); + } + + // subject + using (writer.PushSequence()) + { + WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.3", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.2", "Washington", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.15", "Private Organization", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.5", "600413485", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); + WriteRdn(writer, "2.5.4.17", "98052", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.8", "Washington", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.7", "Redmond", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.9", "1 Microsoft Way", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.10", "Microsoft Corporation", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.11", "MSCOM", UniversalTagNumber.UTF8String); + WriteRdn(writer, "2.5.4.3", "www.microsoft.com", UniversalTagNumber.UTF8String); + } + + // subjectPublicKeyInfo + using (writer.PushSequence()) + { + // subjectPublicKeyInfo.algorithm + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.1"); + writer.WriteNull(); + } + + AsnWriter publicKeyWriter = new AsnWriter(AsnEncodingRules.DER); + + using (publicKeyWriter.PushSequence()) + { + BigInteger modulus = BigInteger.Parse( + "207545550571844404676608632512851454930111394466749205318948660756381" + + "523214360115124048083611193260260272384440199925180817531535965931647" + + "037093368608713442955529617501657176146245891571745113402870077189045" + + "116705181899983704226178882882602868159586789723579670915035003754974" + + "985730226756711782751711104985926458681071638525996766798322809764200" + + "941677343791419428587801897366593842552727222686457866144928124161967" + + "521735393182823375650694786333059783380738262856873316471830589717911" + + "537307419734834201104082715701367336140572971505716740825623220507359" + + "42929758463490933054115079473593821332264673455059897928082590541"); + publicKeyWriter.WriteInteger(modulus); + publicKeyWriter.WriteInteger(65537); + } + + // subjectPublicKeyInfo.subjectPublicKey + writer.WriteBitString(publicKeyWriter.Encode()); + } + + // extensions ([3] EXPLICIT Extensions) + Asn1Tag context3 = new Asn1Tag(TagClass.ContextSpecific, 3); + + using (writer.PushSequence(context3)) + { + using (writer.PushSequence()) + { + Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); + + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.17"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "www.microsoft.com", + dnsName); + + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "wwwqa.microsoft.com", + dnsName); + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.19"); + // Empty sequence as the payload for a non-CA basic constraint. + writer.WriteOctetString(new byte[] { 0x30, 0x00 }); + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.15"); + // critical: true + writer.WriteBoolean(true); + + using (writer.PushOctetString()) + { + // This extension doesn't use a sequence at all, just Named Bit List. + writer.WriteNamedBitList( + X509KeyUsageCSharpStyle.DigitalSignature | + X509KeyUsageCSharpStyle.KeyEncipherment); + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.37"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.3.1"); + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.3.2"); + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.32"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.16.840.1.113733.1.7.23.6"); + + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.2.1"); + + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "https://d.symcb.com/cps"); + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.2.2"); + + using (writer.PushSequence()) + { + writer.WriteCharacterString( + UniversalTagNumber.VisibleString, + "https://d.symcb.com/rpa"); + } + } + } + } + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.35"); + + Asn1Tag keyIdentifier = context0; + byte[] authorityKeyIdentifier = "0159ABE7DD3A0B59A66463D6CF200757D591E76A".HexToByteArray(); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + writer.WriteOctetString(authorityKeyIdentifier, keyIdentifier); + } + } + + Asn1Tag generalNameUriChoice = new Asn1Tag(TagClass.ContextSpecific, 6); + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("2.5.29.31"); + + Asn1Tag distributionPointChoice = context0; + Asn1Tag fullNameChoice = context0; + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + using (writer.PushSequence(distributionPointChoice)) + { + using (writer.PushSequence(fullNameChoice)) + { + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "http://sr.symcb.com/sr.crl", + generalNameUriChoice); + } + } + } + } + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.1.1"); + + using (writer.PushOctetString()) + using (writer.PushSequence()) + { + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "http://sr.symcd.com", + generalNameUriChoice); + } + + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2"); + writer.WriteCharacterString( + UniversalTagNumber.IA5String, + "http://sr.symcb.com/sr.crt", + generalNameUriChoice); + } + } + } + } + } + } + + // signatureAlgorithm + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); + writer.WriteNull(); + } + + // signature + byte[] containsSignature = ( + "010203040506070809" + + "15F8505B627ED7F9F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC" + + "20ACF728AAFA7A1A1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D6" + + "45BFCF840A4A3FDD988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A" + + "64A9C5FB96932BA70059CE92BD278B41299FD213471BD8165F924285AE3ECD66" + + "6C703885DCA65D24DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D" + + "4A56ADB21B5822549918015647B5F8AC131CC5EB24534D172BC60218A88B65BC" + + "F71C7F388CE3E0EF697B4203720483BB5794455B597D80D48CD3A1D73CBBC609" + + "C058767D1FF060A609D7E3D4317079AF0CD0A8A49251AB129157F9894A036487" + + "090807060504030201").HexToByteArray(); + + writer.WriteBitString(containsSignature.AsSpan(9, 256)); + } + + Assert.Equal( + ComprehensiveReadTests.MicrosoftDotComSslCertBytes.ByteArrayToHex(), + writer.Encode().ByteArrayToHex()); + + Assert.True(writer.EncodedValueEquals(ComprehensiveReadTests.MicrosoftDotComSslCertBytes)); + } + + private static void WriteRdn(AsnWriter writer, string oid, string value, UniversalTagNumber valueType) + { + using (writer.PushSetOf()) + using (writer.PushSequence()) + { + writer.WriteObjectIdentifier(oid); + writer.WriteCharacterString(valueType, value); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs new file mode 100644 index 0000000..b3cb9e7 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopOctetString.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class PushPopOctetString : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopOctetString()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + writer.PopOctetString(); + + Assert.Throws( + () => writer.PopOctetString()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + writer.PopOctetString(); + + Assert.Throws( + () => writer.PopOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + Assert.Throws( + () => writer.PopOctetString()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + + Assert.Throws( + () => writer.PopOctetString(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(UniversalTagNumber.OctetString)); + writer.PopOctetString(); + + Verify(writer, "0400"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(TagClass.Private, 5)); + writer.PopOctetString(new Asn1Tag(TagClass.Private, 5, true)); + + Verify(writer, "C500"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(); + writer.PopOctetString(new Asn1Tag(UniversalTagNumber.OctetString)); + + Verify(writer, "0400"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushOctetString(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopOctetString(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1100_0000); + string tagHex = tag.ToString("X2"); + string expectedHex = tagHex + "00"; + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void LargePayload_1000(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (writer.PushOctetString(new Asn1Tag(TagClass.ContextSpecific, 9))) + { + byte[] tmp = new byte[496]; + writer.WriteOctetString(tmp); + writer.WriteOctetString(tmp, new Asn1Tag(TagClass.ContextSpecific, 10)); + } + + string zeroHex496Bytes = new string('0', 496 * 2); + + string expectedHex = + // Tag + "89" + + // Length + "8203E8" + + // First written content + "048201F0" + zeroHex496Bytes + + // Second written content + "8A8201F0" + zeroHex496Bytes; + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void LargePayload_1001(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (writer.PushOctetString(new Asn1Tag(TagClass.Private, 9))) + { + byte[] tmp = new byte[497]; + writer.WriteOctetString(tmp.AsSpan(0, 496)); + writer.WriteOctetString(tmp, new Asn1Tag(TagClass.Application, 10)); + } + + string zeroHex496Bytes = new string('0', 496 * 2); + + string expectedHex; + + if (ruleSet == AsnEncodingRules.CER) + { + // This moved into the constructed encoding form. + // Tag + expectedHex = + // Tag + "E9" + + // Indefinite length + "80" + + // Definite octet string + "04" + + // 1000 bytes + "8203E8" + + // First written content + "048201F0" + zeroHex496Bytes + + // Second written content tag, length, and first 498 payload bytes + "4A8201F1" + zeroHex496Bytes + + // Second definite octet string, 1 byte, { 0x00 } payload + "040100" + + // End indefinite length + "0000"; + } + else + { + expectedHex = + // Tag + "C9" + + // Length + "8203E9" + + // First written content + "048201F0" + zeroHex496Bytes + + // Second written content + "4A8201F1" + zeroHex496Bytes + "00"; + } + + Verify(writer, expectedHex); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs new file mode 100644 index 0000000..85d8676 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSequence.cs @@ -0,0 +1,602 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class PushPopSequence : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSequence()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(); + + Assert.Throws( + () => writer.PopSequence()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(); + + Assert.Throws( + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + Assert.Throws( + () => writer.PopSequence()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + + Assert.Throws( + () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); + writer.PopSequence(); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "30800000"); + } + else + { + Verify(writer, "3000"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.Private, 5)); + writer.PopSequence(new Asn1Tag(TagClass.Private, 5, true)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "E5800000"); + } + else + { + Verify(writer, "E500"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(); + writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "30800000"); + } + else + { + Verify(writer, "3000"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSequence(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopSequence(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == AsnEncodingRules.CER ? "800000" : "00"; + + Verify(writer, tagHex + rest); + } + + [Fact] + public static void BER_WritesDefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "3000"); + } + + [Fact] + public static void CER_WritesIndefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "30800000"); + } + + [Fact] + public static void DER_WritesDefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSequence(); + writer.PopSequence(); + + Verify(writer, "3000"); + } + + [Fact] + public static void BER_WritesDefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "EF00"); + } + + [Fact] + public static void CER_WritesIndefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "7F5B800000"); + } + + [Fact] + public static void DER_WritesDefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSequence(tag); + writer.PopSequence(tag); + + Verify(writer, "BE00"); + } + + private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) + { + writer.PushSequence(); + { + writer.PushSequence(alt); + writer.PopSequence(alt); + + writer.PushSequence(); + { + writer.PushSequence(alt); + { + writer.PushSequence(); + writer.PopSequence(); + } + + writer.PopSequence(alt); + } + + writer.PopSequence(); + } + + writer.PopSequence(); + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + + TestNested(writer, alt, "300AFF7F003005FF7F023000"); + } + + [Fact] + public static void CER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); + + TestNested(writer, alt, "3080AC8000003080AC8030800000000000000000"); + } + + [Fact] + public static void DER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); + + TestNested(writer, alt, "30086500300465023000"); + } + + private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) + { + writer.PushSequence(); + + // F00DF00D...F00DF00D + byte[] contentBytes = new byte[126]; + + for (int i = 0; i < contentBytes.Length; i += 2) + { + contentBytes[i] = 0xF0; + contentBytes[i + 1] = 0x0D; + } + + writer.WriteOctetString(contentBytes); + writer.PopSequence(); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void SimpleContentShift(AsnEncodingRules ruleSet) + { + const string ExpectedHex = + "308180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; + + AsnWriter writer = new AsnWriter(ruleSet); + SimpleContentShiftCore(writer, ExpectedHex); + } + + [Fact] + public static void SimpleContentShift_CER() + { + const string ExpectedHex = + "3080" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "0000"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + SimpleContentShiftCore(writer, ExpectedHex); + } + + private static void WriteRSAPublicKeyCore(AsnEncodingRules ruleSet, string expectedHex) + { + AsnWriter innerWriter = new AsnWriter(ruleSet); + byte[] paddedBigEndianN = ( + "00" + + "AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED369731156" + + "20968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925BCE" + + "624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8CBB" + + "5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF18" + + "7B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C94" + + "AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF2B" + + "82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6D9" + + "FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD8847").HexToByteArray(); + + // Now it's padded little-endian. + Array.Reverse(paddedBigEndianN); + BigInteger n = new BigInteger(paddedBigEndianN); + const long e = 8589935681; + + innerWriter.PushSequence(); + innerWriter.WriteInteger(n); + innerWriter.WriteInteger(e); + innerWriter.PopSequence(); + + AsnWriter outerWriter = new AsnWriter(ruleSet); + // RSAPublicKey + outerWriter.PushSequence(); + + // AlgorithmIdentifier + outerWriter.PushSequence(); + outerWriter.WriteObjectIdentifier("1.2.840.113549.1.1.1"); + outerWriter.WriteNull(); + outerWriter.PopSequence(); + + outerWriter.WriteBitString(innerWriter.Encode()); + outerWriter.PopSequence(); + + Verify(outerWriter, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void WriteRSAPublicKey(AsnEncodingRules ruleSet) + { + const string ExpectedHex = + // CONSTRUCTED SEQUENCE + "30820124" + + // CONSTRUCTED SEQUENCE + "300D" + + // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) + "06092A864886F70D010101" + + // NULL + "0500" + + // BIT STRING + "03820111" + + // 0 unused bits + "00" + + // sneaky inspection of the payload bytes + // CONSTRUCTED SEQUENCE + "3082010C" + + // INTEGER (n) + "02820101" + + "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + + "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + + "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + + "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + + "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + + "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + + "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + + "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + + "47" + + // INTEGER (e) + "02050200000441"; + + WriteRSAPublicKeyCore(ruleSet, ExpectedHex); + } + + [Fact] + public static void WriteRSAPublicKey_CER() + { + const string ExpectedHex = + // CONSTRUCTED SEQUENCE + "3080" + + // CONSTRUCTED SEQUENCE + "3080" + + // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) + "06092A864886F70D010101" + + // NULL + "0500" + + // End-of-Contents + "0000" + + // BIT STRING + "03820111" + + // 0 unused bits + "00" + + // sneaky inspection of the payload bytes + // CONSTRUCTED SEQUENCE + "3080" + + // INTEGER (n) + "02820101" + + "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + + "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + + "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + + "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + + "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + + "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + + "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + + "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + + "47" + + // INTEGER (e) + "02050200000441" + + // End-of-Contents + "0000" + + // (no EoC for the BIT STRING) + // End-of-Contents + "0000"; + + WriteRSAPublicKeyCore(AsnEncodingRules.CER, ExpectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, true)] + public static void CannotEncodeWhileUnbalanced(AsnEncodingRules ruleSet, bool customTag) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSequence(); + } + + int written = -5; + + Assert.Throws(() => writer.GetEncodedLength()); + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); + + byte[] buf = new byte[10]; + Assert.Throws(() => writer.TryEncode(buf, out written)); + Assert.Throws(() => writer.EncodedValueEquals(buf)); + Assert.Equal(-5, written); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => writer.PushSequence(Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_PopSequence(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + writer.PushSetOf(tag); + + Assert.Throws( + () => writer.PopSequence(tag)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_IdempotentDispose(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + + using (var scope = writer.PushSequence(tag)) + { + writer.PopSequence(tag); + + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_IdempotentDispose_Complex(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + expectedHex += expectedHex; + + using (var scope = writer.PushSequence(tag)) + { + writer.PopSequence(tag); + + writer.PushSequence(tag); + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + writer.PopSequence(tag); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_DisposeThrowsIfDeepContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + var scope = writer.PushSequence(); + writer.PushSetOf(); + + Assert.Throws(() => scope.Dispose()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_DisposeSilentNotContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (var scope = writer.PushSequence()) + { + writer.PopSequence(); + + // Since this has a different write offset, the equals is false, so no exception. + writer.PushSequence(); + writer.PushSequence(); + + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs new file mode 100644 index 0000000..aaf55ab --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/PushPopSetOf.cs @@ -0,0 +1,585 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class PushPopSetOf : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSetOf()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopNewWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Assert.Throws( + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(); + + Assert.Throws( + () => writer.PopSetOf()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PopBalancedWriter_CustomTag(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(); + + Assert.Throws( + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustom_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + + Assert.Throws( + () => writer.PopSetOf()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopCustom(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + + Assert.Throws( + () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushPrimitive_PopStandard(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + writer.PopSetOf(); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "31800000"); + } + else + { + Verify(writer, "3100"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomPrimitive_PopConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.Private, 5)); + writer.PopSetOf(new Asn1Tag(TagClass.Private, 5, true)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "E5800000"); + } + else + { + Verify(writer, "E500"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushStandard_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); + + if (ruleSet == AsnEncodingRules.CER) + { + Verify(writer, "31800000"); + } + else + { + Verify(writer, "3100"); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushCustomConstructed_PopPrimitive(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); + writer.PopSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet)); + + byte tag = (byte)((int)ruleSet | 0b1110_0000); + string tagHex = tag.ToString("X2"); + string rest = ruleSet == AsnEncodingRules.CER ? "800000" : "00"; + + Verify(writer, tagHex + rest); + } + + [Fact] + public static void BER_WritesDefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "3100"); + } + + [Fact] + public static void CER_WritesIndefinite_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "31800000"); + } + + [Fact] + public static void DER_WritesDefinite_CustomTag_Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.PushSetOf(); + writer.PopSetOf(); + + Verify(writer, "3100"); + } + + [Fact] + public static void BER_WritesDefinite_CustomTag__Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "EF00"); + } + + [Fact] + public static void CER_WritesIndefinite_CustomTag__Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "7F5B800000"); + } + + [Fact] + public static void DER_WritesDefinite_CustomTag__Empty() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); + writer.PushSetOf(tag); + writer.PopSetOf(tag); + + Verify(writer, "BE00"); + } + + private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) + { + // Written in pre-sorted order, since sorting is a different test. + writer.PushSetOf(); + { + writer.PushSetOf(); + { + writer.PushSetOf(alt); + { + writer.PushSetOf(); + writer.PopSetOf(); + } + + writer.PopSetOf(alt); + } + + writer.PopSetOf(); + + writer.PushSetOf(alt); + writer.PopSetOf(alt); + } + + writer.PopSetOf(); + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); + + TestNested(writer, alt, "310A3105FF7F023100FF7F00"); + } + + [Fact] + public static void CER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); + + TestNested(writer, alt, "31803180AC803180000000000000AC8000000000"); + } + + [Fact] + public static void DER_Nested() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); + + TestNested(writer, alt, "31083104650231006500"); + } + + private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) + { + writer.PushSetOf(); + + // F00DF00D...F00DF00D + byte[] contentBytes = new byte[126]; + + for (int i = 0; i < contentBytes.Length; i += 2) + { + contentBytes[i] = 0xF0; + contentBytes[i + 1] = 0x0D; + } + + writer.WriteOctetString(contentBytes); + writer.PopSetOf(); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public static void SimpleContentShift(AsnEncodingRules ruleSet) + { + const string ExpectedHex = + "318180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; + + AsnWriter writer = new AsnWriter(ruleSet); + SimpleContentShiftCore(writer, ExpectedHex); + } + + [Fact] + public static void SimpleContentShift_CER() + { + const string ExpectedHex = + "3180" + + "047E" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + + "0000"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + SimpleContentShiftCore(writer, ExpectedHex); + } + + private static void ValidateDataSorting(AsnEncodingRules ruleSet, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.PushSetOf(); + + // 02 01 FF + writer.WriteInteger(-1); + // 02 01 00 + writer.WriteInteger(0); + // 02 02 00 FF + writer.WriteInteger(255); + // 01 01 FF + writer.WriteBoolean(true); + // 45 01 00 + writer.WriteBoolean(false, new Asn1Tag(TagClass.Application, 5)); + // 02 01 7F + writer.WriteInteger(127); + // 02 01 80 + writer.WriteInteger(sbyte.MinValue); + // 02 02 00 FE + writer.WriteInteger(254); + // 02 01 00 + writer.WriteInteger(0); + + writer.PopSetOf(); + + // The correct sort order (CER, DER) is + // Universal Boolean: true + // Universal Integer: 0 + // Universal Integer: 0 + // Universal Integer: 127 + // Universal Integer: -128 + // Universal Integer: -1 + // Universal Integer: 254 + // Universal Integer: 255 + // Application 5 (Boolean): false + + // This test would be + // + // GrabBag ::= SET OF GrabBagItem + // + // GrabBagItem ::= CHOICE ( + // value INTEGER + // bool BOOLEAN + // grr [APPLICATION 5] IMPLICIT BOOLEAN + // ) + + Verify(writer, expectedHex); + } + + [Fact] + public static void BER_DoesNotSort() + { + const string ExpectedHex = + "311D" + + "0201FF" + + "020100" + + "020200FF" + + "0101FF" + + "450100" + + "02017F" + + "020180" + + "020200FE" + + "020100"; + + ValidateDataSorting(AsnEncodingRules.BER, ExpectedHex); + } + + [Fact] + public static void CER_SortsData() + { + const string ExpectedHex = + "3180" + + "0101FF" + + "020100" + + "020100" + + "02017F" + + "020180" + + "0201FF" + + "020200FE" + + "020200FF" + + "450100" + + "0000"; + + ValidateDataSorting(AsnEncodingRules.CER, ExpectedHex); + } + + [Fact] + public static void DER_SortsData() + { + const string ExpectedHex = + "311D" + + "0101FF" + + "020100" + + "020100" + + "02017F" + + "020180" + + "0201FF" + + "020200FE" + + "020200FF" + + "450100"; + + ValidateDataSorting(AsnEncodingRules.DER, ExpectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, true)] + public static void CannotEncodeWhileUnbalanced(AsnEncodingRules ruleSet, bool customTag) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); + } + else + { + writer.PushSetOf(); + } + + int written = -5; + + Assert.Throws(() => writer.GetEncodedLength()); + Assert.Throws(() => writer.Encode()); + Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); + Assert.Equal(-5, written); + + byte[] buf = new byte[10]; + Assert.Throws(() => writer.TryEncode(buf, out written)); + Assert.Throws(() => writer.EncodedValueEquals(buf)); + Assert.Equal(-5, written); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => writer.PushSetOf(Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSequence_PopSetOf(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + writer.PushSequence(tag); + + Assert.Throws( + () => writer.PopSetOf(tag)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_IdempotentDispose(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + + using (var scope = writer.PushSetOf(tag)) + { + writer.PopSetOf(tag); + + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_IdempotentDispose_Complex(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + + string expectedHex = ruleSet == AsnEncodingRules.CER ? "A3800000" : "A300"; + expectedHex += expectedHex; + + using (var scope = writer.PushSetOf(tag)) + { + writer.PopSetOf(tag); + + writer.PushSetOf(tag); + // The state is wrong now, so Dispose does nothing + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + writer.PopSetOf(tag); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_DisposeThrowsIfDeepContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + var scope = writer.PushSetOf(); + writer.PushSequence(); + + Assert.Throws(() => scope.Dispose()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void PushSetOf_DisposeSilentNotContained(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + using (var scope = writer.PushSetOf()) + { + writer.PopSetOf(); + + // Since this has a different write offset, the equals is false, so no exception. + writer.PushSetOf(); + writer.PushSequence(); + + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs b/src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs new file mode 100644 index 0000000..67c675f --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/SimpleWriterTests.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public static class SimpleWriterTests + { + [Theory] + [InlineData(-1)] + [InlineData(3)] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + public static void ValidateRuleSet(int value) + { + AssertExtensions.Throws( + "ruleSet", + () => new AsnWriter((AsnEncodingRules)value)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EncodeEmpty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + byte[] encoded = writer.Encode(); + Assert.Same(Array.Empty(), encoded); + + Assert.True(writer.TryEncode(Span.Empty, out int written)); + Assert.Equal(0, written); + Assert.True(writer.EncodedValueEquals(ReadOnlySpan.Empty)); + + Span negativeTest = stackalloc byte[] { 5, 0 }; + Assert.False(writer.EncodedValueEquals(negativeTest)); + } + + [Fact] + public static void DisposeDefaultScope() + { + AsnWriter.Scope scope = default; + // Assert.NoThrow + scope.Dispose(); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_Empty(AsnEncodingRules ruleSet) + { + AsnWriter empty = new AsnWriter(ruleSet); + AsnWriter dest = new AsnWriter(AsnEncodingRules.BER); + + Assert.Throws(() => empty.CopyTo(dest)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_TwoValues(AsnEncodingRules ruleSet) + { + AsnWriter source = new AsnWriter(ruleSet); + AsnWriter dest = new AsnWriter(AsnEncodingRules.BER); + + source.WriteBoolean(false); + source.WriteBoolean(true); + + Assert.Throws(() => source.CopyTo(dest)); + } + + [Fact] + public static void CopyTo_IncompatibleRules() + { + AsnWriter cer = new AsnWriter(AsnEncodingRules.CER); + AsnWriter der = new AsnWriter(AsnEncodingRules.DER); + + using (cer.PushSequence()) + using (der.PushSequence()) + { + cer.WriteBoolean(false); + der.WriteBoolean(true); + } + + Assert.Throws(() => cer.CopyTo(der)); + Assert.Throws(() => der.CopyTo(cer)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_Success(AsnEncodingRules ruleSet) + { + AsnWriter source = new AsnWriter(ruleSet); + AsnWriter dest = new AsnWriter(AsnEncodingRules.BER); + + Assert.True(source.EncodedValueEquals(dest)); + + source.WriteBoolean(false); + Assert.False(source.EncodedValueEquals(dest)); + + source.CopyTo(dest); + Assert.True(source.EncodedValueEquals(dest)); + + Assert.Equal(source.Encode(), dest.Encode()); + + source.CopyTo(dest); + Assert.False(source.EncodedValueEquals(dest)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EncodedValueEquals_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "other", + () => writer.EncodedValueEquals((AsnWriter)null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void CopyTo_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "destination", + () => writer.CopyTo(null)); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs similarity index 88% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs index 890e04f..aeb39de 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBMPString.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBMPString.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteBMPString : WriteCharacterString { @@ -67,13 +66,13 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 [MemberData(nameof(InvalidInputs))] public void VerifyWrite_Span_NonEncodable(string input) => base.VerifyWrite_Span_NonEncodable_Helper(input); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_Span(bool empty) => - base.WriteAfterDispose_Span_Helper(empty); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_String(bool empty) => - base.WriteAfterDispose_String_Helper(empty); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs new file mode 100644 index 0000000..d70c5ad --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBitString.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteBitString : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteEmpty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(ReadOnlySpan.Empty); + + Verify(writer, "030100"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 1, 1, "030201")] + [InlineData(AsnEncodingRules.CER, 2, 1, "030301")] + [InlineData(AsnEncodingRules.DER, 3, 1, "030401")] + [InlineData(AsnEncodingRules.BER, 126, 0, "037F00")] + [InlineData(AsnEncodingRules.CER, 127, 3, "03818003")] + [InlineData(AsnEncodingRules.BER, 999, 0, "038203E800")] + [InlineData(AsnEncodingRules.CER, 999, 0, "038203E800")] + [InlineData(AsnEncodingRules.DER, 999, 0, "038203E800")] + [InlineData(AsnEncodingRules.BER, 1000, 0, "038203E900")] + [InlineData(AsnEncodingRules.DER, 1000, 0, "038203E900")] + [InlineData(AsnEncodingRules.BER, 2000, 0, "038207D100")] + [InlineData(AsnEncodingRules.DER, 2000, 0, "038207D100")] + public void WritePrimitive(AsnEncodingRules ruleSet, int length, int unusedBitCount, string hexStart) + { + string payloadHex = new string('0', 2 * length); + string expectedHex = hexStart + payloadHex; + byte[] data = new byte[length]; + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(data, unusedBitCount); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(1000, 1, "2380038203E800", "030201")] + [InlineData(999*2, 3, "2380038203E800", "038203E803")] + public void WriteSegmentedCER(int length, int unusedBitCount, string hexStart, string hexStart2) + { + string payload1Hex = new string('8', 999 * 2); + string payload2Hex = new string('8', (length - 999) * 2); + string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; + byte[] data = new byte[length]; + + for (int i = 0; i < data.Length; i++) + { + data[i] = 0x88; + } + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteBitString(data, unusedBitCount); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, false)] + [InlineData(AsnEncodingRules.CER, 0, false)] + [InlineData(AsnEncodingRules.DER, 0, false)] + [InlineData(AsnEncodingRules.BER, 999, false)] + [InlineData(AsnEncodingRules.CER, 999, false)] + [InlineData(AsnEncodingRules.DER, 999, false)] + [InlineData(AsnEncodingRules.BER, 1000, false)] + [InlineData(AsnEncodingRules.CER, 1000, true)] + [InlineData(AsnEncodingRules.DER, 1000, false)] + [InlineData(AsnEncodingRules.BER, 1998, false)] + [InlineData(AsnEncodingRules.CER, 1998, true)] + [InlineData(AsnEncodingRules.BER, 4096, false)] + [InlineData(AsnEncodingRules.CER, 4096, true)] + [InlineData(AsnEncodingRules.DER, 4096, false)] + public void VerifyWriteBitString_PrimitiveOrConstructed( + AsnEncodingRules ruleSet, + int payloadLength, + bool expectConstructed) + { + byte[] data = new byte[payloadLength]; + + Asn1Tag[] tagsToTry = + { + new Asn1Tag(UniversalTagNumber.BitString), + new Asn1Tag(UniversalTagNumber.BitString, isConstructed: true), + new Asn1Tag(TagClass.Private, 87), + new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), + }; + + byte[] answerBuf = new byte[payloadLength + 100]; + + foreach (Asn1Tag toTry in tagsToTry) + { + Asn1Tag writtenTag; + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBitString(data, tag: toTry); + + Assert.True(writer.TryEncode(answerBuf, out _)); + Assert.True(Asn1Tag.TryDecode(answerBuf, out writtenTag, out _)); + + if (expectConstructed) + { + Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + else + { + Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + + Assert.Equal(toTry.TagClass, writtenTag.TagClass); + Assert.Equal(toTry.TagValue, writtenTag.TagValue); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "FF", false)] + [InlineData(AsnEncodingRules.BER, 1, "FE", false)] + [InlineData(AsnEncodingRules.CER, 1, "FE", false)] + [InlineData(AsnEncodingRules.DER, 1, "FE", false)] + [InlineData(AsnEncodingRules.BER, 1, "FF", true)] + [InlineData(AsnEncodingRules.CER, 1, "FF", true)] + [InlineData(AsnEncodingRules.DER, 1, "FF", true)] + [InlineData(AsnEncodingRules.BER, 7, "C0", true)] + [InlineData(AsnEncodingRules.CER, 7, "C0", true)] + [InlineData(AsnEncodingRules.DER, 7, "C0", true)] + [InlineData(AsnEncodingRules.BER, 7, "80", false)] + [InlineData(AsnEncodingRules.CER, 7, "80", false)] + [InlineData(AsnEncodingRules.DER, 7, "80", false)] + [InlineData(AsnEncodingRules.DER, 7, "40", true)] + [InlineData(AsnEncodingRules.DER, 6, "40", false)] + [InlineData(AsnEncodingRules.DER, 6, "C0", false)] + [InlineData(AsnEncodingRules.DER, 6, "20", true)] + [InlineData(AsnEncodingRules.DER, 5, "20", false)] + [InlineData(AsnEncodingRules.DER, 5, "A0", false)] + [InlineData(AsnEncodingRules.DER, 5, "10", true)] + [InlineData(AsnEncodingRules.DER, 4, "10", false)] + [InlineData(AsnEncodingRules.DER, 4, "90", false)] + [InlineData(AsnEncodingRules.DER, 4, "30", false)] + [InlineData(AsnEncodingRules.DER, 4, "08", true)] + [InlineData(AsnEncodingRules.DER, 4, "88", true)] + [InlineData(AsnEncodingRules.DER, 3, "08", false)] + [InlineData(AsnEncodingRules.DER, 3, "A8", false)] + [InlineData(AsnEncodingRules.DER, 3, "04", true)] + [InlineData(AsnEncodingRules.DER, 3, "14", true)] + [InlineData(AsnEncodingRules.DER, 2, "04", false)] + [InlineData(AsnEncodingRules.DER, 2, "0C", false)] + [InlineData(AsnEncodingRules.DER, 2, "FC", false)] + [InlineData(AsnEncodingRules.DER, 2, "02", true)] + [InlineData(AsnEncodingRules.DER, 2, "82", true)] + [InlineData(AsnEncodingRules.DER, 2, "FE", true)] + [InlineData(AsnEncodingRules.DER, 1, "02", false)] + [InlineData(AsnEncodingRules.DER, 1, "82", false)] + [InlineData(AsnEncodingRules.DER, 1, "80", false)] + public static void WriteBitString_UnusedBitCount_MustBeValid( + AsnEncodingRules ruleSet, + int unusedBitCount, + string inputHex, + bool expectThrow) + { + byte[] inputBytes = inputHex.HexToByteArray(); + + AsnWriter writer = new AsnWriter(ruleSet); + + if (expectThrow) + { + AssertExtensions.Throws( + "unusedBitCount", + () => writer.WriteBitString(inputBytes, unusedBitCount)); + + AssertExtensions.Throws( + "unusedBitCount", + () => writer.WriteBitString( + inputBytes, + unusedBitCount, + new Asn1Tag(TagClass.ContextSpecific, 3))); + + return; + } + + byte[] output = new byte[512]; + writer.WriteBitString(inputBytes, unusedBitCount); + Assert.True(writer.TryEncode(output, out int bytesWritten)); + + // This assumes that inputBytes is never more than 999 (and avoids CER constructed forms) + Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); + + writer.WriteBitString(inputBytes, unusedBitCount, new Asn1Tag(TagClass.ContextSpecific, 9)); + Assert.True(writer.TryEncode(output, out bytesWritten)); + + Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, -1)] + [InlineData(AsnEncodingRules.CER, -1)] + [InlineData(AsnEncodingRules.DER, -1)] + [InlineData(AsnEncodingRules.BER, -2)] + [InlineData(AsnEncodingRules.CER, -2)] + [InlineData(AsnEncodingRules.DER, -2)] + [InlineData(AsnEncodingRules.BER, 8)] + [InlineData(AsnEncodingRules.CER, 8)] + [InlineData(AsnEncodingRules.DER, 8)] + [InlineData(AsnEncodingRules.BER, 9)] + [InlineData(AsnEncodingRules.CER, 9)] + [InlineData(AsnEncodingRules.DER, 9)] + [InlineData(AsnEncodingRules.BER, 1048576)] + [InlineData(AsnEncodingRules.CER, 1048576)] + [InlineData(AsnEncodingRules.DER, 1048576)] + public static void UnusedBitCounts_Bounds(AsnEncodingRules ruleSet, int unusedBitCount) + { + byte[] data = new byte[5]; + + AsnWriter writer = new AsnWriter(ruleSet); + + ArgumentOutOfRangeException exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(data, unusedBitCount)); + + Assert.Equal(unusedBitCount, exception.ActualValue); + + exception = AssertExtensions.Throws( + nameof(unusedBitCount), + () => writer.WriteBitString(data, unusedBitCount, new Asn1Tag(TagClass.ContextSpecific, 5))); + + Assert.Equal(unusedBitCount, exception.ActualValue); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void EmptyData_Requires0UnusedBits(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); + + Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 1, contextTag)); + + Assert.Throws( + "unusedBitCount", + () => writer.WriteBitString(ReadOnlySpan.Empty, 7, contextTag)); + + writer.WriteBitString(ReadOnlySpan.Empty, 0); + writer.WriteBitString(ReadOnlySpan.Empty, 0, contextTag); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, TagClass.Universal, 3, "030100")] + [InlineData(AsnEncodingRules.CER, TagClass.Universal, 3, "030100")] + [InlineData(AsnEncodingRules.DER, TagClass.Universal, 3, "030100")] + [InlineData(AsnEncodingRules.BER, TagClass.Private, 1, "C10100")] + [InlineData(AsnEncodingRules.CER, TagClass.Application, 5, "450100")] + [InlineData(AsnEncodingRules.DER, TagClass.ContextSpecific, 32, "9F200100")] + public static void EmptyData_Allows0UnusedBits( + AsnEncodingRules ruleSet, + TagClass tagClass, + int tagValue, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (tagClass == TagClass.Universal) + { + Debug.Assert(tagValue == (int)UniversalTagNumber.BitString); + writer.WriteBitString(ReadOnlySpan.Empty, 0); + } + else + { + writer.WriteBitString(ReadOnlySpan.Empty, 0, new Asn1Tag(tagClass, tagValue)); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteBitString_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(ReadOnlySpan.Empty, tag: Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBitString(new byte[1], tag: Asn1Tag.Null)); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs new file mode 100644 index 0000000..b558b84 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteBoolean.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteBoolean : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER, false, "010100")] + [InlineData(AsnEncodingRules.BER, true, "0101FF")] + [InlineData(AsnEncodingRules.CER, false, "010100")] + [InlineData(AsnEncodingRules.CER, true, "0101FF")] + [InlineData(AsnEncodingRules.DER, false, "010100")] + [InlineData(AsnEncodingRules.DER, true, "0101FF")] + public void VerifyWriteBoolean(AsnEncodingRules ruleSet, bool value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false, "830100")] + [InlineData(AsnEncodingRules.BER, true, "8301FF")] + [InlineData(AsnEncodingRules.CER, false, "830100")] + [InlineData(AsnEncodingRules.CER, true, "8301FF")] + [InlineData(AsnEncodingRules.DER, false, "830100")] + [InlineData(AsnEncodingRules.DER, true, "8301FF")] + public void VerifyWriteBoolean_Context3(AsnEncodingRules ruleSet, bool value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value, new Asn1Tag(TagClass.ContextSpecific, 3)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.DER, true)] + public void VerifyWriteBoolean_Null(AsnEncodingRules ruleSet, bool value) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteBoolean(value, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.DER, true)] + public void VerifyWriteBoolean_ConstructedIgnored(AsnEncodingRules ruleSet, bool value) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteBoolean(value, new Asn1Tag(TagClass.ContextSpecific, 7, true)); + writer.WriteBoolean(value, new Asn1Tag(UniversalTagNumber.Boolean, true)); + + if (value) + { + Verify(writer, "8701FF0101FF"); + } + else + { + Verify(writer, "870100010100"); + } + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs new file mode 100644 index 0000000..c299053 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteCharacterString.cs @@ -0,0 +1,421 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public abstract class WriteCharacterString : Asn1WriterTests + { + internal abstract void WriteString(AsnWriter writer, string s); + internal abstract void WriteString(AsnWriter writer, Asn1Tag tag, string s); + + internal abstract void WriteSpan(AsnWriter writer, ReadOnlySpan s); + internal abstract void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s); + + internal abstract Asn1Tag StandardTag { get; } + + protected const string GettysburgAddress = + "Four score and seven years ago our fathers brought forth on this continent, a new nation, " + + "conceived in Liberty, and dedicated to the proposition that all men are created equal.\r\n" + + "\r\n" + + "Now we are engaged in a great civil war, testing whether that nation, or any nation so " + + "conceived and so dedicated, can long endure. We are met on a great battle-field of that " + + "war. We have come to dedicate a portion of that field, as a final resting place for those " + + "who here gave their lives that that nation might live. It is altogether fitting and proper " + + "that we should do this.\r\n" + + "\r\n" + + "But, in a larger sense, we can not dedicate-we can not consecrate-we can not hallow-this " + + "ground. The brave men, living and dead, who struggled here, have consecrated it, far above " + + "our poor power to add or detract. The world will little note, nor long remember what we say " + + "here, but it can never forget what they did here. It is for us the living, rather, to be " + + "dedicated here to the unfinished work which they who fought here have thus far so nobly " + + "advanced. It is rather for us to be here dedicated to the great task remaining before " + + "us-that from these honored dead we take increased devotion to that cause for which they " + + "gave the last full measure of devotion-that we here highly resolve that these dead shall " + + "not have died in vain-that this nation, under God, shall have a new birth of freedom-and " + + "that government of the people, by the people, for the people, shall not perish from the earth."; + + protected void VerifyWrite_BER_String_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_String_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 14); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 19); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteString(writer, input); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 2); + WriteString(writer, tag, input); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, int.MaxValue >> 1); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 30); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + WriteSpan(writer, input.AsSpan()); + + Verify(writer, Stringify(StandardTag) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_CustomTag_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 31); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(tag) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_BER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 1701, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_CER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteString(writer, tag, input); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteString(writer, tag, input); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(standard) + expectedPayloadHex); + } + + protected void VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); + Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); + WriteSpan(writer, tag, input.AsSpan()); + + Verify(writer, Stringify(expected) + expectedPayloadHex); + } + + protected void VerifyWrite_String_Null_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "value", + () => WriteString(writer, null)); + } + + protected void VerifyWrite_String_Null_CustomTag_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "value", + () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); + } + + protected void VerifyWrite_Null_String_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => WriteString(writer, Asn1Tag.Null, "hi")); + } + + protected void VerifyWrite_Null_Span_Helper(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => WriteSpan(writer, Asn1Tag.Null, "hi".AsSpan())); + } + + private void VerifyWrite_CERSegmented_Helper(AsnWriter writer, string tagHex, int contentByteCount) + { + int div = Math.DivRem(contentByteCount, 1000, out int rem); + + // tag, length(80), div full segments at 1004 bytes each, and the end of contents. + int encodedSize = (tagHex.Length / 2) + 1 + 1004 * div + 2; + + if (rem != 0) + { + // tag, contents (length TBD) + encodedSize += 1 + rem; + + if (encodedSize < 0x80) + encodedSize++; + else if (encodedSize <= 0xFF) + encodedSize += 2; + else + encodedSize += 3; + } + + byte[] encoded = writer.Encode(); + + Assert.Equal(tagHex, encoded.AsSpan(0, tagHex.Length / 2).ByteArrayToHex()); + Assert.Equal("0000", encoded.AsSpan(encoded.Length - 2).ByteArrayToHex()); + Assert.Equal(encodedSize, encoded.Length); + } + + protected void VerifyWrite_CERSegmented_String_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteString(writer, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_String_CustomTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); + string tagHex = Stringify(tag); + + WriteString(writer, tag, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_String_ConstructedTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteString(writer, tag, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_String_CustomPrimitiveTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); + Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); + string tagHex = Stringify(constr); + + WriteString(writer, prim, input); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_CustomTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, tag, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_ConstructedTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag standard = StandardTag; + Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); + string tagHex = Stringify(tag); + + WriteSpan(writer, tag, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag_Helper(string input, int contentByteCount) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); + Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); + string tagHex = Stringify(constr); + + WriteSpan(writer, prim, input.AsSpan()); + VerifyWrite_CERSegmented_Helper(writer, tagHex, contentByteCount); + } + + protected void VerifyWrite_String_NonEncodable_Helper(string input) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Assert.Throws(() => WriteString(writer, input)); + } + + protected void VerifyWrite_Span_NonEncodable_Helper(string input) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Assert.Throws(() => WriteSpan(writer, input.AsSpan())); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs new file mode 100644 index 0000000..03c1e92 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEncodedValue.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteEncodedValue : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyNoEmptyValues(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "value", + () => writer.WriteEncodedValue(ReadOnlySpan.Empty)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyCurrentEncodingMattersForLength(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + byte[] wideNull = { 0x05, 0x81, 0x00 }; + + if (ruleSet == AsnEncodingRules.BER) + { + writer.WriteEncodedValue(wideNull); + Verify(writer, "058100"); + } + else + { + AssertExtensions.Throws( + "value", + () => writer.WriteEncodedValue(wideNull)); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyTrailingBytesDisallowed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + byte[] nullAndARogueByte = { 0x05, 0x00, 0x00 }; + + AssertExtensions.Throws( + "value", + () => writer.WriteEncodedValue(nullAndARogueByte)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteComplexEncodedValue(AsnEncodingRules ruleSet) + { + string inputHex = + "3082027A020100300D06092A864886F70D010101050004820264308202600201" + + "0002818200BCACB1A5349D7B35A580AC3B3998EB15EBF900ECB329BF1F75717A" + + "00B2199C8A18D791B592B7EC52BD5AF2DB0D3B635F0595753DFF7BA7C9872DBF" + + "7E3226DEF44A07CA568D1017992C2B41BFE5EC3570824CF1F4B15919FED513FD" + + "A56204AF2034A2D08FF04C2CCA49D168FA03FA2FA32FCCD3484C15F0A2E5467C" + + "76FC760B5509020301000102818110D7A0A704F69EE247D31FECCC84324E1B69" + + "B7B3A97DAB4639636716EB4F1E7A7463BFE9D3BE4FDE05F1B9B6A4AC7DBF247E" + + "364051CF5DC7BF65ADCFABD5ECF6A2B627171F6798541F1BF11CAC9AA56A6B2B" + + "C9C1082616651AB1AE6C02E10C7C8802C24A6B4D181087FD241D0753782CF4CD" + + "0355F8FD15791B49C90022BE3CE45502410E15300A9D34BA37B6BDA831BC6727" + + "B2F7F6D0EFB7B33A99C9AF28CFD625E245A54F251B784C4791ADA585ADB711D9" + + "300A3D52B450CC307F55D31E1217B9FFD74502410D65C60DE8B6F54A7756FD1C" + + "CBA76CE41EF446D024031EE9C5A40931B07336CFED35A8EE580E19DB8592CB0F" + + "266EC69028EB9E98E3E84FF1A459A8A26860A610F502410D9DB4BE7E730D9D72" + + "A57B2AE3738571C7C82F09A7BEB5E91D94AACC10CCBE33027B3C708BE68CC830" + + "71BA87545B00782F5E4D49A4595886B56F9342810848725502410CF6FBDDE1E1" + + "8B2570AF2169883A90C9809AEB1BE87D8CA0B4BDB497FD24C15A1D36DC2F29CF" + + "1B7EAF980A20B31467DA817EE18F1A9D691F71E7C1A4C8551EDF310241010CE9" + + "936E96FBADF87240CC419D01081BB67C981D44314E58583AC7FE9379EA0272E6" + + "C4C7C14638E1D5ECE7840DDB15A12D7054A418F8764FA54CE134EBD2635E"; + + string expectedOutput = "30820282" + "A082027E" + inputHex; + + byte[] input = inputHex.HexToByteArray(); + AsnWriter writer = new AsnWriter(ruleSet); + + using (writer.PushSequence()) + using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + writer.WriteEncodedValue(input); + } + + Verify(writer, expectedOutput); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs new file mode 100644 index 0000000..2ea0786 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteEnumerated.cs @@ -0,0 +1,373 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Formats.Asn1.Tests.Reader; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteEnumerated : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.SByteBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.SByteBacked.Pillow, true, "9E01EF")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.SByteBacked.Fluff, false, "0A0153")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.SByteBacked.Fluff, true, "9E0153")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.SByteBacked)(-127), true, "9E0181")] + public void VerifyWriteEnumerated_SByte( + AsnEncodingRules ruleSet, + ReadEnumerated.SByteBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 30)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ByteBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.ByteBacked.NotFluffy, true, "9A010B")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.ByteBacked.Fluff, false, "0A010C")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ByteBacked.Fluff, true, "9A010C")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ByteBacked)253, false, "0A0200FD")] + public void VerifyWriteEnumerated_Byte( + AsnEncodingRules ruleSet, + ReadEnumerated.ByteBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 26)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ShortBacked.Zero, true, "DF81540100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.ShortBacked.Pillow, true, "DF815402FC00")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.ShortBacked.Fluff, false, "0A020209")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ShortBacked.Fluff, true, "DF8154020209")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ShortBacked)25321, false, "0A0262E9")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ShortBacked)(-12345), false, "0A02CFC7")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ShortBacked)(-1), true, "DF815401FF")] + public void VerifyWriteEnumerated_Short( + AsnEncodingRules ruleSet, + ReadEnumerated.ShortBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Private, 212)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, true, "4D0100")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.UShortBacked.Fluff, false, "0A03008000")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UShortBacked.Fluff, true, "4D03008000")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.UShortBacked)11, false, "0A010B")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.UShortBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.UShortBacked)ushort.MaxValue, true, "4D0300FFFF")] + public void VerifyWriteEnumerated_UShort( + AsnEncodingRules ruleSet, + ReadEnumerated.UShortBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Application, 13)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.IntBacked.Zero, true, "5F81FF7F0100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.IntBacked.Pillow, true, "5F81FF7F03FEFFFF")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.IntBacked.Fluff, false, "0A03010001")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.IntBacked.Fluff, true, "5F81FF7F03010001")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.IntBacked)25321, false, "0A0262E9")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.IntBacked)(-12345), false, "0A02CFC7")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.IntBacked)(-1), true, "5F81FF7F01FF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.IntBacked)int.MinValue, true, "5F81FF7F0480000000")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.IntBacked)int.MaxValue, false, "0A047FFFFFFF")] + public void VerifyWriteEnumerated_Int( + AsnEncodingRules ruleSet, + ReadEnumerated.IntBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Application, short.MaxValue)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, true, "9F610100")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.UIntBacked.Fluff, false, "0A050080000005")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.UIntBacked.Fluff, true, "9F61050080000005")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.UIntBacked)11, false, "0A010B")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.UIntBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.UIntBacked)ushort.MaxValue, true, "9F610300FFFF")] + public void VerifyWriteEnumerated_UInt( + AsnEncodingRules ruleSet, + ReadEnumerated.UIntBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 97)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.LongBacked.Zero, true, "800100")] + [InlineData(AsnEncodingRules.CER, ReadEnumerated.LongBacked.Pillow, true, "8005FF00000000")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.LongBacked.Fluff, false, "0A050200000441")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.LongBacked.Fluff, true, "80050200000441")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.LongBacked)25321, false, "0A0262E9")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.LongBacked)(-12345), false, "0A02CFC7")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.LongBacked)(-1), true, "8001FF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.LongBacked)int.MinValue, true, "800480000000")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.LongBacked)int.MaxValue, false, "0A047FFFFFFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.LongBacked)long.MinValue, false, "0A088000000000000000")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.LongBacked)long.MaxValue, true, "80087FFFFFFFFFFFFFFF")] + public void VerifyWriteEnumerated_Long( + AsnEncodingRules ruleSet, + ReadEnumerated.LongBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.ContextSpecific, 0)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, false, "0A0100")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, true, "C10100")] + [InlineData(AsnEncodingRules.DER, ReadEnumerated.ULongBacked.Fluff, false, "0A0900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.BER, ReadEnumerated.ULongBacked.Fluff, true, "C10900FACEF00DCAFEBEEF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ULongBacked)11, false, "0A010B")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.ULongBacked)short.MaxValue, false, "0A027FFF")] + [InlineData(AsnEncodingRules.BER, (ReadEnumerated.ULongBacked)ushort.MaxValue, true, "C10300FFFF")] + [InlineData(AsnEncodingRules.CER, (ReadEnumerated.ULongBacked)long.MaxValue, true, "C1087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, (ReadEnumerated.ULongBacked)ulong.MaxValue, false, "0A0900FFFFFFFFFFFFFFFF")] + public void VerifyWriteEnumerated_ULong( + AsnEncodingRules ruleSet, + ReadEnumerated.ULongBacked value, + bool customTag, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + + if (customTag) + { + writer.WriteEnumeratedValue(value, new Asn1Tag(TagClass.Private, 1)); + } + else + { + writer.WriteEnumeratedValue(value); + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyFlagsBased(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue(OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue( + OpenFlags.IncludeArchived, + new Asn1Tag(TagClass.ContextSpecific, 13))); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue((Enum)OpenFlags.IncludeArchived)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteEnumeratedValue( + (Enum)OpenFlags.IncludeArchived, + new Asn1Tag(TagClass.ContextSpecific, 13))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue(ReadEnumerated.IntBacked.Pillow, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteEnumeratedValue((Enum)ReadEnumerated.IntBacked.Pillow, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_NonNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "value", + () => writer.WriteEnumeratedValue(null)); + + AssertExtensions.Throws( + "value", + () => writer.WriteEnumeratedValue( + null, + new Asn1Tag(TagClass.ContextSpecific, 1))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.UIntBacked.Fluff); + + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.SByteBacked.Fluff); + + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.ULongBacked.Fluff); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteEnumeratedValue_Object_WithTag(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + + genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.UIntBacked.Fluff, tag); + + tag = new Asn1Tag(TagClass.Private, 4); + + genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.SByteBacked.Fluff, tag); + + tag = new Asn1Tag(TagClass.Application, 75); + + genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff, tag); + objWriter.WriteEnumeratedValue((Enum)ReadEnumerated.ULongBacked.Fluff, tag); + + Verify(objWriter, genWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteEnumeratedValue_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteEnumeratedValue( + ReadEnumerated.ULongBacked.Fluff, + new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true)); + + writer.WriteEnumeratedValue( + (Enum)ReadEnumerated.SByteBacked.Fluff, + new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + + Verify(writer, "0A0900FACEF00DCAFEBEEF" + "800153"); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs similarity index 58% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs index c195061..715f5ed 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteGeneralizedTime.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteGeneralizedTime.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteGeneralizedTime : Asn1WriterTests { @@ -135,12 +134,10 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 bool omitFractionalSeconds, string expectedHexPayload) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - writer.WriteGeneralizedTime(tag, input, omitFractionalSeconds); + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteGeneralizedTime(input, omitFractionalSeconds, tag); - Verify(writer, Stringify(tag) + expectedHexPayload); - } + Verify(writer, Stringify(tag) + expectedHexPayload); } [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, true)] - public void VerifyWriteGeneralizedTime_EndOfContents( - PublicEncodingRules ruleSet, + [InlineData(AsnEncodingRules.BER, false)] + [InlineData(AsnEncodingRules.CER, false)] + [InlineData(AsnEncodingRules.DER, false)] + [InlineData(AsnEncodingRules.BER, true)] + [InlineData(AsnEncodingRules.CER, true)] + [InlineData(AsnEncodingRules.DER, true)] + public void VerifyWriteGeneralizedTime_Null( + AsnEncodingRules ruleSet, bool omitFractionalSeconds) { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteGeneralizedTime(Asn1Tag.EndOfContents, DateTimeOffset.Now, omitFractionalSeconds)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteGeneralizedTime_IgnoresConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); + AsnWriter writer = new AsnWriter(ruleSet); - writer.WriteGeneralizedTime(new Asn1Tag(UniversalTagNumber.GeneralizedTime, true), value); - writer.WriteGeneralizedTime(new Asn1Tag(TagClass.ContextSpecific, 3, true), value); - Verify(writer, "180F32303137313131363137333530315A" + "830F32303137313131363137333530315A"); - } + AssertExtensions.Throws( + "tag", + () => writer.WriteGeneralizedTime(DateTimeOffset.Now, omitFractionalSeconds, Asn1Tag.Null)); } [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteGeneralizedTime_IgnoresConstructed(AsnEncodingRules ruleSet) { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteGeneralizedTime(DateTimeOffset.UtcNow)); - - Assert.Throws( - () => writer.WriteGeneralizedTime(DateTimeOffset.UtcNow, true)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteGeneralizedTime(Asn1Tag.Integer, DateTimeOffset.Now)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteGeneralizedTime(Asn1Tag.Integer, DateTimeOffset.Now, true)); - - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 18); - - Assert.Throws( - () => writer.WriteGeneralizedTime(tag, DateTimeOffset.Now)); + AsnWriter writer = new AsnWriter(ruleSet); + DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); - Assert.Throws( - () => writer.WriteGeneralizedTime(tag, DateTimeOffset.Now, true)); - } + writer.WriteGeneralizedTime(value, tag: new Asn1Tag(UniversalTagNumber.GeneralizedTime, true)); + writer.WriteGeneralizedTime(value, tag: new Asn1Tag(TagClass.ContextSpecific, 3, true)); + Verify(writer, "180F32303137313131363137333530315A" + "830F32303137313131363137333530315A"); } } } diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs similarity index 87% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs index 607ef4c..31165b9 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteIA5String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteIA5String.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteIA5String : WriteCharacterString { @@ -61,13 +60,13 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 [MemberData(nameof(InvalidInputs))] public void VerifyWrite_Span_NonEncodable(string input) => base.VerifyWrite_Span_NonEncodable_Helper(input); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_Span(bool empty) => - base.WriteAfterDispose_Span_Helper(empty); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_String(bool empty) => - base.WriteAfterDispose_String_Helper(empty); } } diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs new file mode 100644 index 0000000..7ea14a9 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteInteger.cs @@ -0,0 +1,454 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteInteger : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "020100")] + [InlineData(AsnEncodingRules.CER, 0, "020100")] + [InlineData(AsnEncodingRules.DER, 0, "020100")] + [InlineData(AsnEncodingRules.BER, -1, "0201FF")] + [InlineData(AsnEncodingRules.CER, -1, "0201FF")] + [InlineData(AsnEncodingRules.DER, -1, "0201FF")] + [InlineData(AsnEncodingRules.BER, -2, "0201FE")] + [InlineData(AsnEncodingRules.DER, sbyte.MinValue, "020180")] + [InlineData(AsnEncodingRules.BER, sbyte.MinValue + 1, "020181")] + [InlineData(AsnEncodingRules.CER, sbyte.MinValue - 1, "0202FF7F")] + [InlineData(AsnEncodingRules.DER, sbyte.MinValue - 2, "0202FF7E")] + [InlineData(AsnEncodingRules.BER, -256, "0202FF00")] + [InlineData(AsnEncodingRules.CER, -257, "0202FEFF")] + [InlineData(AsnEncodingRules.DER, short.MinValue, "02028000")] + [InlineData(AsnEncodingRules.BER, short.MinValue + 1, "02028001")] + [InlineData(AsnEncodingRules.CER, short.MinValue + byte.MaxValue, "020280FF")] + [InlineData(AsnEncodingRules.DER, short.MinValue - 1, "0203FF7FFF")] + [InlineData(AsnEncodingRules.BER, short.MinValue - 2, "0203FF7FFE")] + [InlineData(AsnEncodingRules.CER, -65281, "0203FF00FF")] + [InlineData(AsnEncodingRules.DER, -8388608, "0203800000")] + [InlineData(AsnEncodingRules.BER, -8388607, "0203800001")] + [InlineData(AsnEncodingRules.CER, -8388609, "0204FF7FFFFF")] + [InlineData(AsnEncodingRules.DER, -16777216, "0204FF000000")] + [InlineData(AsnEncodingRules.BER, -16777217, "0204FEFFFFFF")] + [InlineData(AsnEncodingRules.CER, int.MinValue, "020480000000")] + [InlineData(AsnEncodingRules.DER, int.MinValue + 1, "020480000001")] + [InlineData(AsnEncodingRules.BER, (long)int.MinValue - 1, "0205FF7FFFFFFF")] + [InlineData(AsnEncodingRules.CER, (long)int.MinValue - 2, "0205FF7FFFFFFE")] + [InlineData(AsnEncodingRules.DER, -4294967296, "0205FF00000000")] + [InlineData(AsnEncodingRules.BER, -4294967295, "0205FF00000001")] + [InlineData(AsnEncodingRules.CER, -4294967294, "0205FF00000002")] + [InlineData(AsnEncodingRules.DER, -4294967297, "0205FEFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, -549755813888, "02058000000000")] + [InlineData(AsnEncodingRules.CER, -549755813887, "02058000000001")] + [InlineData(AsnEncodingRules.DER, -549755813889, "0206FF7FFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, -549755813890, "0206FF7FFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, -140737488355328, "0206800000000000")] + [InlineData(AsnEncodingRules.DER, -140737488355327, "0206800000000001")] + [InlineData(AsnEncodingRules.BER, -140737488355329, "0207FF7FFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, -281474976710656, "0207FF000000000000")] + [InlineData(AsnEncodingRules.DER, -281474976710655, "0207FF000000000001")] + [InlineData(AsnEncodingRules.BER, -281474976710657, "0207FEFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, -36028797018963968, "020780000000000000")] + [InlineData(AsnEncodingRules.DER, -36028797018963967, "020780000000000001")] + [InlineData(AsnEncodingRules.DER, -36028797018963969, "0208FF7FFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, -36028797018963970, "0208FF7FFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, -72057594037927936, "0208FF00000000000000")] + [InlineData(AsnEncodingRules.DER, -72057594037927935, "0208FF00000000000001")] + [InlineData(AsnEncodingRules.BER, -72057594037927937, "0208FEFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, long.MinValue + 1, "02088000000000000001")] + [InlineData(AsnEncodingRules.DER, long.MinValue, "02088000000000000000")] + [InlineData(AsnEncodingRules.BER, 1, "020101")] + [InlineData(AsnEncodingRules.CER, 127, "02017F")] + [InlineData(AsnEncodingRules.DER, 126, "02017E")] + [InlineData(AsnEncodingRules.BER, 128, "02020080")] + [InlineData(AsnEncodingRules.CER, 129, "02020081")] + [InlineData(AsnEncodingRules.DER, 254, "020200FE")] + [InlineData(AsnEncodingRules.BER, 255, "020200FF")] + [InlineData(AsnEncodingRules.CER, 256, "02020100")] + [InlineData(AsnEncodingRules.DER, 32767, "02027FFF")] + [InlineData(AsnEncodingRules.BER, 32766, "02027FFE")] + [InlineData(AsnEncodingRules.CER, 32768, "0203008000")] + [InlineData(AsnEncodingRules.DER, 32769, "0203008001")] + [InlineData(AsnEncodingRules.BER, 65535, "020300FFFF")] + [InlineData(AsnEncodingRules.CER, 65534, "020300FFFE")] + [InlineData(AsnEncodingRules.DER, 65536, "0203010000")] + [InlineData(AsnEncodingRules.BER, 65537, "0203010001")] + [InlineData(AsnEncodingRules.CER, 8388607, "02037FFFFF")] + [InlineData(AsnEncodingRules.DER, 8388606, "02037FFFFE")] + [InlineData(AsnEncodingRules.BER, 8388608, "020400800000")] + [InlineData(AsnEncodingRules.CER, 8388609, "020400800001")] + [InlineData(AsnEncodingRules.DER, 16777215, "020400FFFFFF")] + [InlineData(AsnEncodingRules.BER, 16777214, "020400FFFFFE")] + [InlineData(AsnEncodingRules.CER, 16777216, "020401000000")] + [InlineData(AsnEncodingRules.DER, 16777217, "020401000001")] + [InlineData(AsnEncodingRules.BER, 2147483647, "02047FFFFFFF")] + [InlineData(AsnEncodingRules.CER, 2147483646, "02047FFFFFFE")] + [InlineData(AsnEncodingRules.DER, 2147483648, "02050080000000")] + [InlineData(AsnEncodingRules.BER, 2147483649, "02050080000001")] + [InlineData(AsnEncodingRules.BER, 4294967295, "020500FFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 4294967294, "020500FFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 4294967296, "02050100000000")] + [InlineData(AsnEncodingRules.BER, 4294967297, "02050100000001")] + [InlineData(AsnEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 549755813888, "0206008000000000")] + [InlineData(AsnEncodingRules.CER, 549755813889, "0206008000000001")] + [InlineData(AsnEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 1099511627776, "0206010000000000")] + [InlineData(AsnEncodingRules.DER, 1099511627777, "0206010000000001")] + [InlineData(AsnEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 140737488355328, "020700800000000000")] + [InlineData(AsnEncodingRules.BER, 140737488355329, "020700800000000001")] + [InlineData(AsnEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 281474976710656, "020701000000000000")] + [InlineData(AsnEncodingRules.CER, 281474976710657, "020701000000000001")] + [InlineData(AsnEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 36028797018963968, "02080080000000000000")] + [InlineData(AsnEncodingRules.DER, 36028797018963969, "02080080000000000001")] + [InlineData(AsnEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 72057594037927936, "02080100000000000000")] + [InlineData(AsnEncodingRules.BER, 72057594037927937, "02080100000000000001")] + [InlineData(AsnEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Long(AsnEncodingRules ruleSet, long value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "020100")] + [InlineData(AsnEncodingRules.CER, 0, "020100")] + [InlineData(AsnEncodingRules.DER, 0, "020100")] + [InlineData(AsnEncodingRules.BER, 1, "020101")] + [InlineData(AsnEncodingRules.CER, 127, "02017F")] + [InlineData(AsnEncodingRules.DER, 126, "02017E")] + [InlineData(AsnEncodingRules.BER, 128, "02020080")] + [InlineData(AsnEncodingRules.CER, 129, "02020081")] + [InlineData(AsnEncodingRules.DER, 254, "020200FE")] + [InlineData(AsnEncodingRules.BER, 255, "020200FF")] + [InlineData(AsnEncodingRules.CER, 256, "02020100")] + [InlineData(AsnEncodingRules.DER, 32767, "02027FFF")] + [InlineData(AsnEncodingRules.BER, 32766, "02027FFE")] + [InlineData(AsnEncodingRules.CER, 32768, "0203008000")] + [InlineData(AsnEncodingRules.DER, 32769, "0203008001")] + [InlineData(AsnEncodingRules.BER, 65535, "020300FFFF")] + [InlineData(AsnEncodingRules.CER, 65534, "020300FFFE")] + [InlineData(AsnEncodingRules.DER, 65536, "0203010000")] + [InlineData(AsnEncodingRules.BER, 65537, "0203010001")] + [InlineData(AsnEncodingRules.CER, 8388607, "02037FFFFF")] + [InlineData(AsnEncodingRules.DER, 8388606, "02037FFFFE")] + [InlineData(AsnEncodingRules.BER, 8388608, "020400800000")] + [InlineData(AsnEncodingRules.CER, 8388609, "020400800001")] + [InlineData(AsnEncodingRules.DER, 16777215, "020400FFFFFF")] + [InlineData(AsnEncodingRules.BER, 16777214, "020400FFFFFE")] + [InlineData(AsnEncodingRules.CER, 16777216, "020401000000")] + [InlineData(AsnEncodingRules.DER, 16777217, "020401000001")] + [InlineData(AsnEncodingRules.BER, 2147483647, "02047FFFFFFF")] + [InlineData(AsnEncodingRules.CER, 2147483646, "02047FFFFFFE")] + [InlineData(AsnEncodingRules.DER, 2147483648, "02050080000000")] + [InlineData(AsnEncodingRules.BER, 2147483649, "02050080000001")] + [InlineData(AsnEncodingRules.BER, 4294967295, "020500FFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 4294967294, "020500FFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 4294967296, "02050100000000")] + [InlineData(AsnEncodingRules.BER, 4294967297, "02050100000001")] + [InlineData(AsnEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 549755813888, "0206008000000000")] + [InlineData(AsnEncodingRules.CER, 549755813889, "0206008000000001")] + [InlineData(AsnEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 1099511627776, "0206010000000000")] + [InlineData(AsnEncodingRules.DER, 1099511627777, "0206010000000001")] + [InlineData(AsnEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 140737488355328, "020700800000000000")] + [InlineData(AsnEncodingRules.BER, 140737488355329, "020700800000000001")] + [InlineData(AsnEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 281474976710656, "020701000000000000")] + [InlineData(AsnEncodingRules.CER, 281474976710657, "020701000000000001")] + [InlineData(AsnEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.CER, 36028797018963968, "02080080000000000000")] + [InlineData(AsnEncodingRules.DER, 36028797018963969, "02080080000000000001")] + [InlineData(AsnEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.DER, 72057594037927936, "02080100000000000000")] + [InlineData(AsnEncodingRules.BER, 72057594037927937, "02080100000000000001")] + [InlineData(AsnEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] + [InlineData(AsnEncodingRules.BER, 9223372036854775808, "0209008000000000000000")] + [InlineData(AsnEncodingRules.CER, 9223372036854775809, "0209008000000000000001")] + [InlineData(AsnEncodingRules.DER, ulong.MaxValue, "020900FFFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, ulong.MaxValue-1, "020900FFFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_ULong(AsnEncodingRules ruleSet, ulong value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0", "020100")] + [InlineData(AsnEncodingRules.CER, "127", "02017F")] + [InlineData(AsnEncodingRules.DER, "128", "02020080")] + [InlineData(AsnEncodingRules.BER, "32767", "02027FFF")] + [InlineData(AsnEncodingRules.CER, "32768", "0203008000")] + [InlineData(AsnEncodingRules.DER, "9223372036854775807", "02087FFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.BER, "9223372036854775808", "0209008000000000000000")] + [InlineData(AsnEncodingRules.CER, "18446744073709551615", "020900FFFFFFFFFFFFFFFF")] + [InlineData(AsnEncodingRules.DER, "18446744073709551616", "0209010000000000000000")] + [InlineData(AsnEncodingRules.BER, "1339673755198158349044581307228491520", "02100102030405060708090A0B0C0D0E0F00")] + [InlineData(AsnEncodingRules.CER, "320182027492359845421654932427609477120", "021100F0E0D0C0B0A090807060504030201000")] + [InlineData(AsnEncodingRules.DER, "-1339673755198158349044581307228491520", "0210FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_BigInteger(AsnEncodingRules ruleSet, string decimalValue, string expectedHex) + { + BigInteger value = BigInteger.Parse(decimalValue); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "470100")] + [InlineData(AsnEncodingRules.CER, long.MinValue + 1, "47088000000000000001")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "47087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Application7_Long(AsnEncodingRules ruleSet, long value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.Application, 7)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, "890100")] + [InlineData(AsnEncodingRules.CER, 9223372036854775809, "8909008000000000000001")] + [InlineData(AsnEncodingRules.DER, 9223372036854775806, "89087FFFFFFFFFFFFFFE")] + public void VerifyWriteInteger_Context9_ULong(AsnEncodingRules ruleSet, ulong value, string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.ContextSpecific, 9)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, "0", "D00100")] + [InlineData(AsnEncodingRules.BER, "1339673755198158349044581307228491520", "D0100102030405060708090A0B0C0D0E0F00")] + [InlineData(AsnEncodingRules.CER, "320182027492359845421654932427609477120", "D01100F0E0D0C0B0A090807060504030201000")] + [InlineData(AsnEncodingRules.DER, "-1339673755198158349044581307228491520", "D010FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_Private16_BigInteger( + AsnEncodingRules ruleSet, + string decimalValue, + string expectedHex) + { + BigInteger value = BigInteger.Parse(decimalValue); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(value, new Asn1Tag(TagClass.Private, 16)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00")] + [InlineData("01")] + [InlineData("80")] + [InlineData("FF")] + [InlineData("0080")] + [InlineData("00FF")] + [InlineData("8000")] + [InlineData("00F0E0D0C0B0A090807060504030201000")] + [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_EncodedBytes(string valueHex) + { + string expectedHex = "02" + (valueHex.Length / 2).ToString("X2") + valueHex; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteInteger(valueHex.HexToByteArray()); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00")] + [InlineData("01")] + [InlineData("80")] + [InlineData("FF")] + [InlineData("0080")] + [InlineData("00FF")] + [InlineData("8000")] + [InlineData("00F0E0D0C0B0A090807060504030201000")] + [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] + public void VerifyWriteInteger_Context4_EncodedBytes(string valueHex) + { + string expectedHex = "84" + (valueHex.Length / 2).ToString("X2") + valueHex; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteInteger(valueHex.HexToByteArray(), new Asn1Tag(TagClass.ContextSpecific, 4)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00", false)] + [InlineData("7F", false)] + [InlineData("80", true)] + [InlineData("8002030405060708090A0B0C0D0E0F10", true)] + [InlineData("7F02030405060708090A0B0C0D0E0F10", false)] + [InlineData("FFFF", true)] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE", true)] + [InlineData("FF80", true)] + public void VerifyWriteUnsignedInteger(string valueHex, bool gainsPaddingByte) + { + int contentLength = (valueHex.Length / 2) + (gainsPaddingByte ? 1 : 0); + string expectedHex = $"02{contentLength:X2}{(gainsPaddingByte ? "00" : "")}{valueHex}"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteIntegerUnsigned(valueHex.HexToByteArray()); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("00", false)] + [InlineData("7F", false)] + [InlineData("80", true)] + [InlineData("8002030405060708090A0B0C0D0E0F10", true)] + [InlineData("7F02030405060708090A0B0C0D0E0F10", false)] + [InlineData("FFFF", true)] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE", true)] + [InlineData("FF80", true)] + public void VerifyWriteUnsignedInteger_Private7(string valueHex, bool gainsPaddingByte) + { + int contentLength = (valueHex.Length / 2) + (gainsPaddingByte ? 1 : 0); + string expectedHex = $"C7{contentLength:X2}{(gainsPaddingByte ? "00" : "")}{valueHex}"; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteIntegerUnsigned(valueHex.HexToByteArray(), new Asn1Tag(TagClass.Private, 7)); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + [InlineData("FFFF")] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] + [InlineData("FF80")] + public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + AssertExtensions.Throws( + "value", + () => writer.WriteInteger(valueHex.HexToByteArray())); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + [InlineData("FFFF")] + [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] + [InlineData("FF80")] + public void VerifyWriteInteger_Application3_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); + + AssertExtensions.Throws( + "value", + () => writer.WriteInteger(valueHex.HexToByteArray(), tag)); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + public void VerifyWriteIntegerUnsigned_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + AssertExtensions.Throws( + "value", + () => writer.WriteIntegerUnsigned(valueHex.HexToByteArray())); + } + + [Theory] + [InlineData("")] + [InlineData("0000")] + [InlineData("0000000000000000000001")] + [InlineData("0001")] + [InlineData("007F")] + public void VerifyWriteIntegerUnsigned_Application3_InvalidEncodedValue_Throws(string valueHex) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); + + AssertExtensions.Throws( + "value", + () => writer.WriteIntegerUnsigned(valueHex.HexToByteArray(), tag)); + } + + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteInteger_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(0L, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(0UL, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteInteger(BigInteger.Zero, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteInteger_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteInteger(0L, new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true)); + writer.WriteInteger(0L, new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + writer.WriteInteger(0UL, new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true)); + writer.WriteInteger(0UL, new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + writer.WriteInteger(BigInteger.Zero, new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true)); + writer.WriteInteger(BigInteger.Zero, new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true)); + + Verify(writer, "020100800100020100800100020100800100"); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs new file mode 100644 index 0000000..d6501f6 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNamedBitList.cs @@ -0,0 +1,383 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Formats.Asn1.Tests.Reader; +using System.Security.Cryptography.X509Certificates; +using Test.Cryptography; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteNamedBitList : Asn1WriterTests + { + [Theory] + [InlineData( + AsnEncodingRules.BER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.CER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.DER, + "030100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.BER, + "0309000000000000000003", + ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] + [InlineData( + AsnEncodingRules.CER, + "0309010000000080000002", + ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] + [InlineData( + AsnEncodingRules.DER, + "030204B0", + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] + public static void VerifyWriteNamedBitList( + AsnEncodingRules ruleSet, + string expectedHex, + Enum value) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(value); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData( + AsnEncodingRules.BER, + "C00100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.CER, + "410100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.DER, + "820100", + ReadNamedBitList.ULongFlags.None)] + [InlineData( + AsnEncodingRules.BER, + "C009000000000000000003", + ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] + [InlineData( + AsnEncodingRules.CER, + "4109010000000080000002", + ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] + [InlineData( + AsnEncodingRules.DER, + "820204B0", + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] + public static void VerifyWriteNamedBitList_WithTag( + AsnEncodingRules ruleSet, + string expectedHex, + Enum value) + { + int ruleSetVal = (int)ruleSet; + TagClass tagClass = (TagClass)(byte)(ruleSetVal << 6); + + if (tagClass == TagClass.Universal) + tagClass = TagClass.Private; + + Asn1Tag tag = new Asn1Tag(tagClass, ruleSetVal); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(value, tag); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; + + genWriter.WriteNamedBitList(flagsValue); + objWriter.WriteNamedBitList((Enum)flagsValue); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Generic_WithTag(AsnEncodingRules ruleSet) + { + AsnWriter objWriter = new AsnWriter(ruleSet); + AsnWriter genWriter = new AsnWriter(ruleSet); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); + + var flagsValue = + ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | + ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | + ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; + + genWriter.WriteNamedBitList(flagsValue, tag); + objWriter.WriteNamedBitList((Enum)flagsValue, tag); + + Verify(genWriter, objWriter.Encode().ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_NonNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((Enum)null)); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((Enum)null, new Asn1Tag(TagClass.ContextSpecific, 1))); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((BitArray)null)); + + AssertExtensions.Throws( + "value", + () => writer.WriteNamedBitList((BitArray)null, new Asn1Tag(TagClass.Private, 2))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_FlagsEnumRequired(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList(AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList( + AsnEncodingRules.BER, + new Asn1Tag(TagClass.ContextSpecific, 1))); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList((Enum)AsnEncodingRules.BER)); + + AssertExtensions.Throws( + "tEnum", + () => writer.WriteNamedBitList( + (Enum)AsnEncodingRules.BER, + new Asn1Tag(TagClass.ContextSpecific, 1))); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNamedBitList( + StringSplitOptions.RemoveEmptyEntries, + Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNamedBitList( + (Enum)StringSplitOptions.RemoveEmptyEntries, + Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + BitArray bits = new BitArray(18); + bits.Set(1, true); + bits.Set(15, true); + + writer.WriteNamedBitList(bits, new Asn1Tag(TagClass.Application, 4)); + + Verify(writer, "440406400100"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_Empty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + BitArray bits = new BitArray(0); + + writer.WriteNamedBitList(bits); + + Verify(writer, "030100"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_EveryPattern(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + byte[] allTheBytes = new byte[256]; + + for (int i = 0; i < allTheBytes.Length; i++) + { + allTheBytes[i] = (byte)i; + } + + BitArray bits = new BitArray(allTheBytes); + writer.WriteNamedBitList(bits, new Asn1Tag(TagClass.Private, 491)); + + const string ExpectedHex = + // Tag + "DF836B" + + // Length + "820101" + + // Unused bit count + "00" + + // Reversed bits for byte patterns 0x00-0x1F + "008040C020A060E0109050D030B070F0088848C828A868E8189858D838B878F8" + + // Reversed bits for byte patterns 0x20-0x3F + "048444C424A464E4149454D434B474F40C8C4CCC2CAC6CEC1C9C5CDC3CBC7CFC" + + // Reversed bits for byte patterns 0x40-0x5F + "028242C222A262E2129252D232B272F20A8A4ACA2AAA6AEA1A9A5ADA3ABA7AFA" + + // Reversed bits for byte patterns 0x60-0x7F + "068646C626A666E6169656D636B676F60E8E4ECE2EAE6EEE1E9E5EDE3EBE7EFE" + + // Reversed bits for byte patterns 0x80-0x9F + "018141C121A161E1119151D131B171F1098949C929A969E9199959D939B979F9" + + // Reversed bits for byte patterns 0xA0-0xBF + "058545C525A565E5159555D535B575F50D8D4DCD2DAD6DED1D9D5DDD3DBD7DFD" + + // Reversed bits for byte patterns 0xC0-0xDF + "038343C323A363E3139353D333B373F30B8B4BCB2BAB6BEB1B9B5BDB3BBB7BFB" + + // Reversed bits for byte patterns 0xE0-0xFF + "078747C727A767E7179757D737B777F70F8F4FCF2FAF6FEF1F9F5FDF3FBF7FFF"; + + Verify(writer, ExpectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_7992Bits(AsnEncodingRules ruleSet) + { + BitArray array = new BitArray(7992); + array.Set(4, true); + array.Set(7990, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array, new Asn1Tag(TagClass.ContextSpecific, 4)); + + string expectedHex = "848203E80008" + new string('0', 1994) + "02"; + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_BitArray_7993Bits(AsnEncodingRules ruleSet) + { + BitArray array = new BitArray(7993); + array.Set(7992, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array, new Asn1Tag(TagClass.ContextSpecific, 5)); + + string expectedHex; + + if (ruleSet == AsnEncodingRules.CER) + { + expectedHex = "A580038203E8" + new string('0', 2000) + "03020780" + "0000"; + } + else + { + expectedHex = "858203E907" + new string('0', 1998) + "80"; + } + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_KeyUsage_OneByte(AsnEncodingRules ruleSet) + { + // KeyUsage ::= BIT STRING { + // digitalSignature (0), + // nonRepudiation (1), + // keyEncipherment (2), + // dataEncipherment (3), + // keyAgreement (4), + // keyCertSign (5), + // cRLSign (6), + // encipherOnly (7), + // decipherOnly (8) } + + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, + critical: false); + + BitArray array = new BitArray(7); + array.Set(6, true); + array.Set(5, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array); + + Verify(writer, kuExt.RawData.ByteArrayToHex()); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public static void VerifyWriteNamedBitList_KeyUsage_TwoByte(AsnEncodingRules ruleSet) + { + X509KeyUsageExtension kuExt = new X509KeyUsageExtension( + X509KeyUsageFlags.KeyAgreement | X509KeyUsageFlags.DecipherOnly, + critical: false); + + BitArray array = new BitArray(9); + array.Set(4, true); + array.Set(8, true); + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNamedBitList(array); + + Verify(writer, kuExt.RawData.ByteArrayToHex()); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs new file mode 100644 index 0000000..659965c --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteNull.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteNull : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNull(); + + Verify(writer, "0500"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull_OctetString(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteNull(Asn1Tag.PrimitiveOctetString)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.DER)] + [InlineData(AsnEncodingRules.CER)] + public void VerifyWriteNull_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteNull(new Asn1Tag(TagClass.ContextSpecific, 7, true)); + writer.WriteNull(new Asn1Tag(UniversalTagNumber.Null, true)); + + Verify(writer, "87000500"); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs new file mode 100644 index 0000000..7616d3d --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteObjectIdentifier.cs @@ -0,0 +1,235 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteObjectIdentifier : Asn1WriterTests + { + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_String( + AsnEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier(oidValue); + + Verify(writer, expectedHex); + } + + [Theory] + [MemberData(nameof(ValidOidData))] + public void VerifyWriteObjectIdentifier_Span( + AsnEncodingRules ruleSet, + string oidValue, + string expectedHex) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier(oidValue.AsSpan()); + + Verify(writer, expectedHex); + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_String( + string description, + AsnEncodingRules ruleSet, + string nonOidValue) + { + _ = description; + AsnWriter writer = new AsnWriter(ruleSet); + + if (nonOidValue == null) + { + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue)); + } + else + { + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue)); + } + } + + [Theory] + [MemberData(nameof(InvalidOidData))] + public void VerifyWriteOid_InvalidValue_Span( + string description, + AsnEncodingRules ruleSet, + string nonOidValue) + { + _ = description; + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "oidValue", + () => writer.WriteObjectIdentifier(nonOidValue.AsSpan())); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_String(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier("1.3.14.3.2.26", new Asn1Tag(TagClass.ContextSpecific, 3)); + + Verify(writer, "83052B0E03021A"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteObjectIdentifier_CustomTag_Span(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteObjectIdentifier("1.3.14.3.2.26".AsSpan(), new Asn1Tag(TagClass.Application, 2)); + + Verify(writer, "42052B0E03021A"); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WriteObjectIdentifier_NullString(bool defaultTag) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + + AssertExtensions.Throws( + "oidValue", + () => + { + if (defaultTag) + { + writer.WriteObjectIdentifier((string)null); + } + else + { + writer.WriteObjectIdentifier((string)null, new Asn1Tag(TagClass.ContextSpecific, 6)); + } + }); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier("1.1", Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteObjectIdentifier("1.1".AsSpan(), Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteObjectIdentifier_ConstructedIgnored(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + const string OidValue = "1.1"; + Asn1Tag constructedOid = new Asn1Tag(UniversalTagNumber.ObjectIdentifier, isConstructed: true); + Asn1Tag constructedContext0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); + + writer.WriteObjectIdentifier(OidValue, constructedOid); + writer.WriteObjectIdentifier(OidValue, constructedContext0); + writer.WriteObjectIdentifier(OidValue.AsSpan(), constructedOid); + writer.WriteObjectIdentifier(OidValue.AsSpan(), constructedContext0); + + Verify(writer, "060129800129060129800129"); + } + + public static IEnumerable ValidOidData { get; } = + new object[][] + { + new object[] + { + AsnEncodingRules.BER, + "0.0", + "060100", + }, + new object[] + { + AsnEncodingRules.CER, + "1.0", + "060128", + }, + new object[] + { + AsnEncodingRules.DER, + "2.0", + "060150", + }, + new object[] + { + AsnEncodingRules.BER, + "1.3.14.3.2.26", + "06052B0E03021A", + }, + new object[] + { + AsnEncodingRules.CER, + "2.999.19427512891.25", + "06088837C8AFE1A43B19", + }, + new object[] + { + AsnEncodingRules.DER, + "1.2.840.113549.1.1.10", + "06092A864886F70D01010A", + }, + new object[] + { + // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and + // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + // this is + // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } + AsnEncodingRules.DER, + "2.25.329800735698586629295641978511506172918.3", + "06156983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", + }, + }; + + public static IEnumerable InvalidOidData { get; } = + new object[][] + { + new object[] { "Null", AsnEncodingRules.BER, null }, + new object[] { "Empty string", AsnEncodingRules.BER, "" }, + new object[] { "No period", AsnEncodingRules.CER, "1" }, + new object[] { "No second RID", AsnEncodingRules.DER, "1." }, + new object[] { "Invalid first RID", AsnEncodingRules.BER, "3.0" }, + new object[] { "Invalid first RID - multichar", AsnEncodingRules.CER, "27.0" }, + new object[] { "Double zero - First RID", AsnEncodingRules.DER, "00.0" }, + new object[] { "Leading zero - First RID", AsnEncodingRules.BER, "01.0" }, + new object[] { "Double zero - second RID", AsnEncodingRules.CER, "0.00" }, + new object[] { "Leading zero - second RID", AsnEncodingRules.DER, "0.01" }, + new object[] { "Double-period", AsnEncodingRules.BER, "0..0" }, + new object[] { "Ends with period - second RID", AsnEncodingRules.BER, "0.0." }, + new object[] { "Ends with period - third RID", AsnEncodingRules.BER, "0.1.30." }, + new object[] { "Double zero - third RID", AsnEncodingRules.CER, "0.1.00" }, + new object[] { "Leading zero - third RID", AsnEncodingRules.DER, "0.1.023" }, + new object[] { "Invalid character first position", AsnEncodingRules.BER, "a.1.23" }, + new object[] { "Invalid character second position", AsnEncodingRules.CER, "0,1.23" }, + new object[] { "Invalid character second rid", AsnEncodingRules.DER, "0.1q.23" }, + new object[] { "Invalid character third rid", AsnEncodingRules.BER, "0.1.23q" }, + }; + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs new file mode 100644 index 0000000..465df77 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteOctetString.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteOctetString : Asn1WriterTests + { + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void WriteEmpty(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(ReadOnlySpan.Empty); + + Verify(writer, "0400"); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 1, "0401")] + [InlineData(AsnEncodingRules.CER, 2, "0402")] + [InlineData(AsnEncodingRules.DER, 3, "0403")] + [InlineData(AsnEncodingRules.BER, 126, "047E")] + [InlineData(AsnEncodingRules.CER, 127, "047F")] + [InlineData(AsnEncodingRules.DER, 128, "048180")] + [InlineData(AsnEncodingRules.BER, 1000, "048203E8")] + [InlineData(AsnEncodingRules.CER, 1000, "048203E8")] + [InlineData(AsnEncodingRules.DER, 1000, "048203E8")] + [InlineData(AsnEncodingRules.BER, 1001, "048203E9")] + [InlineData(AsnEncodingRules.DER, 1001, "048203E9")] + [InlineData(AsnEncodingRules.BER, 2001, "048207D1")] + [InlineData(AsnEncodingRules.DER, 2001, "048207D1")] + public void WritePrimitive(AsnEncodingRules ruleSet, int length, string hexStart) + { + string payloadHex = new string('0', 2 * length); + string expectedHex = hexStart + payloadHex; + byte[] data = new byte[length]; + + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(data); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(1001, "2480048203E8", "0401")] + [InlineData(1999, "2480048203E8", "048203E7")] + [InlineData(2000, "2480048203E8", "048203E8")] + public void WriteSegmentedCER(int length, string hexStart, string hexStart2) + { + string payload1Hex = new string('8', 2000); + string payload2Hex = new string('8', (length - 1000) * 2); + string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; + byte[] data = new byte[length]; + + for (int i = 0; i < data.Length; i++) + { + data[i] = 0x88; + } + + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteOctetString(data); + + Verify(writer, expectedHex); + } + + [Theory] + [InlineData(AsnEncodingRules.BER, 0, false)] + [InlineData(AsnEncodingRules.CER, 0, false)] + [InlineData(AsnEncodingRules.DER, 0, false)] + [InlineData(AsnEncodingRules.BER, 999, false)] + [InlineData(AsnEncodingRules.CER, 999, false)] + [InlineData(AsnEncodingRules.DER, 999, false)] + [InlineData(AsnEncodingRules.BER, 1000, false)] + [InlineData(AsnEncodingRules.CER, 1000, false)] + [InlineData(AsnEncodingRules.DER, 1000, false)] + [InlineData(AsnEncodingRules.BER, 1001, false)] + [InlineData(AsnEncodingRules.CER, 1001, true)] + [InlineData(AsnEncodingRules.DER, 1001, false)] + [InlineData(AsnEncodingRules.BER, 1998, false)] + [InlineData(AsnEncodingRules.CER, 1998, true)] + [InlineData(AsnEncodingRules.DER, 1998, false)] + [InlineData(AsnEncodingRules.BER, 1999, false)] + [InlineData(AsnEncodingRules.CER, 1999, true)] + [InlineData(AsnEncodingRules.DER, 1999, false)] + [InlineData(AsnEncodingRules.BER, 2000, false)] + [InlineData(AsnEncodingRules.CER, 2000, true)] + [InlineData(AsnEncodingRules.DER, 2000, false)] + [InlineData(AsnEncodingRules.BER, 2001, false)] + [InlineData(AsnEncodingRules.CER, 2001, true)] + [InlineData(AsnEncodingRules.DER, 2001, false)] + [InlineData(AsnEncodingRules.BER, 4096, false)] + [InlineData(AsnEncodingRules.CER, 4096, true)] + [InlineData(AsnEncodingRules.DER, 4096, false)] + public void VerifyWriteOctetString_PrimitiveOrConstructed( + AsnEncodingRules ruleSet, + int payloadLength, + bool expectConstructed) + { + byte[] data = new byte[payloadLength]; + + Asn1Tag[] tagsToTry = + { + new Asn1Tag(UniversalTagNumber.OctetString), + new Asn1Tag(UniversalTagNumber.OctetString, isConstructed: true), + new Asn1Tag(TagClass.Private, 87), + new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), + }; + + byte[] answerBuf = new byte[payloadLength + 100]; + + foreach (Asn1Tag toTry in tagsToTry) + { + AsnWriter writer = new AsnWriter(ruleSet); + writer.WriteOctetString(data, toTry); + + Assert.True(writer.TryEncode(answerBuf, out _)); + Assert.True(Asn1Tag.TryDecode(answerBuf, out Asn1Tag writtenTag, out _)); + + if (expectConstructed) + { + Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + else + { + Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); + } + + Assert.Equal(toTry.TagClass, writtenTag.TagClass); + Assert.Equal(toTry.TagValue, writtenTag.TagValue); + } + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteOctetString_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(ReadOnlySpan.Empty, Asn1Tag.Null)); + + AssertExtensions.Throws( + "tag", + () => writer.WriteOctetString(new byte[1], Asn1Tag.Null)); + } + } +} diff --git a/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs new file mode 100644 index 0000000..2026224 --- /dev/null +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtcTime.cs @@ -0,0 +1,201 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Xunit; + +namespace System.Formats.Asn1.Tests.Writer +{ + public class WriteUtcTime : Asn1WriterTests + { + public static IEnumerable TestCases { get; } = new object[][] + { + new object[] + { + new DateTimeOffset(2017, 10, 16, 8, 24, 3, TimeSpan.FromHours(-7)), + "0D3137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(1817, 10, 16, 21, 24, 3, TimeSpan.FromHours(6)), + "0D3137313031363135323430335A", + }, + new object[] + { + new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero), + "0D3030303130313030303030305A", + }, + }; + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_BER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_BER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); + writer.WriteUtcTime(input, tag); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_CER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_CER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); + writer.WriteUtcTime(input, tag); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_DER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + writer.WriteUtcTime(input); + + Verify(writer, "17" + expectedHexPayload); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_DER_CustomTag(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); + writer.WriteUtcTime(input, tag); + + Verify(writer, Stringify(tag) + expectedHexPayload); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteUtcTime_Null(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + + AssertExtensions.Throws( + "tag", + () => writer.WriteUtcTime(DateTimeOffset.Now, Asn1Tag.Null)); + } + + [Theory] + [InlineData(AsnEncodingRules.BER)] + [InlineData(AsnEncodingRules.CER)] + [InlineData(AsnEncodingRules.DER)] + public void VerifyWriteUtcTime_IgnoresConstructed(AsnEncodingRules ruleSet) + { + AsnWriter writer = new AsnWriter(ruleSet); + DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); + + writer.WriteUtcTime(value, new Asn1Tag(UniversalTagNumber.UtcTime, true)); + writer.WriteUtcTime(value, new Asn1Tag(TagClass.ContextSpecific, 3, true)); + Verify(writer, "170D3137313131363137333530315A" + "830D3137313131363137333530315A"); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void VerifyWriteUtcTime_RespectsYearMax_DER(DateTimeOffset input, string expectedHexPayload) + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + Assert.Equal(0, writer.GetEncodedLength()); + + AssertExtensions.Throws( + "value", + () => writer.WriteUtcTime(input, input.Year - 1)); + + Assert.Equal(0, writer.GetEncodedLength()); + + writer.WriteUtcTime(input, input.Year); + Assert.Equal(15, writer.GetEncodedLength()); + + writer.WriteUtcTime(input, input.Year + 99); + Assert.Equal(30, writer.GetEncodedLength()); + + writer.Reset(); + + _ = expectedHexPayload; + AssertExtensions.Throws( + "value", + () => writer.WriteUtcTime(input, input.Year + 100)); + + Assert.Equal(0, writer.GetEncodedLength()); + } + + [Fact] + public void VerifyWriteUtcTime_RespectsYearMax_UniversalLimit() + { + AsnWriter writer = new AsnWriter(AsnEncodingRules.CER); + Asn1Tag tag = new Asn1Tag(TagClass.Private, 11); + + // 1950 after ToUniversal + writer.WriteUtcTime( + new DateTimeOffset(1949, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), + 2049, + tag); + + Assert.Equal(15, writer.GetEncodedLength()); + + // 1949 after ToUniversal + AssertExtensions.Throws( + "value", + () => + writer.WriteUtcTime( + new DateTimeOffset(1950, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), + 2049, + tag)); + + Assert.Equal(15, writer.GetEncodedLength()); + + // 2050 after ToUniversal + AssertExtensions.Throws( + "value", + () => + writer.WriteUtcTime( + new DateTimeOffset(2049, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), + 2049, + tag)); + + Assert.Equal(15, writer.GetEncodedLength()); + + // 1950 after ToUniversal + writer.WriteUtcTime( + new DateTimeOffset(2050, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), + 2049, + tag); + + Assert.Equal(30, writer.GetEncodedLength()); + + string hex = + Stringify(tag) + "0D3530303130313037313131395A" + + Stringify(tag) + "0D3439313233313139313131395A"; + + Verify(writer, hex); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs similarity index 87% rename from src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs rename to src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs index 9caa0d8..f606137 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtf8String.cs +++ b/src/libraries/System.Formats.Asn1/tests/Writer/WriteUtf8String.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Security.Cryptography.Asn1; using Xunit; -namespace System.Security.Cryptography.Tests.Asn1 +namespace System.Formats.Asn1.Tests.Writer { public class WriteUtf8String : WriteCharacterString { @@ -60,13 +59,13 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 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 @@ namespace System.Security.Cryptography.Tests.Asn1 base.VerifyWrite_CERSegmented_Span_CustomPrimitiveTag_Helper(input, contentByteCount); // UTF8 has no non-encodable values. - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_Span(bool empty) => - base.WriteAfterDispose_Span_Helper(empty); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteAfterDispose_String(bool empty) => - base.WriteAfterDispose_String_Helper(empty); } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 4268d29..99f268a3 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 @@ -578,6 +578,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 71c22ca..877b4ac 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 fd7c034..2e1fcc7 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { try { - using (AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters)) - { - return writer.Encode(); - } + AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters); + return writer.Encode(); } finally { @@ -423,10 +418,8 @@ namespace System.Security.Cryptography { 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 59b52bd..590af79 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { 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 @@ namespace System.Security.Cryptography { try { - using (AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters)) - { - return writer.Encode(); - } + AsnWriter writer = EccKeyFormatHelper.WriteECPrivateKey(ecParameters); + return writer.Encode(); } finally { @@ -1248,10 +1243,8 @@ namespace System.Security.Cryptography { 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 56b25ff..a448842 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 @@ namespace System.Security.Cryptography 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 046835f..1b56985 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 2802ead..d673fa5 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 @@ namespace Internal.Cryptography 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 5c496e6..d828388 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj @@ -122,6 +122,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs deleted file mode 100644 index a609c66..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/Asn1ReaderTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public abstract partial class Asn1ReaderTests - { - public enum PublicTagClass : byte - { - Universal = TagClass.Universal, - Application = TagClass.Application, - ContextSpecific = TagClass.ContextSpecific, - Private = TagClass.Private, - } - - public enum PublicEncodingRules - { - BER = AsnEncodingRules.BER, - CER = AsnEncodingRules.CER, - DER = AsnEncodingRules.DER, - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs deleted file mode 100644 index 02d97d4..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadBoolean.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadBoolean : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, false, 3, "010100")] - [InlineData(PublicEncodingRules.BER, true, 3, "010101")] - // Padded length - [InlineData(PublicEncodingRules.BER, true, 4, "01810101")] - [InlineData(PublicEncodingRules.BER, true, 3, "0101FF0500")] - [InlineData(PublicEncodingRules.CER, false, 3, "0101000500")] - [InlineData(PublicEncodingRules.CER, true, 3, "0101FF")] - [InlineData(PublicEncodingRules.DER, false, 3, "010100")] - [InlineData(PublicEncodingRules.DER, true, 3, "0101FF0500")] - // Context Specific 0 - [InlineData(PublicEncodingRules.DER, true, 3, "8001FF0500")] - // Application 31 - [InlineData(PublicEncodingRules.DER, true, 4, "5F1F01FF0500")] - // Private 253 - [InlineData(PublicEncodingRules.CER, false, 5, "DF817D01000500")] - public static void ReadBoolean_Success( - PublicEncodingRules ruleSet, - bool expectedValue, - int expectedBytesRead, - string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Asn1Tag tag = reader.PeekTag(); - bool value; - - if (tag.TagClass == TagClass.Universal) - { - value = reader.ReadBoolean(); - } - else - { - value = reader.ReadBoolean(tag); - } - - if (inputData.Length == expectedBytesRead) - { - Assert.False(reader.HasData, "reader.HasData"); - } - else - { - Assert.True(reader.HasData, "reader.HasData"); - } - - if (expectedValue) - { - Assert.True(value, "value"); - } - else - { - Assert.False(value, "value"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 1, 1, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadBoolean(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - bool value = reader.ReadBoolean(); - Assert.False(value, "value"); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x80, 1, 0xFF }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadBoolean(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadBoolean()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws(() => reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - bool value = reader.ReadBoolean(new Asn1Tag(TagClass.ContextSpecific, 0)); - Assert.True(value, "value"); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0101FF", PublicTagClass.Universal, 1)] - [InlineData(PublicEncodingRules.CER, "0101FF", PublicTagClass.Universal, 1)] - [InlineData(PublicEncodingRules.DER, "0101FF", PublicTagClass.Universal, 1)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - bool val1 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - bool val2 = reader.ReadBoolean(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - - Assert.Equal(val1, val2); - } - - [Theory] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("TagOnly", PublicEncodingRules.BER, "01")] - [InlineData("TagOnly", PublicEncodingRules.CER, "01")] - [InlineData("TagOnly", PublicEncodingRules.DER, "01")] - [InlineData("MultiByte TagOnly", PublicEncodingRules.DER, "9F1F")] - [InlineData("MultiByte TagOnly", PublicEncodingRules.CER, "9F1F")] - [InlineData("MultiByte TagOnly", PublicEncodingRules.BER, "9F1F")] - [InlineData("TagAndLength", PublicEncodingRules.BER, "0101")] - [InlineData("Tag and MultiByteLength", PublicEncodingRules.BER, "01820001")] - [InlineData("TagAndLength", PublicEncodingRules.CER, "8001")] - [InlineData("TagAndLength", PublicEncodingRules.DER, "C001")] - [InlineData("MultiByteTagAndLength", PublicEncodingRules.DER, "9F2001")] - [InlineData("MultiByteTagAndLength", PublicEncodingRules.CER, "9F2001")] - [InlineData("MultiByteTagAndLength", PublicEncodingRules.BER, "9F2001")] - [InlineData("MultiByteTagAndMultiByteLength", PublicEncodingRules.BER, "9F28200001")] - [InlineData("TooShort", PublicEncodingRules.BER, "0100")] - [InlineData("TooShort", PublicEncodingRules.CER, "8000")] - [InlineData("TooShort", PublicEncodingRules.DER, "0100")] - [InlineData("TooLong", PublicEncodingRules.DER, "C0020000")] - [InlineData("TooLong", PublicEncodingRules.CER, "01020000")] - [InlineData("TooLong", PublicEncodingRules.BER, "C081020000")] - [InlineData("MissingContents", PublicEncodingRules.BER, "C001")] - [InlineData("MissingContents", PublicEncodingRules.CER, "0101")] - [InlineData("MissingContents", PublicEncodingRules.DER, "8001")] - [InlineData("NonCanonical", PublicEncodingRules.DER, "0101FE")] - [InlineData("NonCanonical", PublicEncodingRules.CER, "800101")] - [InlineData("Constructed", PublicEncodingRules.BER, "2103010101")] - [InlineData("Constructed", PublicEncodingRules.CER, "2103010101")] - [InlineData("Constructed", PublicEncodingRules.DER, "2103010101")] - [InlineData("WrongTag", PublicEncodingRules.DER, "0400")] - [InlineData("WrongTag", PublicEncodingRules.CER, "0400")] - [InlineData("WrongTag", PublicEncodingRules.BER, "0400")] - public static void ReadBoolean_Failure( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Asn1Tag tag = default(Asn1Tag); - - if (inputData.Length > 0) - { - tag = reader.PeekTag(); - } - - if (tag.TagClass == TagClass.Universal) - { - Assert.Throws(() => reader.ReadBoolean()); - } - else - { - Assert.Throws(() => reader.ReadBoolean(tag)); - } - - if (inputData.Length == 0) - { - // If we started with nothing, where did the data come from? - Assert.False(reader.HasData, "reader.HasData"); - } - else - { - // Nothing should have moved - Assert.True(reader.HasData, "reader.HasData"); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs deleted file mode 100644 index a2966ad..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadEnumerated.cs +++ /dev/null @@ -1,760 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadEnumerated : Asn1ReaderTests - { - public enum ByteBacked : byte - { - Zero = 0, - NotFluffy = 11, - Fluff = 12, - } - - public enum SByteBacked : sbyte - { - Zero = 0, - Fluff = 83, - Pillow = -17, - } - - public enum ShortBacked : short - { - Zero = 0, - Fluff = 521, - Pillow = -1024, - } - - public enum UShortBacked : ushort - { - Zero = 0, - Fluff = 32768, - } - - public enum IntBacked : int - { - Zero = 0, - Fluff = 0x010001, - Pillow = -Fluff, - } - - public enum UIntBacked : uint - { - Zero = 0, - Fluff = 0x80000005, - } - - public enum LongBacked : long - { - Zero = 0, - Fluff = 0x0200000441, - Pillow = -0x100000000L, - } - - public enum ULongBacked : ulong - { - Zero = 0, - Fluff = 0xFACEF00DCAFEBEEF, - } - - private static void GetExpectedValue( - PublicEncodingRules ruleSet, - TEnum expectedValue, - string inputHex) - where TEnum : struct - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - TEnum value = reader.ReadEnumeratedValue(); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, ByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, ByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, ByteBacked.Fluff, "0A010C")] - [InlineData(PublicEncodingRules.CER, ByteBacked.Fluff, "0A010C")] - [InlineData(PublicEncodingRules.DER, ByteBacked.Fluff, "0A010C")] - [InlineData(PublicEncodingRules.BER, (ByteBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (ByteBacked)128, "0A020080")] - [InlineData(PublicEncodingRules.DER, (ByteBacked)129, "0A020081")] - [InlineData(PublicEncodingRules.BER, (ByteBacked)254, "0A82000200FE")] - public static void GetExpectedValue_ByteBacked( - PublicEncodingRules ruleSet, - ByteBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, SByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, SByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, SByteBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, SByteBacked.Fluff, "0A0153")] - [InlineData(PublicEncodingRules.CER, SByteBacked.Fluff, "0A0153")] - [InlineData(PublicEncodingRules.DER, SByteBacked.Fluff, "0A0153")] - [InlineData(PublicEncodingRules.BER, SByteBacked.Pillow, "0A01EF")] - [InlineData(PublicEncodingRules.CER, (SByteBacked)sbyte.MinValue, "0A0180")] - [InlineData(PublicEncodingRules.DER, (SByteBacked)sbyte.MinValue + 1, "0A0181")] - [InlineData(PublicEncodingRules.BER, SByteBacked.Pillow, "0A820001EF")] - public static void GetExpectedValue_SByteBacked( - PublicEncodingRules ruleSet, - SByteBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, ShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, ShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, ShortBacked.Fluff, "0A020209")] - [InlineData(PublicEncodingRules.CER, ShortBacked.Fluff, "0A020209")] - [InlineData(PublicEncodingRules.DER, ShortBacked.Fluff, "0A020209")] - [InlineData(PublicEncodingRules.BER, ShortBacked.Pillow, "0A02FC00")] - [InlineData(PublicEncodingRules.CER, (ShortBacked)short.MinValue, "0A028000")] - [InlineData(PublicEncodingRules.DER, (ShortBacked)short.MinValue + 1, "0A028001")] - [InlineData(PublicEncodingRules.BER, ShortBacked.Pillow, "0A820002FC00")] - public static void GetExpectedValue_ShortBacked( - PublicEncodingRules ruleSet, - ShortBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, UShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, UShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, UShortBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, UShortBacked.Fluff, "0A03008000")] - [InlineData(PublicEncodingRules.CER, UShortBacked.Fluff, "0A03008000")] - [InlineData(PublicEncodingRules.DER, UShortBacked.Fluff, "0A03008000")] - [InlineData(PublicEncodingRules.BER, (UShortBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (UShortBacked)256, "0A020100")] - [InlineData(PublicEncodingRules.DER, (UShortBacked)0x7FED, "0A027FED")] - [InlineData(PublicEncodingRules.BER, (UShortBacked)ushort.MaxValue, "0A82000300FFFF")] - [InlineData(PublicEncodingRules.BER, (UShortBacked)0x8123, "0A820003008123")] - public static void GetExpectedValue_UShortBacked( - PublicEncodingRules ruleSet, - UShortBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, IntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, IntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, IntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, IntBacked.Fluff, "0A03010001")] - [InlineData(PublicEncodingRules.CER, IntBacked.Fluff, "0A03010001")] - [InlineData(PublicEncodingRules.DER, IntBacked.Fluff, "0A03010001")] - [InlineData(PublicEncodingRules.BER, IntBacked.Pillow, "0A03FEFFFF")] - [InlineData(PublicEncodingRules.CER, (IntBacked)int.MinValue, "0A0480000000")] - [InlineData(PublicEncodingRules.DER, (IntBacked)int.MinValue + 1, "0A0480000001")] - [InlineData(PublicEncodingRules.BER, IntBacked.Pillow, "0A820003FEFFFF")] - public static void GetExpectedValue_IntBacked( - PublicEncodingRules ruleSet, - IntBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, UIntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, UIntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, UIntBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, UIntBacked.Fluff, "0A050080000005")] - [InlineData(PublicEncodingRules.CER, UIntBacked.Fluff, "0A050080000005")] - [InlineData(PublicEncodingRules.DER, UIntBacked.Fluff, "0A050080000005")] - [InlineData(PublicEncodingRules.BER, (UIntBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (UIntBacked)256, "0A020100")] - [InlineData(PublicEncodingRules.DER, (UIntBacked)0x7FED, "0A027FED")] - [InlineData(PublicEncodingRules.BER, (UIntBacked)uint.MaxValue, "0A82000500FFFFFFFF")] - [InlineData(PublicEncodingRules.BER, (UIntBacked)0x8123, "0A820003008123")] - public static void GetExpectedValue_UIntBacked( - PublicEncodingRules ruleSet, - UIntBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, LongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, LongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, LongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, LongBacked.Fluff, "0A050200000441")] - [InlineData(PublicEncodingRules.CER, LongBacked.Fluff, "0A050200000441")] - [InlineData(PublicEncodingRules.DER, LongBacked.Fluff, "0A050200000441")] - [InlineData(PublicEncodingRules.BER, LongBacked.Pillow, "0A05FF00000000")] - [InlineData(PublicEncodingRules.CER, (LongBacked)short.MinValue, "0A028000")] - [InlineData(PublicEncodingRules.DER, (LongBacked)short.MinValue + 1, "0A028001")] - [InlineData(PublicEncodingRules.BER, LongBacked.Pillow, "0A820005FF00000000")] - public static void GetExpectedValue_LongBacked( - PublicEncodingRules ruleSet, - LongBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ULongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.CER, ULongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.DER, ULongBacked.Zero, "0A0100")] - [InlineData(PublicEncodingRules.BER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.CER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.DER, ULongBacked.Fluff, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)255, "0A0200FF")] - [InlineData(PublicEncodingRules.CER, (ULongBacked)256, "0A020100")] - [InlineData(PublicEncodingRules.DER, (ULongBacked)0x7FED, "0A027FED")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)uint.MaxValue, "0A82000500FFFFFFFF")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)ulong.MaxValue, "0A82000900FFFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, (ULongBacked)0x8123, "0A820003008123")] - public static void GetExpectedValue_ULongBacked( - PublicEncodingRules ruleSet, - ULongBacked expectedValue, - string inputHex) - { - GetExpectedValue(ruleSet, expectedValue, inputHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A020102")] - [InlineData(PublicEncodingRules.CER, "0A020102")] - [InlineData(PublicEncodingRules.DER, "0A020102")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Byte(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A020102")] - [InlineData(PublicEncodingRules.CER, "0A020102")] - [InlineData(PublicEncodingRules.DER, "0A020102")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_SByte(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Short(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A03010203")] - [InlineData(PublicEncodingRules.CER, "0A03010203")] - [InlineData(PublicEncodingRules.DER, "0A03010203")] - [InlineData(PublicEncodingRules.BER, "0A0401020304")] - [InlineData(PublicEncodingRules.CER, "0A0401020304")] - [InlineData(PublicEncodingRules.DER, "0A0401020304")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_UShort(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Int(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A050102030405")] - [InlineData(PublicEncodingRules.CER, "0A050102030405")] - [InlineData(PublicEncodingRules.DER, "0A050102030405")] - [InlineData(PublicEncodingRules.BER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.CER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.DER, "0A080102030405060708")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_UInt(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A02FF80")] - [InlineData(PublicEncodingRules.CER, "0A02FF80")] - [InlineData(PublicEncodingRules.DER, "0A02FF80")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_Long(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A01FF")] - [InlineData(PublicEncodingRules.CER, "0A01FF")] - [InlineData(PublicEncodingRules.DER, "0A01FF")] - [InlineData(PublicEncodingRules.BER, "0A02007F")] - [InlineData(PublicEncodingRules.CER, "0A02007F")] - [InlineData(PublicEncodingRules.DER, "0A02007F")] - [InlineData(PublicEncodingRules.BER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0A09010203040506070809")] - [InlineData(PublicEncodingRules.BER, "2A030A0100")] - public static void ReadEnumeratedValue_Invalid_ULong(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedValue_NonEnumType(PublicEncodingRules ruleSet) - { - byte[] data = { 0x0A, 0x01, 0x00 }; - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedValue_FlagsEnum(PublicEncodingRules ruleSet) - { - byte[] data = { 0x0A, 0x01, 0x00 }; - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "tEnum", - () => reader.ReadEnumeratedValue()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEnumeratedBytes(PublicEncodingRules ruleSet) - { - const string Payload = "0102030405060708090A0B0C0D0E0F10"; - - // ENUMERATED (payload) followed by INTEGER (0) - byte[] data = ("0A10" + Payload + "020100").HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - ReadOnlyMemory contents = reader.ReadEnumeratedBytes(); - Assert.Equal(0x10, contents.Length); - Assert.Equal(Payload, contents.ByteArrayToHex()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "")] - [InlineData(PublicEncodingRules.CER, "")] - [InlineData(PublicEncodingRules.DER, "")] - [InlineData(PublicEncodingRules.BER, "0A")] - [InlineData(PublicEncodingRules.CER, "0A")] - [InlineData(PublicEncodingRules.DER, "0A")] - [InlineData(PublicEncodingRules.BER, "0A00")] - [InlineData(PublicEncodingRules.CER, "0A00")] - [InlineData(PublicEncodingRules.DER, "0A00")] - [InlineData(PublicEncodingRules.BER, "0A01")] - [InlineData(PublicEncodingRules.CER, "0A01")] - [InlineData(PublicEncodingRules.DER, "0A01")] - [InlineData(PublicEncodingRules.BER, "010100")] - [InlineData(PublicEncodingRules.CER, "010100")] - [InlineData(PublicEncodingRules.DER, "010100")] - [InlineData(PublicEncodingRules.BER, "9F00")] - [InlineData(PublicEncodingRules.CER, "9F00")] - [InlineData(PublicEncodingRules.DER, "9F00")] - [InlineData(PublicEncodingRules.BER, "0A81")] - [InlineData(PublicEncodingRules.CER, "0A81")] - [InlineData(PublicEncodingRules.DER, "0A81")] - public static void ReadEnumeratedBytes_Throws(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadEnumeratedBytes()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x0A, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadEnumeratedValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - ShortBacked value = reader.ReadEnumeratedValue(); - Assert.Equal((ShortBacked)0x7E, value); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 2, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadEnumeratedValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadEnumeratedValue()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - ShortBacked value = reader.ReadEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 7)); - Assert.Equal((ShortBacked)0x80, value); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0A01FF", PublicTagClass.Universal, 10)] - [InlineData(PublicEncodingRules.CER, "0A01FF", PublicTagClass.Universal, 10)] - [InlineData(PublicEncodingRules.DER, "0A01FF", PublicTagClass.Universal, 10)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ShortBacked val1 = reader.ReadEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ShortBacked val2 = reader.ReadEnumeratedValue(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - - Assert.Equal(val1, val2); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs deleted file mode 100644 index 37d5882..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs +++ /dev/null @@ -1,606 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadInteger : Asn1ReaderTests - { - [Theory] - [InlineData("Constructed Encoding", PublicEncodingRules.BER, "2203020100")] - [InlineData("Constructed Encoding-Indefinite", PublicEncodingRules.BER, "228002010000")] - [InlineData("Constructed Encoding-Indefinite", PublicEncodingRules.CER, "228002010000")] - [InlineData("Constructed Encoding", PublicEncodingRules.DER, "2203020100")] - [InlineData("Wrong Universal Tag", PublicEncodingRules.BER, "030100")] - [InlineData("Bad Length", PublicEncodingRules.BER, "02030102")] - [InlineData("Incorrect Zero Encoding", PublicEncodingRules.BER, "0200")] - [InlineData("Incorrect Zero Encoding", PublicEncodingRules.CER, "0200")] - [InlineData("Incorrect Zero Encoding", PublicEncodingRules.DER, "0200")] - [InlineData("Redundant Leading 0x00", PublicEncodingRules.BER, "0202007F")] - [InlineData("Redundant Leading 0x00", PublicEncodingRules.CER, "0202007F")] - [InlineData("Redundant Leading 0x00", PublicEncodingRules.DER, "0202007F")] - [InlineData("Redundant Leading 0xFF", PublicEncodingRules.BER, "0202FF80")] - [InlineData("Redundant Leading 0xFF", PublicEncodingRules.CER, "0202FF80")] - [InlineData("Redundant Leading 0xFF", PublicEncodingRules.DER, "0202FF80")] - public static void InvalidData( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadIntegerBytes()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "02017F", sbyte.MaxValue)] - [InlineData(PublicEncodingRules.DER, "020180", sbyte.MinValue)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - public static void ReadInt8_Success( - PublicEncodingRules ruleSet, - string inputHex, - sbyte expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt8(out sbyte value); - - Assert.True(didRead, "reader.TryReadInt8"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "02020102")] - [InlineData(PublicEncodingRules.CER, "02020102")] - [InlineData(PublicEncodingRules.DER, "02020102")] - public static void ReadInt8_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt8(out sbyte value); - - Assert.False(didRead, "reader.TryReadInt8"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02017F", 0x7F)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.CER, "020200FF", 0xFF)] - public static void ReadUInt8_Success( - PublicEncodingRules ruleSet, - string inputHex, - byte expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt8(out byte value); - - Assert.True(didRead, "reader.TryReadUInt8"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - public static void ReadUInt8_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt8(out byte value); - - Assert.False(didRead, "reader.TryReadUInt8"); - Assert.Equal((byte)0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - [InlineData(PublicEncodingRules.CER, "0202FEFF", unchecked((short)0xFEFF))] - [InlineData(PublicEncodingRules.BER, "028102FEEF", unchecked((short)0xFEEF))] - [InlineData(PublicEncodingRules.BER, "0281028000", short.MinValue)] - [InlineData(PublicEncodingRules.CER, "02028000", short.MinValue)] - [InlineData(PublicEncodingRules.DER, "02027FFF", short.MaxValue)] - [InlineData(PublicEncodingRules.DER, "02026372", 0x6372)] - [InlineData(PublicEncodingRules.CER, "0202008A", 0x8A)] - [InlineData(PublicEncodingRules.CER, "02028ACE", unchecked((short)0x8ACE))] - public static void ReadInt16_Success( - PublicEncodingRules ruleSet, - string inputHex, - short expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt16(out short value); - - Assert.True(didRead, "reader.TryReadInt16"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0203010203")] - [InlineData(PublicEncodingRules.CER, "0203010203")] - [InlineData(PublicEncodingRules.DER, "0203010203")] - public static void ReadInt16_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt16(out short value); - - Assert.False(didRead, "reader.TryReadInt16"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - public static void ReadUInt16_Success( - PublicEncodingRules ruleSet, - string inputHex, - ushort expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt16(out ushort value); - - Assert.True(didRead, "reader.TryReadUInt16"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "02028000")] - public static void ReadUInt16_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt16(out ushort value); - - Assert.False(didRead, "reader.TryReadUInt16"); - Assert.Equal((ushort)0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "020100", 0)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - [InlineData(PublicEncodingRules.CER, "0202FEFF", unchecked((int)0xFFFF_FEFF))] - [InlineData(PublicEncodingRules.BER, "028102FEEF", unchecked((int)0xFFFF_FEEF))] - [InlineData(PublicEncodingRules.BER, "02810480000000", int.MinValue)] - [InlineData(PublicEncodingRules.CER, "020480000000", int.MinValue)] - [InlineData(PublicEncodingRules.DER, "02047FFFFFFF", int.MaxValue)] - [InlineData(PublicEncodingRules.DER, "02026372", 0x6372)] - [InlineData(PublicEncodingRules.CER, "0203008ACE", 0x8ACE)] - [InlineData(PublicEncodingRules.BER, "0203FACE01", unchecked((int)0xFFFA_CE01))] - [InlineData(PublicEncodingRules.BER, "02820003FACE01", unchecked((int)0xFFFA_CE01))] - public static void ReadInt32_Success( - PublicEncodingRules ruleSet, - string inputHex, - int expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt32(out int value); - - Assert.True(didRead, "reader.TryReadInt32"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "02050102030405")] - [InlineData(PublicEncodingRules.CER, "02050102030405")] - [InlineData(PublicEncodingRules.DER, "02050102030405")] - public static void ReadInt32_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt32(out int value); - - Assert.False(didRead, "reader.TryReadInt32"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] - [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] - [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] - [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] - public static void ReadUInt32_Success( - PublicEncodingRules ruleSet, - string inputHex, - uint expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt32(out uint value); - - Assert.True(didRead, "reader.TryReadUInt32"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "02028000")] - [InlineData(PublicEncodingRules.DER, "0203800000")] - [InlineData(PublicEncodingRules.DER, "020480000000")] - [InlineData(PublicEncodingRules.DER, "02050100000000")] - public static void ReadUInt32_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt32(out uint value); - - Assert.False(didRead, "reader.TryReadUInt32"); - Assert.Equal((uint)0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] - [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] - [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] - [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.DER, "02050183828180", 0x0183828180)] - [InlineData(PublicEncodingRules.DER, "0206018483828180", 0x018483828180)] - [InlineData(PublicEncodingRules.DER, "020701858483828180", 0x01858483828180)] - [InlineData(PublicEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] - [InlineData(PublicEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] - [InlineData(PublicEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] - [InlineData(PublicEncodingRules.DER, "0201FF", -1)] - [InlineData(PublicEncodingRules.DER, "0201FE", -2)] - [InlineData(PublicEncodingRules.DER, "02028012", unchecked((long)0xFFFFFFFF_FFFF8012))] - [InlineData(PublicEncodingRules.DER, "0203818012", unchecked((long)0xFFFFFFFF_FF818012))] - [InlineData(PublicEncodingRules.DER, "020482818012", unchecked((long)0xFFFFFFFF_82818012))] - [InlineData(PublicEncodingRules.DER, "02058382818012", unchecked((long)0xFFFFFF83_82818012))] - [InlineData(PublicEncodingRules.DER, "0206848382818012", unchecked((long)0xFFFF8483_82818012))] - [InlineData(PublicEncodingRules.DER, "020785848382818012", unchecked((long)0xFF858483_82818012))] - [InlineData(PublicEncodingRules.DER, "02088685848382818012", unchecked((long)0x86858483_82818012))] - [InlineData(PublicEncodingRules.DER, "02088000000000000000", long.MinValue)] - [InlineData(PublicEncodingRules.BER, "028800000000000000088000000000000000", long.MinValue)] - public static void ReadInt64_Success( - PublicEncodingRules ruleSet, - string inputHex, - long expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt64(out long value); - - Assert.True(didRead, "reader.TryReadInt64"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0209010203040506070809")] - [InlineData(PublicEncodingRules.CER, "0209010203040506070809")] - [InlineData(PublicEncodingRules.DER, "0209010203040506070809")] - public static void ReadInt64_TooMuchData( - PublicEncodingRules ruleSet, - string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadInt64(out long value); - - Assert.False(didRead, "reader.TryReadInt64"); - Assert.Equal(0, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020100", 0)] - [InlineData(PublicEncodingRules.CER, "02020080", 0x80)] - [InlineData(PublicEncodingRules.DER, "02027F80", 0x7F80)] - [InlineData(PublicEncodingRules.DER, "0203008180", 0x8180)] - [InlineData(PublicEncodingRules.DER, "02030A8180", 0xA8180)] - [InlineData(PublicEncodingRules.DER, "020400828180", 0x828180)] - [InlineData(PublicEncodingRules.DER, "020475828180", 0x75828180)] - [InlineData(PublicEncodingRules.DER, "02050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.BER, "02830000050083828180", 0x83828180)] - [InlineData(PublicEncodingRules.DER, "02050183828180", 0x0183828180)] - [InlineData(PublicEncodingRules.DER, "0206018483828180", 0x018483828180)] - [InlineData(PublicEncodingRules.DER, "020701858483828180", 0x01858483828180)] - [InlineData(PublicEncodingRules.DER, "02080186858483828180", 0x0186858483828180)] - [InlineData(PublicEncodingRules.DER, "02087F86858483828180", 0x7F86858483828180)] - [InlineData(PublicEncodingRules.DER, "02087FFFFFFFFFFFFFFF", long.MaxValue)] - [InlineData(PublicEncodingRules.DER, "0209008000000000000000", 0x80000000_00000000)] - [InlineData(PublicEncodingRules.DER, "020900FFFFFFFFFFFFFFFF", ulong.MaxValue)] - public static void ReadUInt64_Success( - PublicEncodingRules ruleSet, - string inputHex, - ulong expectedValue) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt64(out ulong value); - - Assert.True(didRead, "reader.TryReadUInt64"); - Assert.Equal(expectedValue, value); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "020180")] - [InlineData(PublicEncodingRules.CER, "020180")] - [InlineData(PublicEncodingRules.DER, "020180")] - [InlineData(PublicEncodingRules.BER, "0201FF")] - [InlineData(PublicEncodingRules.CER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "0201FF")] - [InlineData(PublicEncodingRules.DER, "02028000")] - [InlineData(PublicEncodingRules.DER, "0203800000")] - [InlineData(PublicEncodingRules.DER, "020480000000")] - [InlineData(PublicEncodingRules.DER, "02058000000000")] - [InlineData(PublicEncodingRules.DER, "0206800000000000")] - [InlineData(PublicEncodingRules.DER, "020780000000000000")] - [InlineData(PublicEncodingRules.DER, "02088000000000000000")] - [InlineData(PublicEncodingRules.DER, "0209010000000000000000")] - public static void ReadUInt64_Failure(PublicEncodingRules ruleSet, string inputHex) - { - byte[] data = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - bool didRead = reader.TryReadUInt64(out ulong value); - - Assert.False(didRead, "reader.TryReadUInt64"); - Assert.Equal((uint)0, value); - } - - [Fact] - public static void ReadIntegerBytes() - { - const string Payload = "0102030405060708090A0B0C0D0E0F10"; - - // INTEGER (payload) followed by INTEGER (0) - byte[] data = ("0210" + Payload + "020100").HexToByteArray(); - AsnReader reader = new AsnReader(data, AsnEncodingRules.DER); - - ReadOnlyMemory contents = reader.ReadIntegerBytes(); - Assert.Equal(0x10, contents.Length); - Assert.Equal(Payload, contents.ByteArrayToHex()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetBigInteger(PublicEncodingRules ruleSet) - { - byte[] inputData = ( - "0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + - "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + - "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + - "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + - "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + - "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + - "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + - "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + - "DA13810B4D").HexToByteArray(); - - BigInteger expected = BigInteger.Parse( - "2075455505718444046766086325128514549301113944667492053189486607" + - "5638152321436011512404808361119326026027238444019992518081753153" + - "5965931647037093368608713442955529617501657176146245891571745113" + - "4028700771890451167051818999837042261788828826028681595867897235" + - "7967091503500375497498573022675671178275171110498592645868107163" + - "8525996766798322809764200941677343791419428587801897366593842552" + - "7272226864578661449281241619675217353931828233756506947863330597" + - "8338073826285687331647183058971791153730741973483420110408271570" + - "1367336140572971505716740825623220507359429297584634909330541150" + - "79473593821332264673455059897928082590541"); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Assert.Equal(expected, reader.ReadInteger()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetNegativeBigInteger(PublicEncodingRules ruleSet) - { - // This uses a length that doesn't line up with the array pool size so - // the fill code gets tested on netstandard. - // It's the same data as above, minus the padding and lead byte (and the - // next byte changed from 0x68 to 0x88) - byte[] inputData = ( - "0281FF8861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + - "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + - "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + - "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + - "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + - "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + - "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + - "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + - "DA13810B4D").HexToByteArray(); - - BigInteger expected = BigInteger.Parse( - "-" + - "5898547409447487884446992857601985651316300515844052200158198046" + - "7814538020408452501006415149581619776188797413593169277984980446" + - "1302361382932378450492052337986628823880000831383555853860116718" + - "5361729331647715885538858385106930514758305144777880150203212976" + - "6715081632226412951106013360243549075631850526067219857352295397" + - "2308328327377769665309386917336850273904442596855844458638806936" + - "6169824439111394938336579524651037239551388910737675470211780509" + - "8035477769907389338548451561341965157059382875181284370047601682" + - "6924486017215979427815833587119797658480104671279234402026468966" + - "86109928634475300812601680679147599027"); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Assert.Equal(expected, reader.ReadInteger()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void GetDiminutiveBigInteger(PublicEncodingRules ruleSet) - { - // GetBigInteger with the last byte removed. - // Since it is no longer an ArrayPool alignment size the fill code gets tested on netstandard. - byte[] inputData = ( - "0282010000A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" + - "FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" + - "F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" + - "70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" + - "EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" + - "E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" + - "34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" + - "9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" + - "DA13810B").HexToByteArray(); - - BigInteger expected = BigInteger.Parse( - "2075455505718444046766086325128514549301113944667492053189486607" + - "5638152321436011512404808361119326026027238444019992518081753153" + - "5965931647037093368608713442955529617501657176146245891571745113" + - "4028700771890451167051818999837042261788828826028681595867897235" + - "7967091503500375497498573022675671178275171110498592645868107163" + - "8525996766798322809764200941677343791419428587801897366593842552" + - "7272226864578661449281241619675217353931828233756506947863330597" + - "8338073826285687331647183058971791153730741973483420110408271570" + - "1367336140572971505716740825623220507359429297584634909330541150" + - "79473593821332264673455059897928082590541") >> 8; - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - Assert.Equal(expected, reader.ReadInteger()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 2, 1, 0x7E }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadIntegerBytes(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - ReadOnlyMemory value = reader.ReadIntegerBytes(); - Assert.Equal("7E", value.ByteArrayToHex()); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 2, 0, 0x80 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadIntegerBytes(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadIntegerBytes()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws(() => reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - ReadOnlyMemory value = reader.ReadIntegerBytes(new Asn1Tag(TagClass.ContextSpecific, 7)); - Assert.Equal("0080", value.ByteArrayToHex()); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0201FF", PublicTagClass.Universal, 2)] - [InlineData(PublicEncodingRules.CER, "0201FF", PublicTagClass.Universal, 2)] - [InlineData(PublicEncodingRules.DER, "0201FF", PublicTagClass.Universal, 2)] - [InlineData(PublicEncodingRules.BER, "8001FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C01FF", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4601FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ReadOnlyMemory val1 = reader.ReadIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - ReadOnlyMemory val2 = reader.ReadIntegerBytes(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - - Assert.Equal(val1.ByteArrayToHex(), val2.ByteArrayToHex()); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs deleted file mode 100644 index de04782..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadLength.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - internal static class AsnReaderExtensions - { - private delegate Asn1Tag ReadTagAndLengthDelegate(out int? parsedLength, out int bytesRead); - - public static Asn1Tag ReadTagAndLength(this AsnReader reader, out int? parsedLength, out int bytesRead) - { - return ((ReadTagAndLengthDelegate) - typeof(AsnReader).GetMethod("ReadTagAndLength", BindingFlags.Instance | BindingFlags.NonPublic) - .CreateDelegate(typeof(ReadTagAndLengthDelegate), reader)).Invoke(out parsedLength, out bytesRead); - } - } - - public sealed class ReadLength : Asn1ReaderTests - { - [Theory] - [InlineData(4, 0, "0400")] - [InlineData(1, 1, "0101")] - [InlineData(4, 127, "047F")] - [InlineData(4, 128, "048180")] - [InlineData(4, 255, "0481FF")] - [InlineData(2, 256, "02820100")] - [InlineData(4, int.MaxValue, "04847FFFFFFF")] - public static void MinimalPrimitiveLength(int tagValue, int length, string inputHex) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - foreach (PublicEncodingRules rules in Enum.GetValues(typeof(PublicEncodingRules))) - { - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)rules); - - Asn1Tag tag = reader.ReadTagAndLength(out int ? parsedLength, out int bytesRead); - - Assert.Equal(inputBytes.Length, bytesRead); - Assert.False(tag.IsConstructed, "tag.IsConstructed"); - Assert.Equal(tagValue, tag.TagValue); - Assert.Equal(length, parsedLength.Value); - - // ReadTagAndLength doesn't move the _data span forward. - Assert.True(reader.HasData, "reader.HasData"); - } - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) - { - byte[] data = { 0x05, 0x00 }; - - Assert.Throws( - () => new AsnReader(data, (AsnEncodingRules)invalidRuleSetValue)); - } - - [Theory] - [InlineData("")] - [InlineData("05")] - [InlineData("0481")] - [InlineData("048201")] - [InlineData("04830102")] - [InlineData("0484010203")] - public static void ReadWithInsufficientData(string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.DER); - - Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); - } - - [Theory] - [InlineData("DER indefinite constructed", PublicEncodingRules.DER, "3080" + "0500" + "0000")] - [InlineData("0xFF-BER", PublicEncodingRules.BER, "04FF")] - [InlineData("0xFF-CER", PublicEncodingRules.CER, "04FF")] - [InlineData("0xFF-DER", PublicEncodingRules.DER, "04FF")] - [InlineData("CER definite constructed", PublicEncodingRules.CER, "30820500")] - [InlineData("BER indefinite primitive", PublicEncodingRules.BER, "0480" + "0000")] - [InlineData("CER indefinite primitive", PublicEncodingRules.CER, "0480" + "0000")] - [InlineData("DER indefinite primitive", PublicEncodingRules.DER, "0480" + "0000")] - [InlineData("DER non-minimal 0", PublicEncodingRules.DER, "048100")] - [InlineData("DER non-minimal 7F", PublicEncodingRules.DER, "04817F")] - [InlineData("DER non-minimal 80", PublicEncodingRules.DER, "04820080")] - [InlineData("CER non-minimal 0", PublicEncodingRules.CER, "048100")] - [InlineData("CER non-minimal 7F", PublicEncodingRules.CER, "04817F")] - [InlineData("CER non-minimal 80", PublicEncodingRules.CER, "04820080")] - [InlineData("BER too large", PublicEncodingRules.BER, "048480000000")] - [InlineData("CER too large", PublicEncodingRules.CER, "048480000000")] - [InlineData("DER too large", PublicEncodingRules.DER, "048480000000")] - [InlineData("BER padded too large", PublicEncodingRules.BER, "0486000080000000")] - [InlineData("BER uint.MaxValue", PublicEncodingRules.BER, "0484FFFFFFFF")] - [InlineData("CER uint.MaxValue", PublicEncodingRules.CER, "0484FFFFFFFF")] - [InlineData("DER uint.MaxValue", PublicEncodingRules.DER, "0484FFFFFFFF")] - [InlineData("BER padded uint.MaxValue", PublicEncodingRules.BER, "048800000000FFFFFFFF")] - [InlineData("BER 5 byte spread", PublicEncodingRules.BER, "04850100000000")] - [InlineData("CER 5 byte spread", PublicEncodingRules.CER, "04850100000000")] - [InlineData("DER 5 byte spread", PublicEncodingRules.DER, "04850100000000")] - [InlineData("BER padded 5 byte spread", PublicEncodingRules.BER, "0486000100000000")] - public static void InvalidLengths( - string description, - PublicEncodingRules rules, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)rules); - - Assert.Throws(() => reader.ReadTagAndLength(out _, out _)); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void IndefiniteLength(PublicEncodingRules ruleSet) - { - // SEQUENCE (indefinite) - // NULL - // End-of-Contents - byte[] data = { 0x30, 0x80, 0x05, 0x00, 0x00, 0x00 }; - AsnReader reader = new AsnReader(data, (AsnEncodingRules)ruleSet); - - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); - - Assert.Equal(2, bytesRead); - Assert.False(length.HasValue, "length.HasValue"); - Assert.Equal((int)UniversalTagNumber.Sequence, tag.TagValue); - Assert.True(tag.IsConstructed, "tag.IsConstructed"); - } - - [Theory] - [InlineData(0, "0483000000")] - [InlineData(1, "048A00000000000000000001")] - [InlineData(128, "049000000000000000000000000000000080")] - public static void BerNonMinimalLength(int expectedLength, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); - - Assert.Equal(inputData.Length, bytesRead); - Assert.Equal(expectedLength, length.Value); - // ReadTagAndLength doesn't move the _data span forward. - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 4, 0, 5, "0483000000" + "0500")] - [InlineData(PublicEncodingRules.DER, 1, 1, 2, "0101" + "FF")] - [InlineData(PublicEncodingRules.CER, 0x10, null, 2, "3080" + "0500" + "0000")] - public static void ReadWithDataRemaining( - PublicEncodingRules ruleSet, - int tagValue, - int? expectedLength, - int expectedBytesRead, - string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Asn1Tag tag = reader.ReadTagAndLength(out int? length, out int bytesRead); - - Assert.Equal(expectedBytesRead, bytesRead); - Assert.Equal(tagValue, tag.TagValue); - Assert.Equal(expectedLength, length); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs deleted file mode 100644 index b0917c4..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNamedBitList.cs +++ /dev/null @@ -1,315 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadNamedBitList : Asn1ReaderTests - { - [Flags] - public enum X509KeyUsageCSharpStyle - { - None = 0, - DigitalSignature = 1, - NonRepudiation = 1 << 1, - KeyEncipherment = 1 << 2, - DataEncipherment = 1 << 3, - KeyAgreement = 1 << 4, - KeyCertSign = 1 << 5, - CrlSign = 1 << 6, - EncipherOnly = 1 << 7, - DecipherOnly = 1 << 8, - } - - [Flags] - public enum ULongFlags : ulong - { - None = 0, - Min = 1, - Mid = 1L << 32, - AlmostMax = 1L << 62, - Max = 1UL << 63, - } - - [Flags] - public enum LongFlags : long - { - None = 0, - Mid = 1L << 32, - Max = 1L << 62, - Min = long.MinValue, - } - - [Theory] - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.None), - "030100")] - [InlineData( - PublicEncodingRules.CER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), - "0303070480")] - [InlineData( - PublicEncodingRules.DER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.KeyAgreement), - "03020308")] - [InlineData( - PublicEncodingRules.BER, - typeof(LongFlags), - (long)(LongFlags.Mid | LongFlags.Max), - "0309010000000080000002")] - [InlineData( - PublicEncodingRules.CER, - typeof(LongFlags), - (long)(LongFlags.Mid | LongFlags.Min), - "0309000000000080000001")] - [InlineData( - PublicEncodingRules.DER, - typeof(LongFlags), - (long)(LongFlags.Min | LongFlags.Max), - "0309000000000000000003")] - // BER: Unused bits are unmapped, regardless of value. - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign), - "030307048F")] - // BER: Trailing zeros are permitted. - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), - "03050014800000")] - // BER: Trailing 0-bits don't have to be declared "unused" - [InlineData( - PublicEncodingRules.BER, - typeof(X509KeyUsageCSharpStyle), - (long)(X509KeyUsageCSharpStyle.DecipherOnly | X509KeyUsageCSharpStyle.KeyCertSign | X509KeyUsageCSharpStyle.DataEncipherment), - "0303001480")] - public static void VerifyReadNamedBitListEncodings( - PublicEncodingRules ruleSet, - Type enumType, - long enumValue, - string inputHex) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); - Enum readValue = reader.ReadNamedBitListValue(enumType); - - Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); - } - - [Theory] - [InlineData( - PublicEncodingRules.BER, - typeof(ULongFlags), - (ulong)(ULongFlags.Mid | ULongFlags.Max), - "0309000000000080000001")] - [InlineData( - PublicEncodingRules.CER, - typeof(ULongFlags), - (ulong)(ULongFlags.Min | ULongFlags.Mid), - "0306078000000080")] - [InlineData( - PublicEncodingRules.DER, - typeof(ULongFlags), - (ulong)(ULongFlags.Min | ULongFlags.Max), - "0309008000000000000001")] - public static void VerifyReadNamedBitListEncodings_ULong( - PublicEncodingRules ruleSet, - Type enumType, - ulong enumValue, - string inputHex) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputBytes, (AsnEncodingRules)ruleSet); - Enum readValue = reader.ReadNamedBitListValue(enumType); - - Assert.Equal(Enum.ToObject(enumType, enumValue), readValue); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyGenericReadNamedBitList(PublicEncodingRules ruleSet) - { - string inputHex = "0306078000000080" + "0309010000000080000002"; - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - ULongFlags uLongFlags = reader.ReadNamedBitListValue(); - LongFlags longFlags = reader.ReadNamedBitListValue(); - - Assert.False(reader.HasData); - Assert.Equal(ULongFlags.Mid | ULongFlags.Min, uLongFlags); - Assert.Equal(LongFlags.Mid | LongFlags.Max, longFlags); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_RequiresFlags(PublicEncodingRules ruleSet) - { - string inputHex = "030100"; - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "tFlagsEnum", - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_DataOutOfRange(PublicEncodingRules ruleSet) - { - string inputHex = "0309000000000100000001"; - - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_ExcessiveBytes(PublicEncodingRules ruleSet) - { - string inputHex = "03050014800000"; - - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadNamedBitList_ExcessiveBits(PublicEncodingRules ruleSet) - { - string inputHex = "0303061480"; - - AsnReader reader = new AsnReader(inputHex.HexToByteArray(), (AsnEncodingRules)ruleSet); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 3, 2, 1, 2 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNamedBitListValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - Assert.Equal( - X509KeyUsageCSharpStyle.CrlSign, - reader.ReadNamedBitListValue()); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 2, 2, 4 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNamedBitListValue(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadNamedBitListValue()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - Assert.Equal( - X509KeyUsageCSharpStyle.KeyCertSign, - reader.ReadNamedBitListValue(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0303070080", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.CER, "0303070080", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.DER, "0303070080", PublicTagClass.Universal, 3)] - [InlineData(PublicEncodingRules.BER, "8003070080", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C03070080", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4603070080", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Equal( - X509KeyUsageCSharpStyle.DecipherOnly, - reader.ReadNamedBitListValue( - new Asn1Tag((TagClass)tagClass, tagValue, true))); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Equal( - X509KeyUsageCSharpStyle.DecipherOnly, - reader.ReadNamedBitListValue( - new Asn1Tag((TagClass)tagClass, tagValue, false))); - - Assert.False(reader.HasData); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs deleted file mode 100644 index fd78f55..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadNull.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadNull : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, "0500")] - [InlineData(PublicEncodingRules.CER, "0500")] - [InlineData(PublicEncodingRules.DER, "0500")] - [InlineData(PublicEncodingRules.BER, "0583000000")] - public static void ReadNull_Success(PublicEncodingRules ruleSet, string inputHex) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - reader.ReadNull(); - Assert.False(reader.HasData, "reader.HasData"); - } - - [Theory] - [InlineData("Long length", PublicEncodingRules.CER, "0583000000")] - [InlineData("Long length", PublicEncodingRules.DER, "0583000000")] - [InlineData("Constructed definite length", PublicEncodingRules.BER, "2500")] - [InlineData("Constructed definite length", PublicEncodingRules.DER, "2500")] - [InlineData("Constructed indefinite length", PublicEncodingRules.BER, "25800000")] - [InlineData("Constructed indefinite length", PublicEncodingRules.CER, "25800000")] - [InlineData("No length", PublicEncodingRules.BER, "05")] - [InlineData("No length", PublicEncodingRules.CER, "05")] - [InlineData("No length", PublicEncodingRules.DER, "05")] - [InlineData("No data", PublicEncodingRules.BER, "")] - [InlineData("No data", PublicEncodingRules.CER, "")] - [InlineData("No data", PublicEncodingRules.DER, "")] - [InlineData("NonEmpty", PublicEncodingRules.BER, "050100")] - [InlineData("NonEmpty", PublicEncodingRules.CER, "050100")] - [InlineData("NonEmpty", PublicEncodingRules.DER, "050100")] - [InlineData("Incomplete length", PublicEncodingRules.BER, "0581")] - public static void ReadNull_Throws(string description, PublicEncodingRules ruleSet, string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadNull()); - } - - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = { 5, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - reader.ReadNull(); - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = { 0x87, 0 }; - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadNull(new Asn1Tag(UniversalTagNumber.Integer))); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadNull()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.Application, 0))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws(() => reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 1))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - reader.ReadNull(new Asn1Tag(TagClass.ContextSpecific, 7)); - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0500", PublicTagClass.Universal, 5)] - [InlineData(PublicEncodingRules.CER, "0500", PublicTagClass.Universal, 5)] - [InlineData(PublicEncodingRules.DER, "0500", PublicTagClass.Universal, 5)] - [InlineData(PublicEncodingRules.BER, "8000", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C00", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A4600", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, true)); - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - reader.ReadNull(new Asn1Tag((TagClass)tagClass, tagValue, false)); - Assert.False(reader.HasData); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs deleted file mode 100644 index d87cea3..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSequence.cs +++ /dev/null @@ -1,339 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadSequence : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, "3000", false, -1)] - [InlineData(PublicEncodingRules.BER, "30800000", false, -1)] - [InlineData(PublicEncodingRules.BER, "3083000000", false, -1)] - [InlineData(PublicEncodingRules.CER, "30800000", false, -1)] - [InlineData(PublicEncodingRules.DER, "3000", false, -1)] - [InlineData(PublicEncodingRules.BER, "3000" + "0500", true, -1)] - [InlineData(PublicEncodingRules.BER, "3002" + "0500", false, 5)] - [InlineData(PublicEncodingRules.CER, "3080" + "0500" + "0000", false, 5)] - [InlineData(PublicEncodingRules.CER, "3080" + "010100" + "0000" + "0500", true, 1)] - [InlineData(PublicEncodingRules.DER, "3005" + "0500" + "0101FF", false, 5)] - public static void ReadSequence_Success( - PublicEncodingRules ruleSet, - string inputHex, - bool expectDataRemaining, - int expectedSequenceTagNumber) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - AsnReader sequence = reader.ReadSequence(); - - if (expectDataRemaining) - { - Assert.True(reader.HasData, "reader.HasData"); - } - else - { - Assert.False(reader.HasData, "reader.HasData"); - } - - if (expectedSequenceTagNumber < 0) - { - Assert.False(sequence.HasData, "sequence.HasData"); - } - else - { - Assert.True(sequence.HasData, "sequence.HasData"); - - Asn1Tag firstTag = sequence.PeekTag(); - Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); - } - } - - [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "30")] - [InlineData("Missing Length", PublicEncodingRules.CER, "30")] - [InlineData("Missing Length", PublicEncodingRules.DER, "30")] - [InlineData("Primitive Encoding", PublicEncodingRules.BER, "1000")] - [InlineData("Primitive Encoding", PublicEncodingRules.CER, "1000")] - [InlineData("Primitive Encoding", PublicEncodingRules.DER, "1000")] - [InlineData("Definite Length Encoding", PublicEncodingRules.CER, "3000")] - [InlineData("Indefinite Length Encoding", PublicEncodingRules.DER, "3080" + "0000")] - [InlineData("Missing Content", PublicEncodingRules.BER, "3001")] - [InlineData("Missing Content", PublicEncodingRules.DER, "3001")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.BER, "3005" + "010100")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.DER, "3005" + "010100")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.BER, "3080")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.CER, "3080")] - [InlineData("Missing EoC", PublicEncodingRules.BER, "3080" + "010100")] - [InlineData("Missing EoC", PublicEncodingRules.CER, "3080" + "010100")] - [InlineData("Missing Outer EoC", PublicEncodingRules.BER, "3080" + "010100" + ("3080" + "0000"))] - [InlineData("Missing Outer EoC", PublicEncodingRules.CER, "3080" + "010100" + ("3080" + "0000"))] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.BER, "3100")] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.DER, "3100")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.BER, "3180" + "0000")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.CER, "3180" + "0000")] - public static void ReadSequence_Throws( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadSequence()); - } - - private static void ReadEcPublicKey(AsnEncodingRules ruleSet, byte[] inputData) - { - AsnReader mainReader = new AsnReader(inputData, ruleSet); - - AsnReader spkiReader = mainReader.ReadSequence(); - Assert.False(mainReader.HasData, "mainReader.HasData after reading SPKI"); - - AsnReader algorithmReader = spkiReader.ReadSequence(); - Assert.True(spkiReader.HasData, "spkiReader.HasData after reading algorithm"); - - ReadOnlyMemory publicKeyValue; - int unusedBitCount; - - if (!spkiReader.TryReadPrimitiveBitStringValue(out unusedBitCount, out publicKeyValue)) - { - // The correct answer is 65 bytes. - for (int i = 10; ; i *= 2) - { - byte[] buf = new byte[i]; - - if (spkiReader.TryCopyBitStringBytes(buf, out unusedBitCount, out int bytesWritten)) - { - publicKeyValue = new ReadOnlyMemory(buf, 0, bytesWritten); - break; - } - } - } - - Assert.False(spkiReader.HasData, "spkiReader.HasData after reading subjectPublicKey"); - Assert.True(algorithmReader.HasData, "algorithmReader.HasData before reading"); - - Oid algorithmOid = algorithmReader.ReadObjectIdentifier(); - Assert.True(algorithmReader.HasData, "algorithmReader.HasData after reading first OID"); - - Assert.Equal("1.2.840.10045.2.1", algorithmOid.Value); - - Oid curveOid = algorithmReader.ReadObjectIdentifier(); - Assert.False(algorithmReader.HasData, "algorithmReader.HasData after reading second OID"); - - Assert.Equal("1.2.840.10045.3.1.7", curveOid.Value); - - const string PublicKeyValue = - "04" + - "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + - "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; - - Assert.Equal(PublicKeyValue, publicKeyValue.ByteArrayToHex()); - Assert.Equal(0, unusedBitCount); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void ReadEcPublicKey_DefiniteLength(PublicEncodingRules ruleSet) - { - const string InputHex = - "3059" + - "3013" + - "06072A8648CE3D0201" + - "06082A8648CE3D030107" + - "0342" + - "00" + - "04" + - "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + - "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13"; - - byte[] inputData = InputHex.HexToByteArray(); - ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void ReadEcPublicKey_IndefiniteLength(PublicEncodingRules ruleSet) - { - const string InputHex = - "3080" + - "3080" + - "06072A8648CE3D0201" + - "06082A8648CE3D030107" + - "0000" + - "0342" + - "00" + - "04" + - "2363DD131DA65E899A2E63E9E05E50C830D4994662FFE883DB2B9A767DCCABA2" + - "F07081B5711BE1DEE90DFC8DE17970C2D937A16CD34581F52B8D59C9E9532D13" + - "0000"; - - byte[] inputData = InputHex.HexToByteArray(); - ReadEcPublicKey((AsnEncodingRules)ruleSet, inputData); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "30020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSequence(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "308005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSequence(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A5020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSequence()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A58005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSequence(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSequence()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "30030101FF", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.BER, "30800101000000", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.CER, "30800101000000", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.DER, "30030101FF", PublicTagClass.Universal, 16)] - [InlineData(PublicEncodingRules.BER, "A0030101FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.BER, "A1800101000000", PublicTagClass.ContextSpecific, 1)] - [InlineData(PublicEncodingRules.CER, "6C800101000000", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "FF8A46030101FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val1 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, true)); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val2 = reader.ReadSequence(new Asn1Tag((TagClass)tagClass, tagValue, false)); - - Assert.False(reader.HasData); - - Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs deleted file mode 100644 index b2d370c..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadSetOf.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadSetOf : Asn1ReaderTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, "3100", false, -1)] - [InlineData(PublicEncodingRules.BER, "31800000", false, -1)] - [InlineData(PublicEncodingRules.BER, "3183000000", false, -1)] - [InlineData(PublicEncodingRules.CER, "31800000", false, -1)] - [InlineData(PublicEncodingRules.DER, "3100", false, -1)] - [InlineData(PublicEncodingRules.BER, "3100" + "0500", true, -1)] - [InlineData(PublicEncodingRules.BER, "3102" + "0500", false, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "0500" + "0000", false, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0000" + "0500", true, 1)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", false, 1)] - [InlineData(PublicEncodingRules.DER, "3105" + "0101FF" + "0500", false, 1)] - public static void ReadSetOf_Success( - PublicEncodingRules ruleSet, - string inputHex, - bool expectDataRemaining, - int expectedSequenceTagNumber) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - AsnReader sequence = reader.ReadSetOf(); - - if (expectDataRemaining) - { - Assert.True(reader.HasData, "reader.HasData"); - } - else - { - Assert.False(reader.HasData, "reader.HasData"); - } - - if (expectedSequenceTagNumber < 0) - { - Assert.False(sequence.HasData, "sequence.HasData"); - } - else - { - Assert.True(sequence.HasData, "sequence.HasData"); - - Asn1Tag firstTag = sequence.PeekTag(); - Assert.Equal(expectedSequenceTagNumber, firstTag.TagValue); - } - } - - [Theory] - [InlineData("Empty", PublicEncodingRules.BER, "")] - [InlineData("Empty", PublicEncodingRules.CER, "")] - [InlineData("Empty", PublicEncodingRules.DER, "")] - [InlineData("Incomplete Tag", PublicEncodingRules.BER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.CER, "1F")] - [InlineData("Incomplete Tag", PublicEncodingRules.DER, "1F")] - [InlineData("Missing Length", PublicEncodingRules.BER, "31")] - [InlineData("Missing Length", PublicEncodingRules.CER, "31")] - [InlineData("Missing Length", PublicEncodingRules.DER, "31")] - [InlineData("Primitive Encoding", PublicEncodingRules.BER, "1100")] - [InlineData("Primitive Encoding", PublicEncodingRules.CER, "1100")] - [InlineData("Primitive Encoding", PublicEncodingRules.DER, "1100")] - [InlineData("Definite Length Encoding", PublicEncodingRules.CER, "3100")] - [InlineData("Indefinite Length Encoding", PublicEncodingRules.DER, "3180" + "0000")] - [InlineData("Missing Content", PublicEncodingRules.BER, "3101")] - [InlineData("Missing Content", PublicEncodingRules.DER, "3101")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.BER, "3105" + "010100")] - [InlineData("Length Out Of Bounds", PublicEncodingRules.DER, "3105" + "010100")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.BER, "3180")] - [InlineData("Missing Content - Indefinite", PublicEncodingRules.CER, "3180")] - [InlineData("Missing EoC", PublicEncodingRules.BER, "3180" + "010100")] - [InlineData("Missing EoC", PublicEncodingRules.CER, "3180" + "010100")] - [InlineData("Missing Outer EoC", PublicEncodingRules.BER, "3180" + "010100" + ("3180" + "0000"))] - [InlineData("Missing Outer EoC", PublicEncodingRules.CER, "3180" + "010100" + ("3180" + "0000"))] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.BER, "3000")] - [InlineData("Wrong Tag - Definite", PublicEncodingRules.DER, "3000")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.BER, "3080" + "0000")] - [InlineData("Wrong Tag - Indefinite", PublicEncodingRules.CER, "3080" + "0000")] - public static void ReadSetOf_Throws( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadSetOf()); - } - - [Theory] - // BER can read out of order (indefinite) - [InlineData(PublicEncodingRules.BER, "3180" + "0101FF" + "010100" + "0000", true, 1)] - // BER can read out of order (definite) - [InlineData(PublicEncodingRules.BER, "3106" + "0101FF" + "010100", true, 1)] - // CER will not read out of order - [InlineData(PublicEncodingRules.CER, "3180" + "0500" + "010100" + "0000", false, 1)] - [InlineData(PublicEncodingRules.CER, "3180" + "0101FF" + "010100" + "0000", false, 1)] - // CER is happy in order: - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0500" + "0000", true, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "0101FF" + "0500" + "0000", true, 5)] - [InlineData(PublicEncodingRules.CER, "3180" + "010100" + "010100" + "0500" + "0000", true, 5)] - // DER will not read out of order - [InlineData(PublicEncodingRules.DER, "3106" + "0101FF" + "010100", false, 1)] - [InlineData(PublicEncodingRules.DER, "3105" + "0500" + "010100", false, 1)] - // DER is happy in order: - [InlineData(PublicEncodingRules.DER, "3105" + "010100" + "0500", true, 5)] - [InlineData(PublicEncodingRules.DER, "3108" + "010100" + "0101FF" + "0500", true, 5)] - [InlineData(PublicEncodingRules.DER, "3108" + "010100" + "010100" + "0500", true, 5)] - public static void ReadSetOf_DataSorting( - PublicEncodingRules ruleSet, - string inputHex, - bool expectSuccess, - int lastTagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - AsnReader setOf; - - if (expectSuccess) - { - setOf = reader.ReadSetOf(); - } - else - { - AsnReader alsoReader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => alsoReader.ReadSetOf()); - - setOf = reader.ReadSetOf(skipSortOrderValidation: true); - } - - int lastTag = -1; - - while (setOf.HasData) - { - Asn1Tag tag = setOf.PeekTag(); - lastTag = tag.TagValue; - - // Ignore the return, just drain it. - setOf.ReadEncodedValue(); - } - - Assert.Equal(lastTagValue, lastTag); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "31020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSetOf(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Universal_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "318005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - AsnReader seq = reader.ReadSetOf(); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom_Definite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A5020500".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSetOf()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - public static void TagMustBeCorrect_Custom_Indefinite(PublicEncodingRules ruleSet) - { - byte[] inputData = "A58005000000".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadSetOf(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadSetOf()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - AsnReader seq = reader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 5)); - Assert.Equal("0500", seq.ReadEncodedValue().ByteArrayToHex()); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "31030101FF", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.BER, "31800101000000", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.CER, "31800101000000", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.DER, "31030101FF", PublicTagClass.Universal, 17)] - [InlineData(PublicEncodingRules.BER, "A0030101FF", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.BER, "A1800101000000", PublicTagClass.ContextSpecific, 1)] - [InlineData(PublicEncodingRules.CER, "6C800101000000", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "FF8A46030101FF", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val1 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, true)); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AsnReader val2 = reader.ReadSetOf(new Asn1Tag((TagClass)tagClass, tagValue, false)); - - Assert.False(reader.HasData); - - Assert.Equal(val1.ReadEncodedValue().ByteArrayToHex(), val2.ReadEncodedValue().ByteArrayToHex()); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs deleted file mode 100644 index cfe823f..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadUtcTime.cs +++ /dev/null @@ -1,239 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public sealed class ReadUtcTime : Asn1ReaderTests - { - [Theory] - // A, B2, C2 - [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332D30373030", 2017, 9, 8, 10, 35, 3, -7, 0)] - [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332D30303530", 2017, 9, 8, 10, 35, 3, 0, -50)] - [InlineData(PublicEncodingRules.BER, "17113137303930383130333530332B30373030", 2017, 9, 8, 10, 35, 3, 7, 0)] - [InlineData(PublicEncodingRules.BER, "17113030303130313030303030302B30303030", 2000, 1, 1, 0, 0, 0, 0, 0)] - [InlineData(PublicEncodingRules.BER, "17113030303130313030303030302D31343030", 2000, 1, 1, 0, 0, 0, -14, 0)] - // A, B2, C1 (only legal form for CER or DER) - [InlineData(PublicEncodingRules.BER, "170D3132303130323233353935395A", 2012, 1, 2, 23, 59, 59, 0, 0)] - [InlineData(PublicEncodingRules.CER, "170D3439313233313233353935395A", 2049, 12, 31, 23, 59, 59, 0, 0)] - [InlineData(PublicEncodingRules.DER, "170D3530303130323132333435365A", 1950, 1, 2, 12, 34, 56, 0, 0)] - // A, B1, C2 - [InlineData(PublicEncodingRules.BER, "170F313730393038313033352D30373030", 2017, 9, 8, 10, 35, 0, -7, 0)] - [InlineData(PublicEncodingRules.BER, "170F323730393038313033352B30393132", 2027, 9, 8, 10, 35, 0, 9, 12)] - // A, B1, C1 - [InlineData(PublicEncodingRules.BER, "170B313230313032323335395A", 2012, 1, 2, 23, 59, 0, 0, 0)] - [InlineData(PublicEncodingRules.BER, "170B343931323331323335395A", 2049, 12, 31, 23, 59, 0, 0, 0)] - // BER Constructed form - [InlineData( - PublicEncodingRules.BER, - "3780" + - "04023132" + - "04023031" + - "2480" + "040130" + "040132" + "0000" + - "040432333539" + - "04830000015A" + - "0000", - 2012, 1, 2, 23, 59, 0, 0, 0)] - public static void ParseTime_Valid( - PublicEncodingRules ruleSet, - string inputHex, - int year, - int month, - int day, - int hour, - int minute, - int second, - int offsetHour, - int offsetMinute) - { - byte[] inputData = inputHex.HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - DateTimeOffset value = reader.ReadUtcTime(); - - Assert.Equal(year, value.Year); - Assert.Equal(month, value.Month); - Assert.Equal(day, value.Day); - Assert.Equal(hour, value.Hour); - Assert.Equal(minute, value.Minute); - Assert.Equal(second, value.Second); - Assert.Equal(0, value.Millisecond); - Assert.Equal(new TimeSpan(offsetHour, offsetMinute, 0), value.Offset); - } - - [Fact] - public static void ParseTime_InvalidValue_LegalString() - { - byte[] inputData = "17113030303030303030303030302D31353030".HexToByteArray(); - - var exception = Assert.Throws( - () => - { - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - reader.ReadUtcTime(); - }); - - Assert.NotNull(exception.InnerException); - Assert.IsType(exception.InnerException); - } - - [Theory] - [InlineData(2011, 1912)] - [InlineData(2012, 2012)] - [InlineData(2013, 2012)] - [InlineData(2111, 2012)] - [InlineData(2112, 2112)] - [InlineData(2113, 2112)] - [InlineData(12, 12)] - [InlineData(99, 12)] - [InlineData(111, 12)] - public static void ReadUtcTime_TwoYearMaximum(int maximum, int interpretedYear) - { - byte[] inputData = "170D3132303130323233353935395A".HexToByteArray(); - - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - DateTimeOffset value = reader.ReadUtcTime(maximum); - - Assert.Equal(interpretedYear, value.Year); - } - - [Theory] - [InlineData("A,B2,C2", PublicEncodingRules.CER, "17113137303930383130333530332D30373030")] - [InlineData("A,B2,C2", PublicEncodingRules.DER, "17113137303930383130333530332D30373030")] - [InlineData("A,B1,C2", PublicEncodingRules.CER, "170F313730393038313033352D30373030")] - [InlineData("A,B1,C2", PublicEncodingRules.DER, "170F313730393038313033352D30373030")] - [InlineData("A,B1,C1", PublicEncodingRules.CER, "170B313230313032323335395A")] - [InlineData("A,B1,C1", PublicEncodingRules.DER, "170B313230313032323335395A")] - [InlineData("A,B1,C1-NotZ", PublicEncodingRules.BER, "170B313230313032323335392B")] - [InlineData("A,B1,C2-NotPlusMinus", PublicEncodingRules.BER, "170F313730393038313033352C30373030")] - [InlineData("A,B2,C2-NotPlusMinus", PublicEncodingRules.BER, "17113137303930383130333530332C30373030")] - [InlineData("A,B2,C2-MinuteOutOfRange", PublicEncodingRules.BER, "17113030303030303030303030302D31353630")] - [InlineData("A,B1,C2-MinuteOutOfRange", PublicEncodingRules.BER, "170F303030303030303030302D31353630")] - [InlineData("A1,B2,C1-NotZ", PublicEncodingRules.DER, "170D3530303130323132333435365B")] - [InlineData("A,B2,C2-MissingDigit", PublicEncodingRules.BER, "17103137303930383130333530332C303730")] - [InlineData("A,B2,C2-TooLong", PublicEncodingRules.BER, "17123137303930383130333530332B3037303030")] - [InlineData("WrongTag", PublicEncodingRules.BER, "1A0D3132303130323233353935395A")] - public static void ReadUtcTime_Throws( - string description, - PublicEncodingRules ruleSet, - string inputHex) - { - _ = description; - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - Assert.Throws(() => reader.ReadUtcTime()); - } - - [Fact] - public static void ReadUtcTime_WayTooBig_Throws() - { - // Need to exceed the length that the shared pool will return for 17: - byte[] inputData = new byte[4097+4]; - inputData[0] = 0x17; - inputData[1] = 0x82; - inputData[2] = 0x10; - inputData[3] = 0x01; - - AsnReader reader = new AsnReader(inputData, AsnEncodingRules.BER); - - Assert.Throws(() => reader.ReadUtcTime()); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Universal(PublicEncodingRules ruleSet) - { - byte[] inputData = "170D3530303130323132333435365A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadUtcTime(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 0))); - - Assert.True(reader.HasData, "HasData after wrong tag"); - - Assert.Equal( - new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), - reader.ReadUtcTime()); - - Assert.False(reader.HasData, "HasData after read"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void TagMustBeCorrect_Custom(PublicEncodingRules ruleSet) - { - byte[] inputData = "850D3530303130323132333435365A".HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - AssertExtensions.Throws( - "expectedTag", - () => reader.ReadUtcTime(Asn1Tag.Null)); - - Assert.True(reader.HasData, "HasData after bad universal tag"); - - Assert.Throws(() => reader.ReadUtcTime()); - - Assert.True(reader.HasData, "HasData after default tag"); - - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.Application, 5))); - - Assert.True(reader.HasData, "HasData after wrong custom class"); - - Assert.Throws( - () => reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 7))); - - Assert.True(reader.HasData, "HasData after wrong custom tag value"); - - Assert.Equal( - new DateTimeOffset(1950, 1, 2, 12, 34, 56, TimeSpan.Zero), - reader.ReadUtcTime(new Asn1Tag(TagClass.ContextSpecific, 5))); - - Assert.False(reader.HasData, "HasData after reading value"); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] - [InlineData(PublicEncodingRules.CER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] - [InlineData(PublicEncodingRules.DER, "170D3530303130323132333435365A", PublicTagClass.Universal, 23)] - [InlineData(PublicEncodingRules.BER, "800D3530303130323132333435365A", PublicTagClass.ContextSpecific, 0)] - [InlineData(PublicEncodingRules.CER, "4C0D3530303130323132333435365A", PublicTagClass.Application, 12)] - [InlineData(PublicEncodingRules.DER, "DF8A460D3530303130323132333435365A", PublicTagClass.Private, 1350)] - public static void ExpectedTag_IgnoresConstructed( - PublicEncodingRules ruleSet, - string inputHex, - PublicTagClass tagClass, - int tagValue) - { - byte[] inputData = inputHex.HexToByteArray(); - AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - DateTimeOffset val1 = reader.ReadUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, true)); - - Assert.False(reader.HasData); - - reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); - - DateTimeOffset val2 = reader.ReadUtcTime(new Asn1Tag((TagClass)tagClass, tagValue, false)); - - Assert.False(reader.HasData); - - Assert.Equal(val1, val2); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs deleted file mode 100644 index 183c5b7..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/ComprehensiveWriteTest.cs +++ /dev/null @@ -1,290 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -using X509KeyUsageCSharpStyle = System.Security.Cryptography.Tests.Asn1.ReadNamedBitList.X509KeyUsageCSharpStyle; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public static class ComprehensiveWriteTest - { - [Fact] - public static void WriteMicrosoftDotComCert() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - // Certificate - writer.PushSequence(); - - // tbsCertificate - writer.PushSequence(); - - // version ([0] EXPLICIT INTEGER) - Asn1Tag context0 = new Asn1Tag(TagClass.ContextSpecific, 0, true); - writer.PushSequence(context0); - writer.WriteInteger(2); - writer.PopSequence(context0); - - BigInteger serialValue = BigInteger.Parse("82365655871428336739211871484630851433"); - writer.WriteInteger(serialValue); - - // signature (algorithm) - writer.PushSequence(); - writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); - writer.WriteNull(); - writer.PopSequence(); - - // issuer - writer.PushSequence(); - WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.10", "Symantec Corporation", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.11", "Symantec Trust Network", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.3", "Symantec Class 3 EV SSL CA - G3", UniversalTagNumber.PrintableString); - writer.PopSequence(); - - // validity - writer.PushSequence(); - writer.WriteUtcTime(new DateTimeOffset(2014, 10, 15, 0, 0, 0, TimeSpan.Zero)); - writer.WriteUtcTime(new DateTimeOffset(2016, 10, 15, 23, 59, 59, TimeSpan.Zero)); - writer.PopSequence(); - - // subject - writer.PushSequence(); - WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.3", "US", UniversalTagNumber.PrintableString); - WriteRdn(writer, "1.3.6.1.4.1.311.60.2.1.2", "Washington", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.15", "Private Organization", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.5", "600413485", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.6", "US", UniversalTagNumber.PrintableString); - WriteRdn(writer, "2.5.4.17", "98052", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.8", "Washington", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.7", "Redmond", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.9", "1 Microsoft Way", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.10", "Microsoft Corporation", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.11", "MSCOM", UniversalTagNumber.UTF8String); - WriteRdn(writer, "2.5.4.3", "www.microsoft.com", UniversalTagNumber.UTF8String); - writer.PopSequence(); - - // subjectPublicKeyInfo - writer.PushSequence(); - // subjectPublicKeyInfo.algorithm - writer.PushSequence(); - writer.WriteObjectIdentifier("1.2.840.113549.1.1.1"); - writer.WriteNull(); - writer.PopSequence(); - - using (AsnWriter publicKeyWriter = new AsnWriter(AsnEncodingRules.DER)) - { - publicKeyWriter.PushSequence(); - BigInteger modulus = BigInteger.Parse( - "207545550571844404676608632512851454930111394466749205318948660756381" + - "523214360115124048083611193260260272384440199925180817531535965931647" + - "037093368608713442955529617501657176146245891571745113402870077189045" + - "116705181899983704226178882882602868159586789723579670915035003754974" + - "985730226756711782751711104985926458681071638525996766798322809764200" + - "941677343791419428587801897366593842552727222686457866144928124161967" + - "521735393182823375650694786333059783380738262856873316471830589717911" + - "537307419734834201104082715701367336140572971505716740825623220507359" + - "42929758463490933054115079473593821332264673455059897928082590541"); - publicKeyWriter.WriteInteger(modulus); - publicKeyWriter.WriteInteger(65537); - publicKeyWriter.PopSequence(); - - // subjectPublicKeyInfo.subjectPublicKey - writer.WriteBitString(publicKeyWriter.Encode()); - writer.PopSequence(); - } - - // extensions ([3] EXPLICIT Extensions) - Asn1Tag context3 = new Asn1Tag(TagClass.ContextSpecific, 3); - writer.PushSequence(context3); - writer.PushSequence(); - - Asn1Tag dnsName = new Asn1Tag(TagClass.ContextSpecific, 2); - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteCharacterString(dnsName, UniversalTagNumber.IA5String, "www.microsoft.com"); - extensionValueWriter.WriteCharacterString(dnsName, UniversalTagNumber.IA5String, "wwwqa.microsoft.com"); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.17"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.19"); - // Empty sequence as the payload for a non-CA basic constraint. - writer.WriteOctetString(new byte[] { 0x30, 0x00 }); - writer.PopSequence(); - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - // This extension doesn't use a sequence at all, just Named Bit List. - extensionValueWriter.WriteNamedBitList( - X509KeyUsageCSharpStyle.DigitalSignature | X509KeyUsageCSharpStyle.KeyEncipherment); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.15"); - // critical: true - writer.WriteBoolean(true); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.3.1"); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.3.2"); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.37"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("2.16.840.1.113733.1.7.23.6"); - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.2.1"); - extensionValueWriter.WriteCharacterString(UniversalTagNumber.IA5String, "https://d.symcb.com/cps"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.2.2"); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteCharacterString(UniversalTagNumber.VisibleString, "https://d.symcb.com/rpa"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.32"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - byte[] authorityKeyIdentifier = "0159ABE7DD3A0B59A66463D6CF200757D591E76A".HexToByteArray(); - Asn1Tag keyIdentifier = context0; - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteOctetString(keyIdentifier, authorityKeyIdentifier); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.35"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - Asn1Tag distributionPointChoice = context0; - Asn1Tag fullNameChoice = context0; - Asn1Tag generalNameUriChoice = new Asn1Tag(TagClass.ContextSpecific, 6); - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(distributionPointChoice); - extensionValueWriter.PushSequence(fullNameChoice); - extensionValueWriter.WriteCharacterString( - generalNameUriChoice, - UniversalTagNumber.IA5String, - "http://sr.symcb.com/sr.crl"); - extensionValueWriter.PopSequence(fullNameChoice); - extensionValueWriter.PopSequence(distributionPointChoice); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("2.5.29.31"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - using (AsnWriter extensionValueWriter = new AsnWriter(AsnEncodingRules.DER)) - { - extensionValueWriter.PushSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.48.1"); - extensionValueWriter.WriteCharacterString( - generalNameUriChoice, - UniversalTagNumber.IA5String, - "http://sr.symcd.com"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PushSequence(); - extensionValueWriter.WriteObjectIdentifier("1.3.6.1.5.5.7.48.2"); - extensionValueWriter.WriteCharacterString( - generalNameUriChoice, - UniversalTagNumber.IA5String, - "http://sr.symcb.com/sr.crt"); - extensionValueWriter.PopSequence(); - extensionValueWriter.PopSequence(); - - writer.PushSequence(); - writer.WriteObjectIdentifier("1.3.6.1.5.5.7.1.1"); - writer.WriteOctetString(extensionValueWriter.Encode()); - writer.PopSequence(); - } - - writer.PopSequence(); - writer.PopSequence(context3); - - // tbsCertificate - writer.PopSequence(); - - // signatureAlgorithm - writer.PushSequence(); - writer.WriteObjectIdentifier("1.2.840.113549.1.1.11"); - writer.WriteNull(); - writer.PopSequence(); - - // signature - byte[] containsSignature = ( - "010203040506070809" + - "15F8505B627ED7F9F96707097E93A51E7A7E05A3D420A5C258EC7A1CFE1843EC" + - "20ACF728AAFA7A1A1BC222A7CDBF4AF90AA26DEEB3909C0B3FB5C78070DAE3D6" + - "45BFCF840A4A3FDD988C7B3308BFE4EB3FD66C45641E96CA3352DBE2AEB4488A" + - "64A9C5FB96932BA70059CE92BD278B41299FD213471BD8165F924285AE3ECD66" + - "6C703885DCA65D24DA66D3AFAE39968521995A4C398C7DF38DFA82A20372F13D" + - "4A56ADB21B5822549918015647B5F8AC131CC5EB24534D172BC60218A88B65BC" + - "F71C7F388CE3E0EF697B4203720483BB5794455B597D80D48CD3A1D73CBBC609" + - "C058767D1FF060A609D7E3D4317079AF0CD0A8A49251AB129157F9894A036487" + - "090807060504030201").HexToByteArray(); - - writer.WriteBitString(containsSignature.AsSpan(9, 256)); - - // certificate - writer.PopSequence(); - - Assert.Equal( - ComprehensiveReadTests.MicrosoftDotComSslCertBytes.ByteArrayToHex(), - writer.Encode().ByteArrayToHex()); - } - } - - private static void WriteRdn(AsnWriter writer, string oid, string value, UniversalTagNumber valueType) - { - writer.PushSetOf(); - writer.PushSequence(); - writer.WriteObjectIdentifier(oid); - writer.WriteCharacterString(valueType, value); - writer.PopSequence(); - writer.PopSetOf(); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs deleted file mode 100644 index e66448d..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSequence.cs +++ /dev/null @@ -1,639 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class PushPopSequence : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSequence()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(); - - Assert.Throws( - () => writer.PopSequence()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(); - - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustom_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - - Assert.Throws( - () => writer.PopSequence()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushPrimitive_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(UniversalTagNumber.Sequence)); - writer.PopSequence(); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "30800000"); - } - else - { - Verify(writer, "3000"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomPrimitive_PopConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.Private, 5)); - writer.PopSequence(new Asn1Tag(TagClass.Private, 5, true)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "E5800000"); - } - else - { - Verify(writer, "E500"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(); - writer.PopSequence(new Asn1Tag(UniversalTagNumber.Sequence, isConstructed: false)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "30800000"); - } - else - { - Verify(writer, "3000"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomConstructed_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSequence(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); - writer.PopSequence(new Asn1Tag(TagClass.Private, (int)ruleSet)); - - byte tag = (byte)((int)ruleSet | 0b1110_0000); - string tagHex = tag.ToString("X2"); - string rest = ruleSet == PublicEncodingRules.CER ? "800000" : "00"; - - Verify(writer, tagHex + rest); - } - } - - [Fact] - public static void BER_WritesDefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.PushSequence(); - writer.PopSequence(); - - Verify(writer, "3000"); - } - } - - [Fact] - public static void CER_WritesIndefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.PushSequence(); - writer.PopSequence(); - - Verify(writer, "30800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(); - writer.PopSequence(); - - Verify(writer, "3000"); - } - } - - [Fact] - public static void BER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); - writer.PushSequence(tag); - writer.PopSequence(tag); - - Verify(writer, "EF00"); - } - } - - [Fact] - public static void CER_WritesIndefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); - writer.PushSequence(tag); - writer.PopSequence(tag); - - Verify(writer, "7F5B800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); - writer.PushSequence(tag); - writer.PopSequence(tag); - - Verify(writer, "BE00"); - } - } - - private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) - { - writer.PushSequence(); - { - writer.PushSequence(alt); - writer.PopSequence(alt); - - writer.PushSequence(); - { - writer.PushSequence(alt); - { - writer.PushSequence(); - writer.PopSequence(); - } - - writer.PopSequence(alt); - } - - writer.PopSequence(); - } - - writer.PopSequence(); - - Verify(writer, expectedHex); - } - - [Fact] - public static void BER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); - - TestNested(writer, alt, "300AFF7F003005FF7F023000"); - } - } - - [Fact] - public static void CER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); - - TestNested(writer, alt, "3080AC8000003080AC8030800000000000000000"); - } - } - - [Fact] - public static void DER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); - - TestNested(writer, alt, "30086500300465023000"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PushSequence()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PushSequence(new Asn1Tag(TagClass.Application, 2))); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PopSequence()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PopSequence(new Asn1Tag(TagClass.Application, 2))); - } - } - - private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) - { - writer.PushSequence(); - - // F00DF00D...F00DF00D - byte[] contentBytes = new byte[126]; - - for (int i = 0; i < contentBytes.Length; i += 2) - { - contentBytes[i] = 0xF0; - contentBytes[i + 1] = 0x0D; - } - - writer.WriteOctetString(contentBytes); - writer.PopSequence(); - - Verify(writer, expectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void SimpleContentShift(PublicEncodingRules ruleSet) - { - const string ExpectedHex = - "308180" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - [Fact] - public static void SimpleContentShift_CER() - { - const string ExpectedHex = - "3080" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "0000"; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - private static void WriteRSAPublicKeyCore(AsnEncodingRules ruleSet, string expectedHex) - { - using (AsnWriter innerWriter = new AsnWriter(ruleSet)) - { - byte[] paddedBigEndianN = ( - "00" + - "AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED369731156" + - "20968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925BCE" + - "624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8CBB" + - "5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF18" + - "7B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C94" + - "AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF2B" + - "82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6D9" + - "FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD8847").HexToByteArray(); - - // Now it's padded little-endian. - Array.Reverse(paddedBigEndianN); - BigInteger n = new BigInteger(paddedBigEndianN); - const long e = 8589935681; - - innerWriter.PushSequence(); - innerWriter.WriteInteger(n); - innerWriter.WriteInteger(e); - innerWriter.PopSequence(); - - using (AsnWriter outerWriter = new AsnWriter(ruleSet)) - { - // RSAPublicKey - outerWriter.PushSequence(); - - // AlgorithmIdentifier - outerWriter.PushSequence(); - outerWriter.WriteObjectIdentifier("1.2.840.113549.1.1.1"); - outerWriter.WriteNull(); - outerWriter.PopSequence(); - - outerWriter.WriteBitString(innerWriter.Encode()); - outerWriter.PopSequence(); - - Verify(outerWriter, expectedHex); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void WriteRSAPublicKey(PublicEncodingRules ruleSet) - { - const string ExpectedHex = - // CONSTRUCTED SEQUENCE - "30820124" + - // CONSTRUCTED SEQUENCE - "300D" + - // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) - "06092A864886F70D010101" + - // NULL - "0500" + - // BIT STRING - "03820111" + - // 0 unused bits - "00" + - // sneaky inspection of the payload bytes - // CONSTRUCTED SEQUENCE - "3082010C" + - // INTEGER (n) - "02820101" + - "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + - "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + - "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + - "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + - "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + - "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + - "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + - "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + - "47" + - // INTEGER (e) - "02050200000441"; - - WriteRSAPublicKeyCore((AsnEncodingRules)ruleSet, ExpectedHex); - } - - [Fact] - public static void WriteRSAPublicKey_CER() - { - const string ExpectedHex = - // CONSTRUCTED SEQUENCE - "3080" + - // CONSTRUCTED SEQUENCE - "3080" + - // OBJECT IDENTIFIER (1.2.840.113549.1.1.1, rsaEncryption) - "06092A864886F70D010101" + - // NULL - "0500" + - // End-of-Contents - "0000" + - // BIT STRING - "03820111" + - // 0 unused bits - "00" + - // sneaky inspection of the payload bytes - // CONSTRUCTED SEQUENCE - "3080" + - // INTEGER (n) - "02820101" + - "00AF81C1CBD8203F624A539ED6608175372393A2837D4890E48A19DED3697311" + - "5620968D6BE0D3DAA38AA777BE02EE0B6B93B724E8DCC12B632B4FA80BBC925B" + - "CE624F4CA7CC606306B39403E28C932D24DD546FFE4EF6A37F10770B2215EA8C" + - "BB5BF427E8C4D89B79EB338375100C5F83E55DE9B4466DDFBEEE42539AEF33EF" + - "187B7760C3B1A1B2103C2D8144564A0C1039A09C85CF6B5974EB516FC8D6623C" + - "94AE3A5A0BB3B4C792957D432391566CF3E2A52AFB0C142B9E0681B8972671AF" + - "2B82DD390A39B939CF719568687E4990A63050CA7768DCD6B378842F18FDB1F6" + - "D9FF096BAF7BEB98DCF930D66FCFD503F58D41BFF46212E24E3AFC45EA42BD88" + - "47" + - // INTEGER (e) - "02050200000441" + - // End-of-Contents - "0000" + - // (no EoC for the BIT STRING) - // End-of-Contents - "0000"; - - WriteRSAPublicKeyCore(AsnEncodingRules.CER, ExpectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, true)] - public static void CannotEncodeWhileUnbalanced(PublicEncodingRules ruleSet, bool customTag) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - } - else - { - writer.PushSequence(); - } - - int written = -5; - - Assert.Throws(() => writer.Encode()); - Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); - Assert.Equal(-5, written); - - byte[] buf = new byte[10]; - Assert.Throws(() => writer.TryEncode(buf, out written)); - Assert.Equal(-5, written); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSequence_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.PushSequence(Asn1Tag.EndOfContents)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSetOf_PopSequence(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - - writer.PushSetOf(tag); - - Assert.Throws( - () => writer.PopSequence(tag)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs deleted file mode 100644 index adb4ff9..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/PushPopSetOf.cs +++ /dev/null @@ -1,622 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class PushPopSetOf : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopNewWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Assert.Throws( - () => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PopBalancedWriter_CustomTag(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustom_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - - Assert.Throws( - () => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopCustom(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushPrimitive_PopStandard(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); - writer.PopSetOf(); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "31800000"); - } - else - { - Verify(writer, "3100"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomPrimitive_PopConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.Private, 5)); - writer.PopSetOf(new Asn1Tag(TagClass.Private, 5, true)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "E5800000"); - } - else - { - Verify(writer, "E500"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushStandard_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(); - writer.PopSetOf(new Asn1Tag(UniversalTagNumber.SetOf)); - - if (ruleSet == PublicEncodingRules.CER) - { - Verify(writer, "31800000"); - } - else - { - Verify(writer, "3100"); - } - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushCustomConstructed_PopPrimitive(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.PushSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet, true)); - writer.PopSetOf(new Asn1Tag(TagClass.Private, (int)ruleSet)); - - byte tag = (byte)((int)ruleSet | 0b1110_0000); - string tagHex = tag.ToString("X2"); - string rest = ruleSet == PublicEncodingRules.CER ? "800000" : "00"; - - Verify(writer, tagHex + rest); - } - } - - [Fact] - public static void BER_WritesDefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Verify(writer, "3100"); - } - } - - [Fact] - public static void CER_WritesIndefinite_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Verify(writer, "31800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag_Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSetOf(); - writer.PopSetOf(); - - Verify(writer, "3100"); - } - } - - [Fact] - public static void BER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 15, true); - writer.PushSetOf(tag); - writer.PopSetOf(tag); - - Verify(writer, "EF00"); - } - } - - [Fact] - public static void CER_WritesIndefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 91, true); - writer.PushSetOf(tag); - writer.PopSetOf(tag); - - Verify(writer, "7F5B800000"); - } - } - - [Fact] - public static void DER_WritesDefinite_CustomTag__Empty() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 30, true); - writer.PushSetOf(tag); - writer.PopSetOf(tag); - - Verify(writer, "BE00"); - } - } - - private static void TestNested(AsnWriter writer, Asn1Tag alt, string expectedHex) - { - // Written in pre-sorted order, since sorting is a different test. - writer.PushSetOf(); - { - writer.PushSetOf(); - { - writer.PushSetOf(alt); - { - writer.PushSetOf(); - writer.PopSetOf(); - } - - writer.PopSetOf(alt); - } - - writer.PopSetOf(); - - writer.PushSetOf(alt); - writer.PopSetOf(alt); - } - - writer.PopSetOf(); - - Verify(writer, expectedHex); - } - - [Fact] - public static void BER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Private, 127, true); - - TestNested(writer, alt, "310A3105FF7F023100FF7F00"); - } - } - - [Fact] - public static void CER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.ContextSpecific, 12, true); - - TestNested(writer, alt, "31803180AC803180000000000000AC8000000000"); - } - } - - [Fact] - public static void DER_Nested() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag alt = new Asn1Tag(TagClass.Application, 5, true); - - TestNested(writer, alt, "31083104650231006500"); - } - } - - private static void SimpleContentShiftCore(AsnWriter writer, string expectedHex) - { - writer.PushSetOf(); - - // F00DF00D...F00DF00D - byte[] contentBytes = new byte[126]; - - for (int i = 0; i < contentBytes.Length; i += 2) - { - contentBytes[i] = 0xF0; - contentBytes[i + 1] = 0x0D; - } - - writer.WriteOctetString(contentBytes); - writer.PopSetOf(); - - Verify(writer, expectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - public static void SimpleContentShift(PublicEncodingRules ruleSet) - { - const string ExpectedHex = - "318180" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D"; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - [Fact] - public static void SimpleContentShift_CER() - { - const string ExpectedHex = - "3180" + - "047E" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "F00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00DF00D" + - "0000"; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - SimpleContentShiftCore(writer, ExpectedHex); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PushSetOf()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Push_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PushSetOf(new Asn1Tag(TagClass.Application, 2))); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws(() => writer.PopSetOf()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void Pop_Custom_After_Dispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.PopSetOf(new Asn1Tag(TagClass.Application, 2))); - } - } - - private static void ValidateDataSorting(AsnEncodingRules ruleSet, string expectedHex) - { - using (AsnWriter writer = new AsnWriter(ruleSet)) - { - writer.PushSetOf(); - - // 02 01 FF - writer.WriteInteger(-1); - // 02 01 00 - writer.WriteInteger(0); - // 02 02 00 FF - writer.WriteInteger(255); - // 01 01 FF - writer.WriteBoolean(true); - // 45 01 00 - writer.WriteBoolean(new Asn1Tag(TagClass.Application, 5), false); - // 02 01 7F - writer.WriteInteger(127); - // 02 01 80 - writer.WriteInteger(sbyte.MinValue); - // 02 02 00 FE - writer.WriteInteger(254); - // 02 01 00 - writer.WriteInteger(0); - - writer.PopSetOf(); - - // The correct sort order (CER, DER) is - // Universal Boolean: true - // Universal Integer: 0 - // Universal Integer: 0 - // Universal Integer: 127 - // Universal Integer: -128 - // Universal Integer: -1 - // Universal Integer: 254 - // Universal Integer: 255 - // Application 5 (Boolean): false - - // This test would be - // - // GrabBag ::= SET OF GrabBagItem - // - // GrabBagItem ::= CHOICE ( - // value INTEGER - // bool BOOLEAN - // grr [APPLICATION 5] IMPLICIT BOOLEAN - // ) - - Verify(writer, expectedHex); - } - } - - [Fact] - public static void BER_DoesNotSort() - { - const string ExpectedHex = - "311D" + - "0201FF" + - "020100" + - "020200FF" + - "0101FF" + - "450100" + - "02017F" + - "020180" + - "020200FE" + - "020100"; - - ValidateDataSorting(AsnEncodingRules.BER, ExpectedHex); - } - - [Fact] - public static void CER_SortsData() - { - const string ExpectedHex = - "3180" + - "0101FF" + - "020100" + - "020100" + - "02017F" + - "020180" + - "0201FF" + - "020200FE" + - "020200FF" + - "450100" + - "0000"; - - ValidateDataSorting(AsnEncodingRules.CER, ExpectedHex); - } - - [Fact] - public static void DER_SortsData() - { - const string ExpectedHex = - "311D" + - "0101FF" + - "020100" + - "020100" + - "02017F" + - "020180" + - "0201FF" + - "020200FE" + - "020200FF" + - "450100"; - - ValidateDataSorting(AsnEncodingRules.DER, ExpectedHex); - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, true)] - public static void CannotEncodeWhileUnbalanced(PublicEncodingRules ruleSet, bool customTag) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.PushSetOf(new Asn1Tag(TagClass.ContextSpecific, (int)ruleSet, true)); - } - else - { - writer.PushSetOf(); - } - - int written = -5; - - Assert.Throws(() => writer.Encode()); - Assert.Throws(() => writer.TryEncode(Span.Empty, out written)); - Assert.Equal(-5, written); - - byte[] buf = new byte[10]; - Assert.Throws(() => writer.TryEncode(buf, out written)); - Assert.Equal(-5, written); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSetOf_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.PushSetOf(Asn1Tag.EndOfContents)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void PushSequence_PopSetOf(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - - writer.PushSequence(tag); - - Assert.Throws( - () => writer.PopSetOf(tag)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs deleted file mode 100644 index dbe72ac..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBitString.cs +++ /dev/null @@ -1,366 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteBitString : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteEmpty(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(ReadOnlySpan.Empty); - - Verify(writer, "030100"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 1, 1, "030201")] - [InlineData(PublicEncodingRules.CER, 2, 1, "030301")] - [InlineData(PublicEncodingRules.DER, 3, 1, "030401")] - [InlineData(PublicEncodingRules.BER, 126, 0, "037F00")] - [InlineData(PublicEncodingRules.CER, 127, 3, "03818003")] - [InlineData(PublicEncodingRules.BER, 999, 0, "038203E800")] - [InlineData(PublicEncodingRules.CER, 999, 0, "038203E800")] - [InlineData(PublicEncodingRules.DER, 999, 0, "038203E800")] - [InlineData(PublicEncodingRules.BER, 1000, 0, "038203E900")] - [InlineData(PublicEncodingRules.DER, 1000, 0, "038203E900")] - [InlineData(PublicEncodingRules.BER, 2000, 0, "038207D100")] - [InlineData(PublicEncodingRules.DER, 2000, 0, "038207D100")] - public void WritePrimitive(PublicEncodingRules ruleSet, int length, int unusedBitCount, string hexStart) - { - string payloadHex = new string('0', 2 * length); - string expectedHex = hexStart + payloadHex; - byte[] data = new byte[length]; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(data, unusedBitCount); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(1000, 1, "2380038203E800", "030201")] - [InlineData(999*2, 3, "2380038203E800", "038203E803")] - public void WriteSegmentedCER(int length, int unusedBitCount, string hexStart, string hexStart2) - { - string payload1Hex = new string('8', 999 * 2); - string payload2Hex = new string('8', (length - 999) * 2); - string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; - byte[] data = new byte[length]; - - for (int i = 0; i < data.Length; i++) - { - data[i] = 0x88; - } - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteBitString(data, unusedBitCount); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, false)] - [InlineData(PublicEncodingRules.CER, 0, false)] - [InlineData(PublicEncodingRules.DER, 0, false)] - [InlineData(PublicEncodingRules.BER, 999, false)] - [InlineData(PublicEncodingRules.CER, 999, false)] - [InlineData(PublicEncodingRules.DER, 999, false)] - [InlineData(PublicEncodingRules.BER, 1000, false)] - [InlineData(PublicEncodingRules.CER, 1000, true)] - [InlineData(PublicEncodingRules.DER, 1000, false)] - [InlineData(PublicEncodingRules.BER, 1998, false)] - [InlineData(PublicEncodingRules.CER, 1998, true)] - [InlineData(PublicEncodingRules.BER, 4096, false)] - [InlineData(PublicEncodingRules.CER, 4096, true)] - [InlineData(PublicEncodingRules.DER, 4096, false)] - public void VerifyWriteBitString_PrimitiveOrConstructed( - PublicEncodingRules ruleSet, - int payloadLength, - bool expectConstructed) - { - byte[] data = new byte[payloadLength]; - - Asn1Tag[] tagsToTry = - { - new Asn1Tag(UniversalTagNumber.BitString), - new Asn1Tag(UniversalTagNumber.BitString, isConstructed: true), - new Asn1Tag(TagClass.Private, 87), - new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), - }; - - byte[] answerBuf = new byte[payloadLength + 100]; - - foreach (Asn1Tag toTry in tagsToTry) - { - Asn1Tag writtenTag; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBitString(toTry, data); - - Assert.True(writer.TryEncode(answerBuf, out _)); - Assert.True(Asn1Tag.TryDecode(answerBuf, out writtenTag, out _)); - } - - if (expectConstructed) - { - Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - else - { - Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - - Assert.Equal(toTry.TagClass, writtenTag.TagClass); - Assert.Equal(toTry.TagValue, writtenTag.TagValue); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "FF", false)] - [InlineData(PublicEncodingRules.BER, 1, "FE", false)] - [InlineData(PublicEncodingRules.CER, 1, "FE", false)] - [InlineData(PublicEncodingRules.DER, 1, "FE", false)] - [InlineData(PublicEncodingRules.BER, 1, "FF", true)] - [InlineData(PublicEncodingRules.CER, 1, "FF", true)] - [InlineData(PublicEncodingRules.DER, 1, "FF", true)] - [InlineData(PublicEncodingRules.BER, 7, "C0", true)] - [InlineData(PublicEncodingRules.CER, 7, "C0", true)] - [InlineData(PublicEncodingRules.DER, 7, "C0", true)] - [InlineData(PublicEncodingRules.BER, 7, "80", false)] - [InlineData(PublicEncodingRules.CER, 7, "80", false)] - [InlineData(PublicEncodingRules.DER, 7, "80", false)] - [InlineData(PublicEncodingRules.DER, 7, "40", true)] - [InlineData(PublicEncodingRules.DER, 6, "40", false)] - [InlineData(PublicEncodingRules.DER, 6, "C0", false)] - [InlineData(PublicEncodingRules.DER, 6, "20", true)] - [InlineData(PublicEncodingRules.DER, 5, "20", false)] - [InlineData(PublicEncodingRules.DER, 5, "A0", false)] - [InlineData(PublicEncodingRules.DER, 5, "10", true)] - [InlineData(PublicEncodingRules.DER, 4, "10", false)] - [InlineData(PublicEncodingRules.DER, 4, "90", false)] - [InlineData(PublicEncodingRules.DER, 4, "30", false)] - [InlineData(PublicEncodingRules.DER, 4, "08", true)] - [InlineData(PublicEncodingRules.DER, 4, "88", true)] - [InlineData(PublicEncodingRules.DER, 3, "08", false)] - [InlineData(PublicEncodingRules.DER, 3, "A8", false)] - [InlineData(PublicEncodingRules.DER, 3, "04", true)] - [InlineData(PublicEncodingRules.DER, 3, "14", true)] - [InlineData(PublicEncodingRules.DER, 2, "04", false)] - [InlineData(PublicEncodingRules.DER, 2, "0C", false)] - [InlineData(PublicEncodingRules.DER, 2, "FC", false)] - [InlineData(PublicEncodingRules.DER, 2, "02", true)] - [InlineData(PublicEncodingRules.DER, 2, "82", true)] - [InlineData(PublicEncodingRules.DER, 2, "FE", true)] - [InlineData(PublicEncodingRules.DER, 1, "02", false)] - [InlineData(PublicEncodingRules.DER, 1, "82", false)] - [InlineData(PublicEncodingRules.DER, 1, "80", false)] - public static void WriteBitString_UnusedBitCount_MustBeValid( - PublicEncodingRules ruleSet, - int unusedBitCount, - string inputHex, - bool expectThrow) - { - byte[] inputBytes = inputHex.HexToByteArray(); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (expectThrow) - { - Assert.Throws( - () => writer.WriteBitString(inputBytes, unusedBitCount)); - - Assert.Throws( - () => writer.WriteBitString( - new Asn1Tag(TagClass.ContextSpecific, 3), - inputBytes, - unusedBitCount)); - - return; - } - - byte[] output = new byte[512]; - writer.WriteBitString(inputBytes, unusedBitCount); - Assert.True(writer.TryEncode(output, out int bytesWritten)); - - // This assumes that inputBytes is never more than 999 (and avoids CER constructed forms) - Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); - - writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 9), inputBytes, unusedBitCount); - Assert.True(writer.TryEncode(output, out bytesWritten)); - - Assert.Equal(unusedBitCount, output[bytesWritten - inputBytes.Length - 1]); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, -1)] - [InlineData(PublicEncodingRules.CER, -1)] - [InlineData(PublicEncodingRules.DER, -1)] - [InlineData(PublicEncodingRules.BER, -2)] - [InlineData(PublicEncodingRules.CER, -2)] - [InlineData(PublicEncodingRules.DER, -2)] - [InlineData(PublicEncodingRules.BER, 8)] - [InlineData(PublicEncodingRules.CER, 8)] - [InlineData(PublicEncodingRules.DER, 8)] - [InlineData(PublicEncodingRules.BER, 9)] - [InlineData(PublicEncodingRules.CER, 9)] - [InlineData(PublicEncodingRules.DER, 9)] - [InlineData(PublicEncodingRules.BER, 1048576)] - [InlineData(PublicEncodingRules.CER, 1048576)] - [InlineData(PublicEncodingRules.DER, 1048576)] - public static void UnusedBitCounts_Bounds(PublicEncodingRules ruleSet, int unusedBitCount) - { - byte[] data = new byte[5]; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - ArgumentOutOfRangeException exception = AssertExtensions.Throws( - nameof(unusedBitCount), - () => writer.WriteBitString(data, unusedBitCount)); - - Assert.Equal(unusedBitCount, exception.ActualValue); - - exception = AssertExtensions.Throws( - nameof(unusedBitCount), - () => writer.WriteBitString(new Asn1Tag(TagClass.ContextSpecific, 5), data, unusedBitCount)); - - Assert.Equal(unusedBitCount, exception.ActualValue); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void EmptyData_Requires0UnusedBits(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 7)); - - Asn1Tag contextTag = new Asn1Tag(TagClass.ContextSpecific, 19); - - Assert.Throws( - () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 1)); - - Assert.Throws( - () => writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 7)); - - writer.WriteBitString(ReadOnlySpan.Empty, 0); - writer.WriteBitString(contextTag, ReadOnlySpan.Empty, 0); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, PublicTagClass.Universal, 3, "030100")] - [InlineData(PublicEncodingRules.CER, PublicTagClass.Universal, 3, "030100")] - [InlineData(PublicEncodingRules.DER, PublicTagClass.Universal, 3, "030100")] - [InlineData(PublicEncodingRules.BER, PublicTagClass.Private, 1, "C10100")] - [InlineData(PublicEncodingRules.CER, PublicTagClass.Application, 5, "450100")] - [InlineData(PublicEncodingRules.DER, PublicTagClass.ContextSpecific, 32, "9F200100")] - public static void EmptyData_Allows0UnusedBits( - PublicEncodingRules ruleSet, - PublicTagClass tagClass, - int tagValue, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - - if (tagClass == PublicTagClass.Universal) - { - Debug.Assert(tagValue == (int)UniversalTagNumber.BitString); - writer.WriteBitString(ReadOnlySpan.Empty, 0); - } - else - { - writer.WriteBitString(new Asn1Tag((TagClass)tagClass, tagValue), ReadOnlySpan.Empty, 0); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteBitString_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.EndOfContents, new byte[1])); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty)); - - Assert.Throws( - () => writer.WriteBitString(ReadOnlySpan.Empty, 1)); - - AssertExtensions.Throws( - "unusedBitCount", - () => writer.WriteBitString(ReadOnlySpan.Empty, 9)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBitString(Asn1Tag.Boolean, ReadOnlySpan.Empty)); - - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 11); - - Assert.Throws( - () => writer.WriteBitString(tag, ReadOnlySpan.Empty)); - - Assert.Throws( - () => writer.WriteBitString(tag, ReadOnlySpan.Empty, 1)); - - AssertExtensions.Throws( - "unusedBitCount", - () => writer.WriteBitString(tag, ReadOnlySpan.Empty, 9)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs deleted file mode 100644 index 556d347..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteBoolean.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteBoolean : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, false, "010100")] - [InlineData(PublicEncodingRules.BER, true, "0101FF")] - [InlineData(PublicEncodingRules.CER, false, "010100")] - [InlineData(PublicEncodingRules.CER, true, "0101FF")] - [InlineData(PublicEncodingRules.DER, false, "010100")] - [InlineData(PublicEncodingRules.DER, true, "0101FF")] - public void VerifyWriteBoolean(PublicEncodingRules ruleSet, bool value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false, "830100")] - [InlineData(PublicEncodingRules.BER, true, "8301FF")] - [InlineData(PublicEncodingRules.CER, false, "830100")] - [InlineData(PublicEncodingRules.CER, true, "8301FF")] - [InlineData(PublicEncodingRules.DER, false, "830100")] - [InlineData(PublicEncodingRules.DER, true, "8301FF")] - public void VerifyWriteBoolean_Context3(PublicEncodingRules ruleSet, bool value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 3), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.DER, true)] - public void VerifyWriteBoolean_EndOfContents(PublicEncodingRules ruleSet, bool value) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.EndOfContents, value)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, false)] - [InlineData(PublicEncodingRules.BER, true)] - [InlineData(PublicEncodingRules.CER, false)] - [InlineData(PublicEncodingRules.CER, true)] - [InlineData(PublicEncodingRules.DER, false)] - [InlineData(PublicEncodingRules.DER, true)] - public void VerifyWriteBoolean_ConstructedIgnored(PublicEncodingRules ruleSet, bool value) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteBoolean(new Asn1Tag(TagClass.ContextSpecific, 7, true), value); - writer.WriteBoolean(new Asn1Tag(UniversalTagNumber.Boolean, true), value); - - if (value) - { - Verify(writer, "8701FF0101FF"); - } - else - { - Verify(writer, "870100010100"); - } - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteBoolean(false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.Integer, false)); - - Assert.Throws( - () => writer.WriteBoolean(new Asn1Tag(TagClass.Private, 3), false)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs deleted file mode 100644 index 5637e9d..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteCharacterString.cs +++ /dev/null @@ -1,548 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using System.Text; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public abstract class WriteCharacterString : Asn1WriterTests - { - internal abstract void WriteString(AsnWriter writer, string s); - internal abstract void WriteString(AsnWriter writer, Asn1Tag tag, string s); - - internal abstract void WriteSpan(AsnWriter writer, ReadOnlySpan s); - internal abstract void WriteSpan(AsnWriter writer, Asn1Tag tag, ReadOnlySpan s); - - internal abstract Asn1Tag StandardTag { get; } - - protected const string GettysburgAddress = - "Four score and seven years ago our fathers brought forth on this continent, a new nation, " + - "conceived in Liberty, and dedicated to the proposition that all men are created equal.\r\n" + - "\r\n" + - "Now we are engaged in a great civil war, testing whether that nation, or any nation so " + - "conceived and so dedicated, can long endure. We are met on a great battle-field of that " + - "war. We have come to dedicate a portion of that field, as a final resting place for those " + - "who here gave their lives that that nation might live. It is altogether fitting and proper " + - "that we should do this.\r\n" + - "\r\n" + - "But, in a larger sense, we can not dedicate-we can not consecrate-we can not hallow-this " + - "ground. The brave men, living and dead, who struggled here, have consecrated it, far above " + - "our poor power to add or detract. The world will little note, nor long remember what we say " + - "here, but it can never forget what they did here. It is for us the living, rather, to be " + - "dedicated here to the unfinished work which they who fought here have thus far so nobly " + - "advanced. It is rather for us to be here dedicated to the great task remaining before " + - "us-that from these honored dead we take increased devotion to that cause for which they " + - "gave the last full measure of devotion-that we here highly resolve that these dead shall " + - "not have died in vain-that this nation, under God, shall have a new birth of freedom-and " + - "that government of the people, by the people, for the people, shall not perish from the earth."; - - protected void VerifyWrite_BER_String_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_String_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 14); - WriteString(writer, tag, input); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 19); - WriteString(writer, tag, input); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteString(writer, input); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 2); - WriteString(writer, tag, input); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteSpan(writer, input.AsSpan()); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, int.MaxValue >> 1); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteSpan(writer, input.AsSpan()); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 30); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - WriteSpan(writer, input.AsSpan()); - - Verify(writer, Stringify(StandardTag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_CustomTag_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 31); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(tag) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteString(writer, tag, input); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteString(writer, tag, input); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_BER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteString(writer, tag, input); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 1701, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteString(writer, tag, input); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_CER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 11, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteString(writer, tag, input); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_String_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 19, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteString(writer, tag, input); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, isConstructed: true); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(standard) + expectedPayloadHex); - } - } - - protected void VerifyWrite_DER_Span_CustomTag_ClearsConstructed_Helper(string input, string expectedPayloadHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 24601, isConstructed: true); - Asn1Tag expected = new Asn1Tag(tag.TagClass, tag.TagValue); - WriteSpan(writer, tag, input.AsSpan()); - - Verify(writer, Stringify(expected) + expectedPayloadHex); - } - } - - protected void VerifyWrite_String_Null_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "str", - () => WriteString(writer, null)); - } - } - - protected void VerifyWrite_String_Null_CustomTag_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "str", - () => WriteString(writer, new Asn1Tag(TagClass.ContextSpecific, 3), null)); - } - } - - protected void VerifyWrite_EndOfContents_String_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => WriteString(writer, Asn1Tag.EndOfContents, "hi")); - } - } - - protected void VerifyWrite_EndOfContents_Span_Helper(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => WriteSpan(writer, Asn1Tag.EndOfContents, "hi".AsSpan())); - } - } - - private void VerifyWrite_CERSegmented(AsnWriter writer, string tagHex, int contentByteCount) - { - int div = Math.DivRem(contentByteCount, 1000, out int rem); - - // tag, length(80), div full segments at 1004 bytes each, and the end of contents. - int encodedSize = (tagHex.Length / 2) + 1 + 1004 * div + 2; - - if (rem != 0) - { - // tag, contents (length TBD) - encodedSize += 1 + rem; - - if (encodedSize < 0x80) - encodedSize++; - else if (encodedSize <= 0xFF) - encodedSize += 2; - else - encodedSize += 3; - } - - byte[] encoded = writer.Encode(); - - Assert.Equal(tagHex, encoded.AsSpan(0, tagHex.Length / 2).ByteArrayToHex()); - Assert.Equal("0000", encoded.AsSpan(encoded.Length - 2).ByteArrayToHex()); - Assert.Equal(encodedSize, encoded.Length); - } - - protected void VerifyWrite_CERSegmented_String_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteString(writer, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_String_CustomTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); - string tagHex = Stringify(tag); - - WriteString(writer, tag, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_String_ConstructedTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteString(writer, tag, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_String_CustomPrimitiveTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); - Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); - string tagHex = Stringify(constr); - - WriteString(writer, prim, input); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteSpan(writer, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_CustomTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 7, true); - string tagHex = Stringify(tag); - - WriteSpan(writer, tag, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_ConstructedTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag standard = StandardTag; - Asn1Tag tag = new Asn1Tag(standard.TagClass, standard.TagValue, true); - string tagHex = Stringify(tag); - - WriteSpan(writer, tag, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_CERSegmented_Span_CustomPrimitiveTag_Helper(string input, int contentByteCount) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag prim = new Asn1Tag(TagClass.Application, 42); - Asn1Tag constr = new Asn1Tag(prim.TagClass, prim.TagValue, true); - string tagHex = Stringify(constr); - - WriteSpan(writer, prim, input.AsSpan()); - VerifyWrite_CERSegmented(writer, tagHex, contentByteCount); - } - } - - protected void VerifyWrite_String_NonEncodable_Helper(string input) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.Throws(() => WriteString(writer, input)); - } - } - - protected void VerifyWrite_Span_NonEncodable_Helper(string input) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.Throws(() => WriteSpan(writer, input.AsSpan())); - } - } - - protected void WriteAfterDispose_Span_Helper(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - string input = "1"; - - Assert.Throws( - () => WriteSpan(writer, input.AsSpan())); - - AssertExtensions.Throws( - "tag", - () => WriteSpan(writer, Asn1Tag.Boolean, input.AsSpan())); - - Assert.Throws( - () => WriteSpan(writer, new Asn1Tag(TagClass.Application, 0), input.AsSpan())); - } - } - - protected void WriteAfterDispose_String_Helper(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - string input = "1"; - - Assert.Throws( - () => WriteString(writer, input)); - - AssertExtensions.Throws( - "tag", - () => WriteString(writer, Asn1Tag.Boolean, input)); - - Assert.Throws( - () => WriteString(writer, new Asn1Tag(TagClass.Application, 0), input)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs deleted file mode 100644 index 20280d2..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteEnumerated.cs +++ /dev/null @@ -1,526 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.X509Certificates; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteEnumerated : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.SByteBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.SByteBacked.Pillow, true, "9E01EF")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.SByteBacked.Fluff, false, "0A0153")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.SByteBacked.Fluff, true, "9E0153")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.SByteBacked)(-127), true, "9E0181")] - public void VerifyWriteEnumerated_SByte( - PublicEncodingRules ruleSet, - ReadEnumerated.SByteBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 30), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ByteBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.ByteBacked.NotFluffy, true, "9A010B")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.ByteBacked.Fluff, false, "0A010C")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ByteBacked.Fluff, true, "9A010C")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ByteBacked)253, false, "0A0200FD")] - public void VerifyWriteEnumerated_Byte( - PublicEncodingRules ruleSet, - ReadEnumerated.ByteBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 26), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ShortBacked.Zero, true, "DF81540100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.ShortBacked.Pillow, true, "DF815402FC00")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.ShortBacked.Fluff, false, "0A020209")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ShortBacked.Fluff, true, "DF8154020209")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)25321, false, "0A0262E9")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)(-12345), false, "0A02CFC7")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ShortBacked)(-1), true, "DF815401FF")] - public void VerifyWriteEnumerated_Short( - PublicEncodingRules ruleSet, - ReadEnumerated.ShortBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 212), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Zero, true, "4D0100")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.UShortBacked.Fluff, false, "0A03008000")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UShortBacked.Fluff, true, "4D03008000")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.UShortBacked)11, false, "0A010B")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.UShortBacked)short.MaxValue, false, "0A027FFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.UShortBacked)ushort.MaxValue, true, "4D0300FFFF")] - public void VerifyWriteEnumerated_UShort( - PublicEncodingRules ruleSet, - ReadEnumerated.UShortBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, 13), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.IntBacked.Zero, true, "5F81FF7F0100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.IntBacked.Pillow, true, "5F81FF7F03FEFFFF")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.IntBacked.Fluff, false, "0A03010001")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.IntBacked.Fluff, true, "5F81FF7F03010001")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)25321, false, "0A0262E9")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.IntBacked)(-12345), false, "0A02CFC7")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.IntBacked)(-1), true, "5F81FF7F01FF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)int.MinValue, true, "5F81FF7F0480000000")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.IntBacked)int.MaxValue, false, "0A047FFFFFFF")] - public void VerifyWriteEnumerated_Int( - PublicEncodingRules ruleSet, - ReadEnumerated.IntBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Application, short.MaxValue), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Zero, true, "9F610100")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.UIntBacked.Fluff, false, "0A050080000005")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.UIntBacked.Fluff, true, "9F61050080000005")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.UIntBacked)11, false, "0A010B")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.UIntBacked)short.MaxValue, false, "0A027FFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.UIntBacked)ushort.MaxValue, true, "9F610300FFFF")] - public void VerifyWriteEnumerated_UInt( - PublicEncodingRules ruleSet, - ReadEnumerated.UIntBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 97), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.LongBacked.Zero, true, "800100")] - [InlineData(PublicEncodingRules.CER, ReadEnumerated.LongBacked.Pillow, true, "8005FF00000000")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.LongBacked.Fluff, false, "0A050200000441")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.LongBacked.Fluff, true, "80050200000441")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)25321, false, "0A0262E9")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.LongBacked)(-12345), false, "0A02CFC7")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.LongBacked)(-1), true, "8001FF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)int.MinValue, true, "800480000000")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.LongBacked)int.MaxValue, false, "0A047FFFFFFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.LongBacked)long.MinValue, false, "0A088000000000000000")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.LongBacked)long.MaxValue, true, "80087FFFFFFFFFFFFFFF")] - public void VerifyWriteEnumerated_Long( - PublicEncodingRules ruleSet, - ReadEnumerated.LongBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 0), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, false, "0A0100")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Zero, true, "C10100")] - [InlineData(PublicEncodingRules.DER, ReadEnumerated.ULongBacked.Fluff, false, "0A0900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.BER, ReadEnumerated.ULongBacked.Fluff, true, "C10900FACEF00DCAFEBEEF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ULongBacked)11, false, "0A010B")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.ULongBacked)short.MaxValue, false, "0A027FFF")] - [InlineData(PublicEncodingRules.BER, (ReadEnumerated.ULongBacked)ushort.MaxValue, true, "C10300FFFF")] - [InlineData(PublicEncodingRules.CER, (ReadEnumerated.ULongBacked)long.MaxValue, true, "C1087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, (ReadEnumerated.ULongBacked)ulong.MaxValue, false, "0A0900FFFFFFFFFFFFFFFF")] - public void VerifyWriteEnumerated_ULong( - PublicEncodingRules ruleSet, - ReadEnumerated.ULongBacked value, - bool customTag, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (customTag) - { - writer.WriteEnumeratedValue(new Asn1Tag(TagClass.Private, 1), value); - } - else - { - writer.WriteEnumeratedValue(value); - } - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyFlagsBased(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(OpenFlags.IncludeArchived)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 13), - OpenFlags.IncludeArchived)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue((object)OpenFlags.IncludeArchived)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 13), - (object)OpenFlags.IncludeArchived)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyNonEnum(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteEnumeratedValue(5)); - - Assert.Throws( - () => writer.WriteEnumeratedValue((object)"hi")); - - Assert.Throws( - () => writer.WriteEnumeratedValue((object)5)); - - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), 5)); - - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)"hi")); - - Assert.Throws( - () => writer.WriteEnumeratedValue(new Asn1Tag(TagClass.ContextSpecific, 3), (object)5)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyEndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, ReadEnumerated.IntBacked.Pillow)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.EndOfContents, (object)ReadEnumerated.IntBacked.Pillow)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_NonNull(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue(null)); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 1), - null)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_Object(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - genWriter.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.UIntBacked.Fluff); - - genWriter.WriteEnumeratedValue(ReadEnumerated.SByteBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.SByteBacked.Fluff); - - genWriter.WriteEnumeratedValue(ReadEnumerated.ULongBacked.Fluff); - objWriter.WriteEnumeratedValue((object)ReadEnumerated.ULongBacked.Fluff); - - Verify(objWriter, genWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteEnumeratedValue_Object_WithTag(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); - - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.UIntBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.UIntBacked.Fluff); - - tag = new Asn1Tag(TagClass.Private, 4); - - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.SByteBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.SByteBacked.Fluff); - - tag = new Asn1Tag(TagClass.Application, 75); - - genWriter.WriteEnumeratedValue(tag, ReadEnumerated.ULongBacked.Fluff); - objWriter.WriteEnumeratedValue(tag, (object)ReadEnumerated.ULongBacked.Fluff); - - Verify(objWriter, genWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteEnumeratedValue_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteEnumeratedValue( - new Asn1Tag(UniversalTagNumber.Enumerated, isConstructed: true), - ReadEnumerated.ULongBacked.Fluff); - - writer.WriteEnumeratedValue( - new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), - (object)ReadEnumerated.SByteBacked.Fluff); - - Verify(writer, "0A0900FACEF00DCAFEBEEF" + "800153"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue(false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue((object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue((object)null)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue(ReadEnumerated.UIntBacked.Fluff)); - - // Type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(ReadNamedBitList.LongFlags.Mid)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue((object)ReadEnumerated.SByteBacked.Fluff)); - - // Unboxed type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue((object)ReadNamedBitList.ULongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, ReadEnumerated.UIntBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, ReadNamedBitList.LongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)ReadEnumerated.SByteBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteEnumeratedValue(Asn1Tag.Integer, (object)ReadNamedBitList.ULongFlags.Mid)); - - Asn1Tag tag = new Asn1Tag(TagClass.Private, 6); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue(tag, false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteEnumeratedValue(tag, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteEnumeratedValue(tag, (object)null)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue(tag, ReadEnumerated.UIntBacked.Fluff)); - - // Type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(tag, ReadNamedBitList.LongFlags.Mid)); - - // valid input - Assert.Throws( - () => writer.WriteEnumeratedValue(tag, (object)ReadEnumerated.SByteBacked.Fluff)); - - // Unboxed type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteEnumeratedValue(tag, (object)ReadNamedBitList.ULongFlags.Mid)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs deleted file mode 100644 index 05c9b39..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteInteger.cs +++ /dev/null @@ -1,493 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteInteger : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "020100")] - [InlineData(PublicEncodingRules.CER, 0, "020100")] - [InlineData(PublicEncodingRules.DER, 0, "020100")] - [InlineData(PublicEncodingRules.BER, -1, "0201FF")] - [InlineData(PublicEncodingRules.CER, -1, "0201FF")] - [InlineData(PublicEncodingRules.DER, -1, "0201FF")] - [InlineData(PublicEncodingRules.BER, -2, "0201FE")] - [InlineData(PublicEncodingRules.DER, sbyte.MinValue, "020180")] - [InlineData(PublicEncodingRules.BER, sbyte.MinValue + 1, "020181")] - [InlineData(PublicEncodingRules.CER, sbyte.MinValue - 1, "0202FF7F")] - [InlineData(PublicEncodingRules.DER, sbyte.MinValue - 2, "0202FF7E")] - [InlineData(PublicEncodingRules.BER, -256, "0202FF00")] - [InlineData(PublicEncodingRules.CER, -257, "0202FEFF")] - [InlineData(PublicEncodingRules.DER, short.MinValue, "02028000")] - [InlineData(PublicEncodingRules.BER, short.MinValue + 1, "02028001")] - [InlineData(PublicEncodingRules.CER, short.MinValue + byte.MaxValue, "020280FF")] - [InlineData(PublicEncodingRules.DER, short.MinValue - 1, "0203FF7FFF")] - [InlineData(PublicEncodingRules.BER, short.MinValue - 2, "0203FF7FFE")] - [InlineData(PublicEncodingRules.CER, -65281, "0203FF00FF")] - [InlineData(PublicEncodingRules.DER, -8388608, "0203800000")] - [InlineData(PublicEncodingRules.BER, -8388607, "0203800001")] - [InlineData(PublicEncodingRules.CER, -8388609, "0204FF7FFFFF")] - [InlineData(PublicEncodingRules.DER, -16777216, "0204FF000000")] - [InlineData(PublicEncodingRules.BER, -16777217, "0204FEFFFFFF")] - [InlineData(PublicEncodingRules.CER, int.MinValue, "020480000000")] - [InlineData(PublicEncodingRules.DER, int.MinValue + 1, "020480000001")] - [InlineData(PublicEncodingRules.BER, (long)int.MinValue - 1, "0205FF7FFFFFFF")] - [InlineData(PublicEncodingRules.CER, (long)int.MinValue - 2, "0205FF7FFFFFFE")] - [InlineData(PublicEncodingRules.DER, -4294967296, "0205FF00000000")] - [InlineData(PublicEncodingRules.BER, -4294967295, "0205FF00000001")] - [InlineData(PublicEncodingRules.CER, -4294967294, "0205FF00000002")] - [InlineData(PublicEncodingRules.DER, -4294967297, "0205FEFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, -549755813888, "02058000000000")] - [InlineData(PublicEncodingRules.CER, -549755813887, "02058000000001")] - [InlineData(PublicEncodingRules.DER, -549755813889, "0206FF7FFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, -549755813890, "0206FF7FFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, -140737488355328, "0206800000000000")] - [InlineData(PublicEncodingRules.DER, -140737488355327, "0206800000000001")] - [InlineData(PublicEncodingRules.BER, -140737488355329, "0207FF7FFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, -281474976710656, "0207FF000000000000")] - [InlineData(PublicEncodingRules.DER, -281474976710655, "0207FF000000000001")] - [InlineData(PublicEncodingRules.BER, -281474976710657, "0207FEFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, -36028797018963968, "020780000000000000")] - [InlineData(PublicEncodingRules.DER, -36028797018963967, "020780000000000001")] - [InlineData(PublicEncodingRules.DER, -36028797018963969, "0208FF7FFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, -36028797018963970, "0208FF7FFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, -72057594037927936, "0208FF00000000000000")] - [InlineData(PublicEncodingRules.DER, -72057594037927935, "0208FF00000000000001")] - [InlineData(PublicEncodingRules.BER, -72057594037927937, "0208FEFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, long.MinValue + 1, "02088000000000000001")] - [InlineData(PublicEncodingRules.DER, long.MinValue, "02088000000000000000")] - [InlineData(PublicEncodingRules.BER, 1, "020101")] - [InlineData(PublicEncodingRules.CER, 127, "02017F")] - [InlineData(PublicEncodingRules.DER, 126, "02017E")] - [InlineData(PublicEncodingRules.BER, 128, "02020080")] - [InlineData(PublicEncodingRules.CER, 129, "02020081")] - [InlineData(PublicEncodingRules.DER, 254, "020200FE")] - [InlineData(PublicEncodingRules.BER, 255, "020200FF")] - [InlineData(PublicEncodingRules.CER, 256, "02020100")] - [InlineData(PublicEncodingRules.DER, 32767, "02027FFF")] - [InlineData(PublicEncodingRules.BER, 32766, "02027FFE")] - [InlineData(PublicEncodingRules.CER, 32768, "0203008000")] - [InlineData(PublicEncodingRules.DER, 32769, "0203008001")] - [InlineData(PublicEncodingRules.BER, 65535, "020300FFFF")] - [InlineData(PublicEncodingRules.CER, 65534, "020300FFFE")] - [InlineData(PublicEncodingRules.DER, 65536, "0203010000")] - [InlineData(PublicEncodingRules.BER, 65537, "0203010001")] - [InlineData(PublicEncodingRules.CER, 8388607, "02037FFFFF")] - [InlineData(PublicEncodingRules.DER, 8388606, "02037FFFFE")] - [InlineData(PublicEncodingRules.BER, 8388608, "020400800000")] - [InlineData(PublicEncodingRules.CER, 8388609, "020400800001")] - [InlineData(PublicEncodingRules.DER, 16777215, "020400FFFFFF")] - [InlineData(PublicEncodingRules.BER, 16777214, "020400FFFFFE")] - [InlineData(PublicEncodingRules.CER, 16777216, "020401000000")] - [InlineData(PublicEncodingRules.DER, 16777217, "020401000001")] - [InlineData(PublicEncodingRules.BER, 2147483647, "02047FFFFFFF")] - [InlineData(PublicEncodingRules.CER, 2147483646, "02047FFFFFFE")] - [InlineData(PublicEncodingRules.DER, 2147483648, "02050080000000")] - [InlineData(PublicEncodingRules.BER, 2147483649, "02050080000001")] - [InlineData(PublicEncodingRules.BER, 4294967295, "020500FFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 4294967294, "020500FFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 4294967296, "02050100000000")] - [InlineData(PublicEncodingRules.BER, 4294967297, "02050100000001")] - [InlineData(PublicEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 549755813888, "0206008000000000")] - [InlineData(PublicEncodingRules.CER, 549755813889, "0206008000000001")] - [InlineData(PublicEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 1099511627776, "0206010000000000")] - [InlineData(PublicEncodingRules.DER, 1099511627777, "0206010000000001")] - [InlineData(PublicEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 140737488355328, "020700800000000000")] - [InlineData(PublicEncodingRules.BER, 140737488355329, "020700800000000001")] - [InlineData(PublicEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 281474976710656, "020701000000000000")] - [InlineData(PublicEncodingRules.CER, 281474976710657, "020701000000000001")] - [InlineData(PublicEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 36028797018963968, "02080080000000000000")] - [InlineData(PublicEncodingRules.DER, 36028797018963969, "02080080000000000001")] - [InlineData(PublicEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 72057594037927936, "02080100000000000000")] - [InlineData(PublicEncodingRules.BER, 72057594037927937, "02080100000000000001")] - [InlineData(PublicEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_Long(PublicEncodingRules ruleSet, long value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "020100")] - [InlineData(PublicEncodingRules.CER, 0, "020100")] - [InlineData(PublicEncodingRules.DER, 0, "020100")] - [InlineData(PublicEncodingRules.BER, 1, "020101")] - [InlineData(PublicEncodingRules.CER, 127, "02017F")] - [InlineData(PublicEncodingRules.DER, 126, "02017E")] - [InlineData(PublicEncodingRules.BER, 128, "02020080")] - [InlineData(PublicEncodingRules.CER, 129, "02020081")] - [InlineData(PublicEncodingRules.DER, 254, "020200FE")] - [InlineData(PublicEncodingRules.BER, 255, "020200FF")] - [InlineData(PublicEncodingRules.CER, 256, "02020100")] - [InlineData(PublicEncodingRules.DER, 32767, "02027FFF")] - [InlineData(PublicEncodingRules.BER, 32766, "02027FFE")] - [InlineData(PublicEncodingRules.CER, 32768, "0203008000")] - [InlineData(PublicEncodingRules.DER, 32769, "0203008001")] - [InlineData(PublicEncodingRules.BER, 65535, "020300FFFF")] - [InlineData(PublicEncodingRules.CER, 65534, "020300FFFE")] - [InlineData(PublicEncodingRules.DER, 65536, "0203010000")] - [InlineData(PublicEncodingRules.BER, 65537, "0203010001")] - [InlineData(PublicEncodingRules.CER, 8388607, "02037FFFFF")] - [InlineData(PublicEncodingRules.DER, 8388606, "02037FFFFE")] - [InlineData(PublicEncodingRules.BER, 8388608, "020400800000")] - [InlineData(PublicEncodingRules.CER, 8388609, "020400800001")] - [InlineData(PublicEncodingRules.DER, 16777215, "020400FFFFFF")] - [InlineData(PublicEncodingRules.BER, 16777214, "020400FFFFFE")] - [InlineData(PublicEncodingRules.CER, 16777216, "020401000000")] - [InlineData(PublicEncodingRules.DER, 16777217, "020401000001")] - [InlineData(PublicEncodingRules.BER, 2147483647, "02047FFFFFFF")] - [InlineData(PublicEncodingRules.CER, 2147483646, "02047FFFFFFE")] - [InlineData(PublicEncodingRules.DER, 2147483648, "02050080000000")] - [InlineData(PublicEncodingRules.BER, 2147483649, "02050080000001")] - [InlineData(PublicEncodingRules.BER, 4294967295, "020500FFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 4294967294, "020500FFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 4294967296, "02050100000000")] - [InlineData(PublicEncodingRules.BER, 4294967297, "02050100000001")] - [InlineData(PublicEncodingRules.CER, 549755813887, "02057FFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 549755813886, "02057FFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 549755813888, "0206008000000000")] - [InlineData(PublicEncodingRules.CER, 549755813889, "0206008000000001")] - [InlineData(PublicEncodingRules.DER, 1099511627775, "020600FFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 1099511627774, "020600FFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 1099511627776, "0206010000000000")] - [InlineData(PublicEncodingRules.DER, 1099511627777, "0206010000000001")] - [InlineData(PublicEncodingRules.BER, 140737488355327, "02067FFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 140737488355326, "02067FFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 140737488355328, "020700800000000000")] - [InlineData(PublicEncodingRules.BER, 140737488355329, "020700800000000001")] - [InlineData(PublicEncodingRules.CER, 281474976710655, "020700FFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 281474976710654, "020700FFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 281474976710656, "020701000000000000")] - [InlineData(PublicEncodingRules.CER, 281474976710657, "020701000000000001")] - [InlineData(PublicEncodingRules.DER, 36028797018963967, "02077FFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, 36028797018963966, "02077FFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.CER, 36028797018963968, "02080080000000000000")] - [InlineData(PublicEncodingRules.DER, 36028797018963969, "02080080000000000001")] - [InlineData(PublicEncodingRules.BER, 72057594037927935, "020800FFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.CER, 72057594037927934, "020800FFFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.DER, 72057594037927936, "02080100000000000000")] - [InlineData(PublicEncodingRules.BER, 72057594037927937, "02080100000000000001")] - [InlineData(PublicEncodingRules.CER, 9223372036854775807, "02087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "02087FFFFFFFFFFFFFFE")] - [InlineData(PublicEncodingRules.BER, 9223372036854775808, "0209008000000000000000")] - [InlineData(PublicEncodingRules.CER, 9223372036854775809, "0209008000000000000001")] - [InlineData(PublicEncodingRules.DER, ulong.MaxValue, "020900FFFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, ulong.MaxValue-1, "020900FFFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_ULong(PublicEncodingRules ruleSet, ulong value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0", "020100")] - [InlineData(PublicEncodingRules.CER, "127", "02017F")] - [InlineData(PublicEncodingRules.DER, "128", "02020080")] - [InlineData(PublicEncodingRules.BER, "32767", "02027FFF")] - [InlineData(PublicEncodingRules.CER, "32768", "0203008000")] - [InlineData(PublicEncodingRules.DER, "9223372036854775807", "02087FFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.BER, "9223372036854775808", "0209008000000000000000")] - [InlineData(PublicEncodingRules.CER, "18446744073709551615", "020900FFFFFFFFFFFFFFFF")] - [InlineData(PublicEncodingRules.DER, "18446744073709551616", "0209010000000000000000")] - [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "02100102030405060708090A0B0C0D0E0F00")] - [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "021100F0E0D0C0B0A090807060504030201000")] - [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "0210FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_BigInteger(PublicEncodingRules ruleSet, string decimalValue, string expectedHex) - { - BigInteger value = BigInteger.Parse(decimalValue); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "470100")] - [InlineData(PublicEncodingRules.CER, long.MinValue + 1, "47088000000000000001")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "47087FFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_Application7_Long(PublicEncodingRules ruleSet, long value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.Application, 7), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, "890100")] - [InlineData(PublicEncodingRules.CER, 9223372036854775809, "8909008000000000000001")] - [InlineData(PublicEncodingRules.DER, 9223372036854775806, "89087FFFFFFFFFFFFFFE")] - public void VerifyWriteInteger_Context9_ULong(PublicEncodingRules ruleSet, ulong value, string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 9), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, "0", "D00100")] - [InlineData(PublicEncodingRules.BER, "1339673755198158349044581307228491520", "D0100102030405060708090A0B0C0D0E0F00")] - [InlineData(PublicEncodingRules.CER, "320182027492359845421654932427609477120", "D01100F0E0D0C0B0A090807060504030201000")] - [InlineData(PublicEncodingRules.DER, "-1339673755198158349044581307228491520", "D010FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_Private16_BigInteger( - PublicEncodingRules ruleSet, - string decimalValue, - string expectedHex) - { - BigInteger value = BigInteger.Parse(decimalValue); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(TagClass.Private, 16), value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData("00")] - [InlineData("01")] - [InlineData("80")] - [InlineData("FF")] - [InlineData("0080")] - [InlineData("00FF")] - [InlineData("8000")] - [InlineData("00F0E0D0C0B0A090807060504030201000")] - [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_EncodedBytes(string valueHex) - { - string expectedHex = "02" + (valueHex.Length / 2).ToString("X2") + valueHex; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteInteger(valueHex.HexToByteArray()); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData("00")] - [InlineData("01")] - [InlineData("80")] - [InlineData("FF")] - [InlineData("0080")] - [InlineData("00FF")] - [InlineData("8000")] - [InlineData("00F0E0D0C0B0A090807060504030201000")] - [InlineData("FEFDFCFBFAF9F8F7F6F5F4F3F2F1F100")] - public void VerifyWriteInteger_Context4_EncodedBytes(string valueHex) - { - string expectedHex = "84" + (valueHex.Length / 2).ToString("X2") + valueHex; - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 4), valueHex.HexToByteArray()); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData("")] - [InlineData("0000")] - [InlineData("0000000000000000000001")] - [InlineData("0001")] - [InlineData("007F")] - [InlineData("FFFF")] - [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] - [InlineData("FF80")] - public void VerifyWriteInteger_InvalidEncodedValue_Throws(string valuHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Assert.ThrowsAny(() => writer.WriteInteger(valuHex.HexToByteArray())); - } - } - - [Theory] - [InlineData("")] - [InlineData("0000")] - [InlineData("0000000000000000000001")] - [InlineData("0001")] - [InlineData("007F")] - [InlineData("FFFF")] - [InlineData("FFFFFFFFFFFFFFFFFFFFFE")] - [InlineData("FF80")] - public void VerifyWriteInteger_Application3_InvalidEncodedValue_Throws(string valuHex) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); - - Assert.ThrowsAny( - () => writer.WriteInteger(tag, valuHex.HexToByteArray())); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteInteger_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.EndOfContents, 0L)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.EndOfContents, 0UL)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.EndOfContents, BigInteger.Zero)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteInteger_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), 0L); - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), 0L); - writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), 0UL); - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), 0UL); - writer.WriteInteger(new Asn1Tag(UniversalTagNumber.Integer, isConstructed: true), BigInteger.Zero); - writer.WriteInteger(new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true), BigInteger.Zero); - - Verify(writer, "020100800100020100800100020100800100"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteInteger(1)); - - Assert.Throws( - () => writer.WriteInteger(1UL)); - - Assert.Throws( - () => writer.WriteInteger(BigInteger.One)); - - Assert.Throws( - () => writer.WriteInteger(BigInteger.One.ToByteArray())); - - Assert.Throws( - () => writer.WriteInteger(Array.Empty())); - - Assert.Throws( - () => writer.WriteInteger(new byte[] { 0, 0 })); - - Assert.Throws( - () => writer.WriteInteger(new byte[] { 0xFF, 0xFF })); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, 1)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, 1UL)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, BigInteger.One)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, BigInteger.One.ToByteArray())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, Array.Empty())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, new byte[] { 0, 0 })); - - AssertExtensions.Throws( - "tag", - () => writer.WriteInteger(Asn1Tag.Boolean, new byte[] { 0xFF, 0xFF })); - - Asn1Tag tag = new Asn1Tag(TagClass.Application, 0); - - Assert.Throws( - () => writer.WriteInteger(tag, 1)); - - Assert.Throws( - () => writer.WriteInteger(tag, 1UL)); - - Assert.Throws( - () => writer.WriteInteger(tag, BigInteger.One)); - - Assert.Throws( - () => writer.WriteInteger(tag, BigInteger.One.ToByteArray())); - - Assert.Throws( - () => writer.WriteInteger(tag, Array.Empty())); - - Assert.Throws( - () => writer.WriteInteger(tag, new byte[] { 0, 0 })); - - Assert.Throws( - () => writer.WriteInteger(tag, new byte[] { 0xFF, 0xFF })); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs deleted file mode 100644 index f4de4f5..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNamedBitList.cs +++ /dev/null @@ -1,347 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Test.Cryptography; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteNamedBitList : Asn1WriterTests - { - [Theory] - [InlineData( - PublicEncodingRules.BER, - "030100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.CER, - "030100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.DER, - "030100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.BER, - "0309000000000000000003", - ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] - [InlineData( - PublicEncodingRules.CER, - "0309010000000080000002", - ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] - [InlineData( - PublicEncodingRules.DER, - "030204B0", - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] - public static void VerifyWriteNamedBitList( - PublicEncodingRules ruleSet, - string expectedHex, - object value) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNamedBitList(value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData( - PublicEncodingRules.BER, - "C00100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.CER, - "410100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.DER, - "820100", - ReadNamedBitList.ULongFlags.None)] - [InlineData( - PublicEncodingRules.BER, - "C009000000000000000003", - ReadNamedBitList.ULongFlags.Max | ReadNamedBitList.ULongFlags.AlmostMax)] - [InlineData( - PublicEncodingRules.CER, - "4109010000000080000002", - ReadNamedBitList.LongFlags.Max | ReadNamedBitList.LongFlags.Mid)] - [InlineData( - PublicEncodingRules.DER, - "820204B0", - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment)] - public static void VerifyWriteNamedBitList_WithTag( - PublicEncodingRules ruleSet, - string expectedHex, - object value) - { - int ruleSetVal = (int)ruleSet; - TagClass tagClass = (TagClass)(byte)(ruleSetVal << 6); - - if (tagClass == TagClass.Universal) - tagClass = TagClass.Private; - - Asn1Tag tag = new Asn1Tag(tagClass, ruleSetVal); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNamedBitList(tag, value); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_Generic(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - var flagsValue = - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; - - genWriter.WriteNamedBitList(flagsValue); - objWriter.WriteNamedBitList((object)flagsValue); - - Verify(genWriter, objWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_Generic_WithTag(PublicEncodingRules ruleSet) - { - using (AsnWriter objWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - using (AsnWriter genWriter = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 52); - - var flagsValue = - ReadNamedBitList.X509KeyUsageCSharpStyle.DigitalSignature | - ReadNamedBitList.X509KeyUsageCSharpStyle.KeyEncipherment | - ReadNamedBitList.X509KeyUsageCSharpStyle.DataEncipherment; - - genWriter.WriteNamedBitList(tag, flagsValue); - objWriter.WriteNamedBitList(tag, (object)flagsValue); - - Verify(genWriter, objWriter.Encode().ByteArrayToHex()); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_NonNull(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(null)); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), null)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_EnumRequired(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteNamedBitList(3)); - - Assert.Throws( - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), 3)); - - Assert.Throws( - () => writer.WriteNamedBitList((object)3)); - - Assert.Throws( - () => writer.WriteNamedBitList(new Asn1Tag(TagClass.ContextSpecific, 1), (object)3)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_FlagsEnumRequired(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(AsnEncodingRules.BER)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList( - new Asn1Tag(TagClass.ContextSpecific, 1), - AsnEncodingRules.BER)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList((object)AsnEncodingRules.BER)); - - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList( - new Asn1Tag(TagClass.ContextSpecific, 1), - (object)AsnEncodingRules.BER)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public static void VerifyWriteNamedBitList_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList( - Asn1Tag.EndOfContents, - StringSplitOptions.RemoveEmptyEntries)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList( - Asn1Tag.EndOfContents, - (object)StringSplitOptions.RemoveEmptyEntries)); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList(false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList((object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList((object)null)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList(ReadNamedBitList.LongFlags.Mid)); - - // Type is not [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(ReadEnumerated.UIntBacked.Fluff)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList((object)ReadNamedBitList.ULongFlags.Mid)); - - // Unboxed type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList((object)ReadEnumerated.SByteBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, ReadNamedBitList.LongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, ReadEnumerated.UIntBacked.Fluff)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)ReadNamedBitList.ULongFlags.Mid)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNamedBitList(Asn1Tag.Integer, (object)ReadEnumerated.SByteBacked.Fluff)); - - Asn1Tag tag = new Asn1Tag(TagClass.Private, 6); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList(tag, false)); - - // Type not enum - AssertExtensions.Throws( - "enumType", - () => writer.WriteNamedBitList(tag, (object)"hi")); - - AssertExtensions.Throws( - "enumValue", - () => writer.WriteNamedBitList(tag, (object)null)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList(tag, ReadNamedBitList.LongFlags.Mid)); - - // Type is [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(tag, ReadEnumerated.UIntBacked.Fluff)); - - // valid input - Assert.Throws( - () => writer.WriteNamedBitList(tag, (object)ReadNamedBitList.ULongFlags.Mid)); - - // Unboxed type is not [Flags] - AssertExtensions.Throws( - "tEnum", - () => writer.WriteNamedBitList(tag, (object)ReadEnumerated.SByteBacked.Fluff)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs deleted file mode 100644 index f6d2cc0..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteNull.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteNull : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNull(); - - Verify(writer, "0500"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteNull(Asn1Tag.EndOfContents)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.DER)] - [InlineData(PublicEncodingRules.CER)] - public void VerifyWriteNull_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteNull(new Asn1Tag(TagClass.ContextSpecific, 7, true)); - writer.WriteNull(new Asn1Tag(UniversalTagNumber.Null, true)); - - Verify(writer, "87000500"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteBoolean(true); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteNull()); - - AssertExtensions.Throws( - "tag", - () => writer.WriteNull(Asn1Tag.Integer)); - - Assert.Throws( - () => writer.WriteNull(new Asn1Tag(TagClass.Private, 3))); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs deleted file mode 100644 index e488209..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteObjectIdentifier.cs +++ /dev/null @@ -1,533 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteObjectIdentifier : Asn1WriterTests - { - [Theory] - [MemberData(nameof(ValidOidData))] - public void VerifyWriteObjectIdentifier_String( - PublicEncodingRules ruleSet, - string oidValue, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidValue); - - Verify(writer, expectedHex); - } - } - - [Theory] - [MemberData(nameof(ValidOidData))] - public void VerifyWriteObjectIdentifier_Span( - PublicEncodingRules ruleSet, - string oidValue, - string expectedHex) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidValue.AsSpan()); - - Verify(writer, expectedHex); - } - } - - [Theory] - [MemberData(nameof(ValidOidData))] - public void VerifyWriteObjectIdentifier_Oid( - PublicEncodingRules ruleSet, - string oidValue, - string expectedHex) - { - Oid oidObj = new Oid(oidValue, "FriendlyName does not matter"); - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(oidObj); - - Verify(writer, expectedHex); - } - } - - [Theory] - [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_String( - string description, - PublicEncodingRules ruleSet, - string nonOidValue) - { - _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - if (nonOidValue == null) - { - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(nonOidValue)); - } - else - { - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidValue)); - } - } - } - - [Theory] - [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_Span( - string description, - PublicEncodingRules ruleSet, - string nonOidValue) - { - _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidValue.AsSpan())); - } - } - - [Theory] - [MemberData(nameof(InvalidOidData))] - public void VerifyWriteOid_InvalidValue_Oid( - string description, - PublicEncodingRules ruleSet, - string nonOidValue) - { - _ = description; - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - Oid nonOidObj = new Oid(nonOidValue, "FriendlyName does not matter"); - - Assert.Throws( - () => writer.WriteObjectIdentifier(nonOidObj)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_String(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 3), "1.3.14.3.2.26"); - - Verify(writer, "83052B0E03021A"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_Span(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), "1.3.14.3.2.26".AsSpan()); - - Verify(writer, "42052B0E03021A"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteObjectIdentifier_CustomTag_Oid(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteObjectIdentifier( - new Asn1Tag(TagClass.Private, 36), - Oid.FromFriendlyName("SHA1", OidGroup.HashAlgorithm)); - - Verify(writer, "DF24052B0E03021A"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteObjectIdentifier_NullString(bool defaultTag) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - AssertExtensions.Throws( - "oidValue", - () => - { - if (defaultTag) - { - writer.WriteObjectIdentifier((string)null); - } - else - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 6), (string)null); - } - }); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteObjectIdentifier_NullOid(bool defaultTag) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - AssertExtensions.Throws( - "oid", - () => - { - if (defaultTag) - { - writer.WriteObjectIdentifier((Oid)null); - } - else - { - writer.WriteObjectIdentifier(new Asn1Tag(TagClass.Application, 2), (Oid)null); - } - }); - } - } - - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteObjectIdentifier_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, "1.1")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, "1.1".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.EndOfContents, new Oid("1.1", "1.1"))); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteObjectIdentifier_ConstructedIgnored(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - const string OidValue = "1.1"; - Asn1Tag constructedOid = new Asn1Tag(UniversalTagNumber.ObjectIdentifier, isConstructed: true); - Asn1Tag constructedContext0 = new Asn1Tag(TagClass.ContextSpecific, 0, isConstructed: true); - - writer.WriteObjectIdentifier(constructedOid, OidValue); - writer.WriteObjectIdentifier(constructedContext0, OidValue); - writer.WriteObjectIdentifier(constructedOid, OidValue.AsSpan()); - writer.WriteObjectIdentifier(constructedContext0, OidValue.AsSpan()); - writer.WriteObjectIdentifier(constructedOid, new Oid(OidValue, OidValue)); - writer.WriteObjectIdentifier(constructedContext0, new Oid(OidValue, OidValue)); - - Verify(writer, "060129800129060129800129060129800129"); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0q")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("123")); - - Assert.Throws( - () => writer.WriteObjectIdentifier("4.0")); - - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier((string)null)); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("0.0q".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("123".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier("4.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("0.0", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("0", "short"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("0.0q", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("123", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid("4.0", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(new Oid(null, "null"))); - - AssertExtensions.Throws( - "oid", - () => writer.WriteObjectIdentifier((Oid)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteBoolean(Asn1Tag.Integer, false)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0q")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "123")); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "4.0")); - - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, (string)null)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "0.0q".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "123".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, "4.0".AsSpan())); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("0.0", "valid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("0", "short"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("0.0q", "invalid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("123", "invalid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid("4.0", "invalid"))); - - AssertExtensions.Throws( - "tag", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, new Oid(null, "null"))); - - AssertExtensions.Throws( - "oid", - () => writer.WriteObjectIdentifier(Asn1Tag.Integer, (Oid)null)); - - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 123); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0q")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "123")); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "4.0")); - - AssertExtensions.Throws( - "oidValue", - () => writer.WriteObjectIdentifier(tag, (string)null)); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "0.0q".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "123".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, "4.0".AsSpan())); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("0.0", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("0", "short"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("0.0q", "invalid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("123", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid("4.0", "valid"))); - - Assert.Throws( - () => writer.WriteObjectIdentifier(tag, new Oid(null, "null"))); - - AssertExtensions.Throws( - "oid", - () => writer.WriteObjectIdentifier(tag, (Oid)null)); - } - } - - public static IEnumerable ValidOidData { get; } = - new object[][] - { - new object[] - { - PublicEncodingRules.BER, - "0.0", - "060100", - }, - new object[] - { - PublicEncodingRules.CER, - "1.0", - "060128", - }, - new object[] - { - PublicEncodingRules.DER, - "2.0", - "060150", - }, - new object[] - { - PublicEncodingRules.BER, - "1.3.14.3.2.26", - "06052B0E03021A", - }, - new object[] - { - PublicEncodingRules.CER, - "2.999.19427512891.25", - "06088837C8AFE1A43B19", - }, - new object[] - { - PublicEncodingRules.DER, - "1.2.840.113549.1.1.10", - "06092A864886F70D01010A", - }, - new object[] - { - // Using the rules of ITU-T-REC-X.667-201210 for 2.25.{UUID} unregistered arcs, and - // their sample value of f81d4fae-7dec-11d0-a765-00a0c91e6bf6 - // this is - // { joint-iso-itu-t(2) uuid(255) thatuuid(329800735698586629295641978511506172918) three(3) } - PublicEncodingRules.DER, - "2.25.329800735698586629295641978511506172918.3", - "06156983F09DA7EBCFDEE0C7A1A7B2C0948CC8F9D77603", - }, - }; - - public static IEnumerable InvalidOidData { get; } = - new object[][] - { - new object[] { "Null", PublicEncodingRules.BER, null }, - new object[] { "Empty string", PublicEncodingRules.BER, "" }, - new object[] { "No period", PublicEncodingRules.CER, "1" }, - new object[] { "No second RID", PublicEncodingRules.DER, "1." }, - new object[] { "Invalid first RID", PublicEncodingRules.BER, "3.0" }, - new object[] { "Invalid first RID - multichar", PublicEncodingRules.CER, "27.0" }, - new object[] { "Double zero - First RID", PublicEncodingRules.DER, "00.0" }, - new object[] { "Leading zero - First RID", PublicEncodingRules.BER, "01.0" }, - new object[] { "Double zero - second RID", PublicEncodingRules.CER, "0.00" }, - new object[] { "Leading zero - second RID", PublicEncodingRules.DER, "0.01" }, - new object[] { "Double-period", PublicEncodingRules.BER, "0..0" }, - new object[] { "Ends with period - second RID", PublicEncodingRules.BER, "0.0." }, - new object[] { "Ends with period - third RID", PublicEncodingRules.BER, "0.1.30." }, - new object[] { "Double zero - third RID", PublicEncodingRules.CER, "0.1.00" }, - new object[] { "Leading zero - third RID", PublicEncodingRules.DER, "0.1.023" }, - new object[] { "Invalid character first position", PublicEncodingRules.BER, "a.1.23" }, - new object[] { "Invalid character second position", PublicEncodingRules.CER, "0,1.23" }, - new object[] { "Invalid character second rid", PublicEncodingRules.DER, "0.1q.23" }, - new object[] { "Invalid character third rid", PublicEncodingRules.BER, "0.1.23q" }, - }; - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs deleted file mode 100644 index f0c7f98..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteOctetString.cs +++ /dev/null @@ -1,193 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteOctetString : Asn1WriterTests - { - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void WriteEmpty(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(ReadOnlySpan.Empty); - - Verify(writer, "0400"); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 1, "0401")] - [InlineData(PublicEncodingRules.CER, 2, "0402")] - [InlineData(PublicEncodingRules.DER, 3, "0403")] - [InlineData(PublicEncodingRules.BER, 126, "047E")] - [InlineData(PublicEncodingRules.CER, 127, "047F")] - [InlineData(PublicEncodingRules.DER, 128, "048180")] - [InlineData(PublicEncodingRules.BER, 1000, "048203E8")] - [InlineData(PublicEncodingRules.CER, 1000, "048203E8")] - [InlineData(PublicEncodingRules.DER, 1000, "048203E8")] - [InlineData(PublicEncodingRules.BER, 1001, "048203E9")] - [InlineData(PublicEncodingRules.DER, 1001, "048203E9")] - [InlineData(PublicEncodingRules.BER, 2001, "048207D1")] - [InlineData(PublicEncodingRules.DER, 2001, "048207D1")] - public void WritePrimitive(PublicEncodingRules ruleSet, int length, string hexStart) - { - string payloadHex = new string('0', 2 * length); - string expectedHex = hexStart + payloadHex; - byte[] data = new byte[length]; - - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(data); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(1001, "2480048203E8", "0401")] - [InlineData(1999, "2480048203E8", "048203E7")] - [InlineData(2000, "2480048203E8", "048203E8")] - public void WriteSegmentedCER(int length, string hexStart, string hexStart2) - { - string payload1Hex = new string('8', 2000); - string payload2Hex = new string('8', (length - 1000) * 2); - string expectedHex = hexStart + payload1Hex + hexStart2 + payload2Hex + "0000"; - byte[] data = new byte[length]; - - for (int i = 0; i < data.Length; i++) - { - data[i] = 0x88; - } - - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteOctetString(data); - - Verify(writer, expectedHex); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER, 0, false)] - [InlineData(PublicEncodingRules.CER, 0, false)] - [InlineData(PublicEncodingRules.DER, 0, false)] - [InlineData(PublicEncodingRules.BER, 999, false)] - [InlineData(PublicEncodingRules.CER, 999, false)] - [InlineData(PublicEncodingRules.DER, 999, false)] - [InlineData(PublicEncodingRules.BER, 1000, false)] - [InlineData(PublicEncodingRules.CER, 1000, false)] - [InlineData(PublicEncodingRules.DER, 1000, false)] - [InlineData(PublicEncodingRules.BER, 1001, false)] - [InlineData(PublicEncodingRules.CER, 1001, true)] - [InlineData(PublicEncodingRules.DER, 1001, false)] - [InlineData(PublicEncodingRules.BER, 1998, false)] - [InlineData(PublicEncodingRules.CER, 1998, true)] - [InlineData(PublicEncodingRules.DER, 1998, false)] - [InlineData(PublicEncodingRules.BER, 1999, false)] - [InlineData(PublicEncodingRules.CER, 1999, true)] - [InlineData(PublicEncodingRules.DER, 1999, false)] - [InlineData(PublicEncodingRules.BER, 2000, false)] - [InlineData(PublicEncodingRules.CER, 2000, true)] - [InlineData(PublicEncodingRules.DER, 2000, false)] - [InlineData(PublicEncodingRules.BER, 2001, false)] - [InlineData(PublicEncodingRules.CER, 2001, true)] - [InlineData(PublicEncodingRules.DER, 2001, false)] - [InlineData(PublicEncodingRules.BER, 4096, false)] - [InlineData(PublicEncodingRules.CER, 4096, true)] - [InlineData(PublicEncodingRules.DER, 4096, false)] - public void VerifyWriteOctetString_PrimitiveOrConstructed( - PublicEncodingRules ruleSet, - int payloadLength, - bool expectConstructed) - { - byte[] data = new byte[payloadLength]; - - Asn1Tag[] tagsToTry = - { - new Asn1Tag(UniversalTagNumber.OctetString), - new Asn1Tag(UniversalTagNumber.OctetString, isConstructed: true), - new Asn1Tag(TagClass.Private, 87), - new Asn1Tag(TagClass.ContextSpecific, 13, isConstructed: true), - }; - - byte[] answerBuf = new byte[payloadLength + 100]; - - foreach (Asn1Tag toTry in tagsToTry) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - writer.WriteOctetString(toTry, data); - - Assert.True(writer.TryEncode(answerBuf, out _)); - } - Assert.True(Asn1Tag.TryDecode(answerBuf, out Asn1Tag writtenTag, out _)); - - if (expectConstructed) - { - Assert.True(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - else - { - Assert.False(writtenTag.IsConstructed, $"writtenTag.IsConstructed ({toTry})"); - } - - Assert.Equal(toTry.TagClass, writtenTag.TagClass); - Assert.Equal(toTry.TagValue, writtenTag.TagValue); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteOctetString_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.EndOfContents, ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.EndOfContents, new byte[1])); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteOctetString(ReadOnlySpan.Empty)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteOctetString(Asn1Tag.Integer, ReadOnlySpan.Empty)); - - Assert.Throws( - () => writer.WriteOctetString( - new Asn1Tag(TagClass.Private, 3), - ReadOnlySpan.Empty)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs b/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs deleted file mode 100644 index 75e4281..0000000 --- a/src/libraries/System.Security.Cryptography.Encoding/tests/Asn1/Writer/WriteUtcTime.cs +++ /dev/null @@ -1,273 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Security.Cryptography.Asn1; -using Xunit; - -namespace System.Security.Cryptography.Tests.Asn1 -{ - public class WriteUtcTime : Asn1WriterTests - { - public static IEnumerable TestCases { get; } = new object[][] - { - new object[] - { - new DateTimeOffset(2017, 10, 16, 8, 24, 3, TimeSpan.FromHours(-7)), - "0D3137313031363135323430335A", - }, - new object[] - { - new DateTimeOffset(1817, 10, 16, 21, 24, 3, TimeSpan.FromHours(6)), - "0D3137313031363135323430335A", - }, - new object[] - { - new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero), - "0D3030303130313030303030305A", - }, - }; - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_BER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - writer.WriteUtcTime(input); - - Verify(writer, "17" + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_BER_CustomTag(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Application, 11); - writer.WriteUtcTime(tag, input); - - Verify(writer, Stringify(tag) + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_CER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - writer.WriteUtcTime(input); - - Verify(writer, "17" + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_CER_CustomTag(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 95); - writer.WriteUtcTime(tag, input); - - Verify(writer, Stringify(tag) + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_DER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.WriteUtcTime(input); - - Verify(writer, "17" + expectedHexPayload); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_DER_CustomTag(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 3); - writer.WriteUtcTime(tag, input); - - Verify(writer, Stringify(tag) + expectedHexPayload); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteUtcTime_EndOfContents(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.EndOfContents, DateTimeOffset.Now)); - } - } - - [Theory] - [InlineData(PublicEncodingRules.BER)] - [InlineData(PublicEncodingRules.CER)] - [InlineData(PublicEncodingRules.DER)] - public void VerifyWriteUtcTime_IgnoresConstructed(PublicEncodingRules ruleSet) - { - using (AsnWriter writer = new AsnWriter((AsnEncodingRules)ruleSet)) - { - DateTimeOffset value = new DateTimeOffset(2017, 11, 16, 17, 35, 1, TimeSpan.Zero); - - writer.WriteUtcTime(new Asn1Tag(UniversalTagNumber.UtcTime, true), value); - writer.WriteUtcTime(new Asn1Tag(TagClass.ContextSpecific, 3, true), value); - Verify(writer, "170D3137313131363137333530315A" + "830D3137313131363137333530315A"); - } - } - - [Theory] - [MemberData(nameof(TestCases))] - public void VerifyWriteUtcTime_RespectsYearMax_DER(DateTimeOffset input, string expectedHexPayload) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - Assert.Equal(0, writer.GetEncodedLength()); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(input, input.Year - 1)); - - Assert.Equal(0, writer.GetEncodedLength()); - - writer.WriteUtcTime(input, input.Year); - Assert.Equal(15, writer.GetEncodedLength()); - - writer.WriteUtcTime(input, input.Year + 99); - Assert.Equal(30, writer.GetEncodedLength()); - - writer.Reset(); - - _ = expectedHexPayload; - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(input, input.Year + 100)); - - Assert.Equal(0, writer.GetEncodedLength()); - } - } - - [Fact] - public void VerifyWriteUtcTime_RespectsYearMax_UniversalLimit() - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.CER)) - { - Asn1Tag tag = new Asn1Tag(TagClass.Private, 11); - - // 1950 afte ToUniversal - writer.WriteUtcTime( - tag, - new DateTimeOffset(1949, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), - 2049); - - Assert.Equal(15, writer.GetEncodedLength()); - - // 1949 after ToUniversal - AssertExtensions.Throws( - "value", - () => - writer.WriteUtcTime( - tag, - new DateTimeOffset(1950, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), - 2049)); - - Assert.Equal(15, writer.GetEncodedLength()); - - // 2050 after ToUniversal - AssertExtensions.Throws( - "value", - () => - writer.WriteUtcTime( - tag, - new DateTimeOffset(2049, 12, 31, 23, 11, 19, TimeSpan.FromHours(-8)), - 2049)); - - Assert.Equal(15, writer.GetEncodedLength()); - - // 1950 afte ToUniversal - writer.WriteUtcTime( - tag, - new DateTimeOffset(2050, 1, 1, 3, 11, 19, TimeSpan.FromHours(8)), - 2049); - - Assert.Equal(30, writer.GetEncodedLength()); - - string hex = - Stringify(tag) + "0D3530303130313037313131395A" + - Stringify(tag) + "0D3439313233313139313131395A"; - - Verify(writer, hex); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void WriteAfterDispose(bool empty) - { - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) - { - if (!empty) - { - writer.WriteNull(); - } - - writer.Dispose(); - - Assert.Throws( - () => writer.WriteUtcTime(DateTimeOffset.Now)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(DateTimeOffset.Now, 1999)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(DateTimeOffset.Now, 8999)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.Integer, DateTimeOffset.Now)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.Integer, DateTimeOffset.Now, 1999)); - - AssertExtensions.Throws( - "tag", - () => writer.WriteUtcTime(Asn1Tag.Integer, DateTimeOffset.Now, 8999)); - - Asn1Tag tag = new Asn1Tag(TagClass.Application, 3); - - Assert.Throws( - () => writer.WriteUtcTime(tag, DateTimeOffset.Now)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(tag, DateTimeOffset.Now, 1999)); - - AssertExtensions.Throws( - "value", - () => writer.WriteUtcTime(tag, DateTimeOffset.Now, 8999)); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj b/src/libraries/System.Security.Cryptography.Encoding/tests/System.Security.Cryptography.Encoding.Tests.csproj index e89c8cf..8754e92 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 03996c4..728cc5d 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 64329c0..ba13968 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 @@ namespace Internal.Cryptography.Pal.AnyOS int keyLength; byte[] parameters = Array.Empty(); - switch (asn.Algorithm.Value) + switch (asn.Algorithm) { case Oids.Rc2Cbc: { @@ -97,27 +98,35 @@ namespace Internal.Cryptography.Pal.AnyOS } 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 e39d35f..9bc4d24 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 dea5107..63f2b1e 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 58ec6b7..79c5e08 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 @@ namespace Internal.Cryptography.Pal.AnyOS // 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 4691380..002a48c 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 @@ namespace Internal.Cryptography.Pal.AnyOS ContentEncryptionAlgorithm = { - Algorithm = contentEncryptionAlgorithm.Oid, + Algorithm = contentEncryptionAlgorithm.Oid.Value!, Parameters = parameterBytes, }, @@ -141,11 +142,9 @@ namespace Internal.Cryptography.Pal.AnyOS 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 @@ namespace Internal.Cryptography.Pal.AnyOS { 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 @@ namespace Internal.Cryptography.Pal.AnyOS { 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 e7684f7..455f326 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 @@ namespace Internal.Cryptography.Pal.AnyOS default: exception = new CryptographicException( SR.Cryptography_Cms_UnknownAlgorithm, - _asn.KeyEncryptionAlgorithm.Algorithm.Value); + _asn.KeyEncryptionAlgorithm.Algorithm); return null; } @@ -141,27 +141,27 @@ namespace Internal.Cryptography.Pal.AnyOS 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 3386f88..4d475a4 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 @@ namespace Internal.Cryptography.Pal.AnyOS 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 cf4c423..7397177 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 @@ namespace Internal.Cryptography.Pal.Windows { 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 539dd52..e6e66a8 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 FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; using Microsoft.Win32.SafeHandles; using static Interop.Crypt32; -using System.Security.Cryptography.Asn1; namespace Internal.Cryptography.Pal.Windows { @@ -69,21 +68,37 @@ namespace Internal.Cryptography.Pal.Windows 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 9e59207..35ec020 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 @@ namespace Internal.Cryptography { 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 @@ namespace Internal.Cryptography 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 @@ namespace Internal.Cryptography } } - 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 @@ namespace Internal.Cryptography } // 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 @@ namespace Internal.Cryptography { 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 @@ namespace Internal.Cryptography 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 @@ namespace Internal.Cryptography return false; } - switch (oaepParameters.HashFunc.Algorithm.Value) + switch (oaepParameters.HashFunc.Algorithm) { case Oids.Sha1: rsaEncryptionPadding = RSAEncryptionPadding.OaepSHA1; @@ -645,7 +680,7 @@ namespace Internal.Cryptography 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 d6fc49b..dea5805 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 fc9cf83..4a889d6 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -59,6 +65,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; 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 073aac1..99b1889 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 } } - 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,15 +69,34 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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(); ReadOnlySpan rebindSpan = rebind.Span; 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 e80e5dd..c4bc5e6 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -58,6 +78,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader explicitReader; @@ -65,7 +97,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 dfcaffa..acdbc65 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -82,6 +88,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; 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 d5e2095..538ff8b 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -56,6 +62,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -63,7 +81,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 df54bc6..d94f24c 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 // 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -87,6 +90,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader defaultReader; @@ -106,7 +121,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 } - 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 3077daa..446d9e0 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -60,6 +73,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 cc5df62..4fca26a 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,15 +64,34 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 012c59d..11691c9 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -74,6 +80,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader explicitReader; @@ -98,7 +116,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { 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 94bffbf..b6ced3d 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -55,6 +61,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -70,7 +88,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 1907c3b..d31418b 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -59,7 +77,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 b9a72cc..3d42533 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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,15 +75,34 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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(); ReadOnlySpan rebindSpan = rebind.Span; @@ -101,7 +119,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 009aef4..643246b 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -76,6 +89,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; 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 0448009..6311bf2 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 writer.PushSequence(tag); Algorithm.Encode(writer); - writer.WriteBitString(PublicKey.Span); + writer.WriteBitString(PublicKey.Span, 0); writer.PopSequence(tag); } @@ -37,11 +36,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -59,7 +77,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 b33506c..c02ffbb 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -56,13 +76,25 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 ff2b70e..ccddd87 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -72,6 +85,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 2836c2a..a48ea27 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -64,11 +77,23 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 a1cfc84..4a2dfa0 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -51,13 +71,25 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 a68a568..c7e229d 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -59,7 +77,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 2523671..21c2d31 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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,15 +64,34 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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(); ReadOnlySpan rebindSpan = rebind.Span; @@ -90,7 +108,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 4290612..0f8b918 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,15 +64,34 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 b32bd30..27ed1e2 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 if (Date.HasValue) { - writer.WriteGeneralizedTime(Date.Value); + writer.WriteGeneralizedTime(Date.Value, false); } @@ -49,11 +48,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -63,6 +69,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -70,7 +88,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 10924f9..03b7e5c 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -68,6 +74,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 704fcc6..0097e7e 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 // 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -110,6 +120,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader defaultReader; 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 e7b85af..d8239c7 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -56,6 +69,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 49a96ec..73609bf 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 // 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -125,6 +135,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader explicitReader; 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 ca4374f..db84eb1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -53,6 +73,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader explicitReader; @@ -60,7 +92,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 7c81d8c..7ba35d2 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,15 +61,34 @@ namespace System.Security.Cryptography.Pkcs 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(); AsnValueReader collectionReader; 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 1af67b0..32b82b3 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -98,6 +111,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; 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 5cb956e..23dd4e8 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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,15 +64,34 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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(); ReadOnlySpan rebindSpan = rebind.Span; @@ -90,7 +108,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 dc22672..8b209b2 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 } } - 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -88,6 +101,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; @@ -112,7 +137,7 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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 52a06d9..54a4bcd 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -71,6 +77,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; 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 6556faf..7201b40 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 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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) @@ -71,6 +77,18 @@ namespace System.Security.Cryptography.Pkcs.Asn1 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); AsnValueReader collectionReader; 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 247ed26..03e37f9 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 2ae38f7..f32495d 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 @@ namespace System.Security.Cryptography.Pkcs return !sequence.HasData; } + catch (AsnContentException) + { + return false; + } catch (CryptographicException) { return false; @@ -163,7 +167,7 @@ namespace System.Security.Cryptography.Pkcs 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 0070cc4..c0bf05e 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs { 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 @@ namespace System.Security.Cryptography.Pkcs // 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 @@ namespace System.Security.Cryptography.Pkcs } newSignerInfo.SignatureValue = signatureValue; - newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm!; + newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm!.Value!; X509Certificate2Collection certs = new X509Certificate2Collection(); certs.AddRange(Certificates); @@ -317,7 +314,7 @@ namespace System.Security.Cryptography.Pkcs { 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 be3874b..7da2de8 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs } 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs // authSafe ContentInfo, // macData MacData OPTIONAL // } - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); { writer.PushSequence(); @@ -234,7 +238,7 @@ namespace System.Security.Cryptography.Pkcs writer.PushSequence(); { - writer.WriteObjectIdentifier(Oids.Pkcs7Data); + writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data); Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0); @@ -262,7 +266,7 @@ namespace System.Security.Cryptography.Pkcs { writer.PushSequence(); { - writer.WriteObjectIdentifier(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm)); + writer.WriteObjectIdentifierForCrypto(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm)); writer.PopSequence(); } @@ -308,8 +312,8 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 7496e00..144c337 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs CertValue = encodedCertificate, }; - using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER)) - { - certBagAsn.Encode(writer); - return writer.Encode(); - } + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + certBagAsn.Encode(writer); + return writer.Encode(); } internal static Pkcs12CertBag DecodeValue(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 d101234..57bcc85 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs } 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 054c9c9..c67f7c0 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs public byte[] Encode() { - using (AsnWriter writer = EncodeToNewWriter()) - { - return writer.Encode(); - } + AsnWriter writer = EncodeToNewWriter(); + return writer.Encode(); } public Oid GetBagId() @@ -69,31 +67,19 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 49a4a24..32a4871 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs break; } } + catch (AsnContentException) + { + } catch (CryptographicException) { } @@ -352,81 +361,72 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs ConfidentialityMode == Pkcs12ConfidentialityMode.PublicKey) { writer = new AsnWriter(AsnEncodingRules.BER); - writer.WriteEncodedValue(_encrypted.Span); + writer.WriteEncodedValueForCrypto(_encrypted.Span); return writer; } @@ -446,67 +446,60 @@ namespace System.Security.Cryptography.Pkcs 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 cda6344..1536bf4 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 @@ namespace System.Security.Cryptography.Pkcs 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 e275fca..eb9d874 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 @@ namespace System.Security.Cryptography.Pkcs 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 97bc53b..a6c20a4 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs { 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 92584dd..fc3907d 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 @@ namespace System.Security.Cryptography.Pkcs 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 7361976..05c3875 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 @@ namespace System.Security.Cryptography.Pkcs 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 b126b1a..fca4e91 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 @@ namespace System.Security.Cryptography.Pkcs { private byte[] _encodedBytes = null!; // Initided using object initializer private Rfc3161TimeStampReq _parsedData; + private Oid? _hashAlgorithmId; + private Oid? _requestedPolicyId; private Rfc3161TimestampRequest() { @@ -23,8 +26,8 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs { HashAlgorithm = { - Algorithm = hashAlgorithmId, + Algorithm = hashAlgorithmId.Value!, Parameters = AlgorithmIdentifierAsn.ExplicitDerNull, }, HashedMessage = hash, }, - ReqPolicy = requestedPolicyId, + ReqPolicy = requestedPolicyId?.Value, CertReq = requestSignerCertificates, Nonce = nonce, }; @@ -312,21 +325,19 @@ namespace System.Security.Cryptography.Pkcs // 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 @@ namespace System.Security.Cryptography.Pkcs 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 958308b..43cc195 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 @@ namespace System.Security.Cryptography.Pkcs return true; } } + catch (AsnContentException) + { + } catch (CryptographicException) { } @@ -522,7 +526,7 @@ namespace System.Security.Cryptography.Pkcs 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 a9ff1fe..54c1383 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 @@ namespace System.Security.Cryptography.Pkcs { private readonly byte[] _encodedBytes; private readonly Rfc3161TstInfo _parsedData; + private Oid? _policyOid; + private Oid? _hashAlgorithmId; private ReadOnlyMemory? _tsaNameBytes; public Rfc3161TimestampTokenInfo( @@ -56,8 +59,8 @@ namespace System.Security.Cryptography.Pkcs } 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs bytesConsumed = firstElement.Length; return true; } + catch (AsnContentException) + { + tstInfo = default; + bytesConsumed = 0; + copiedBytes = null; + return false; + } catch (CryptographicException) { tstInfo = default; @@ -228,12 +236,12 @@ namespace System.Security.Cryptography.Pkcs { // 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 @@ namespace System.Security.Cryptography.Pkcs 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 74646c7..dd0e1ff 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs { 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs // 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 @@ namespace System.Security.Cryptography.Pkcs { 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 3d1e349..1f2dca1 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs } } - 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 @@ namespace System.Security.Cryptography.Pkcs { 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 @@ namespace System.Security.Cryptography.Pkcs // 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 @@ namespace System.Security.Cryptography.Pkcs 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 @@ namespace System.Security.Cryptography.Pkcs 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]; @@ -393,7 +390,7 @@ namespace System.Security.Cryptography.Pkcs { AttributeAsn attributeAsn = unsignedAttrs[i]; - if (attributeAsn.AttrType.Value == Oids.CounterSigner) + if (attributeAsn.AttrType == Oids.CounterSigner) { if (index < csIndex + attributeAsn.AttrValues.Length) { @@ -483,7 +480,7 @@ namespace System.Security.Cryptography.Pkcs public void CheckHash() { - if (_signatureAlgorithm.Value != Oids.NoSignature) + if (_signatureAlgorithm != Oids.NoSignature) { throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); } @@ -613,7 +610,7 @@ namespace System.Security.Cryptography.Pkcs { 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, @@ -636,7 +633,7 @@ namespace System.Security.Cryptography.Pkcs // .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); @@ -660,27 +657,15 @@ namespace System.Security.Cryptography.Pkcs { 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 } } } @@ -837,7 +822,7 @@ namespace System.Security.Cryptography.Pkcs { 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 8ff27c2..7cb5919 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 @@ namespace Internal.Cryptography 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 08faefd..bc11ac0 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; 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 @@ namespace Internal.Cryptography.Pal // // 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 9c81909..8f9c872 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal 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 f9a22a4..dded057 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 @@ namespace Internal.Cryptography.Pal { public AlgorithmIdentifier(AlgorithmIdentifierAsn algorithmIdentifier) { - AlgorithmId = algorithmIdentifier.Algorithm.Value; + AlgorithmId = algorithmIdentifier.Algorithm; Parameters = algorithmIdentifier.Parameters?.ToArray() ?? Array.Empty(); } @@ -82,11 +83,9 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal 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 e1b86cd..fe45ba5 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 @@ namespace Internal.Cryptography.Pal { 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 3387ccd..8f685cd 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal { 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 @@ namespace Internal.Cryptography.Pal 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 22c0d5c..aeb2a2b 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 @@ namespace Internal.Cryptography.Pal { // 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 fed160f..d7ccc9c 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 @@ namespace Internal.Cryptography.Pal 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 e88618e..3a39724 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 @@ namespace Internal.Cryptography.Pal (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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal // // 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 @@ namespace Internal.Cryptography.Pal // // 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 @@ namespace Internal.Cryptography.Pal // // 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal // 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 842472e..d55d344 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal 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 644e318..0aa954d 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.Buffers; 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 @@ namespace Internal.Cryptography.Pal { // 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 49320d4..b409468 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 @@ namespace Internal.Cryptography.Pal { 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 89c5e0c..0d5639b 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 Microsoft.Win32.SafeHandles; 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 @@ namespace Internal.Cryptography.Pal finally { password.DangerousRelease(); - tmpWriter.Dispose(); certAttrs.AsSpan(0, certCount).Clear(); certBags.AsSpan(0, certCount).Clear(); keyBags.AsSpan(0, certCount).Clear(); @@ -199,7 +199,7 @@ namespace Internal.Cryptography.Pal AttributeAsn attribute = new AttributeAsn { - AttrType = new Oid(Oids.LocalKeyId, null), + AttrType = Oids.LocalKeyId, AttrValues = new ReadOnlyMemory[] { attrBytes, @@ -289,18 +289,25 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal // } tmpWriter.PushSequence(); - tmpWriter.WriteObjectIdentifier(Oids.Pkcs12CertBag); + tmpWriter.WriteObjectIdentifierForCrypto(Oids.Pkcs12CertBag); tmpWriter.PushSequence(s_contextSpecific0); certBags[i].Encode(tmpWriter); @@ -361,12 +368,11 @@ namespace Internal.Cryptography.Pal } 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 @@ namespace Internal.Cryptography.Pal ReadOnlySpan.Empty, cipher, isPkcs12, - contentsSpan, + tmpWriter, s_windowsPbe, salt, certContents, @@ -442,7 +448,7 @@ namespace Internal.Cryptography.Pal hmacOid, certContentsIv); - tmpWriter.WriteOctetString(s_contextSpecific0, encodedCertContents.Span); + tmpWriter.WriteOctetString(encodedCertContents.Span, s_contextSpecific0); tmpWriter.PopSequence(); } @@ -456,12 +462,18 @@ namespace Internal.Cryptography.Pal 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 d1d4532..d32d711 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; 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal // 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal { 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal publicKeyInfo.Algorithm.Parameters.Value.Span.SequenceEqual(keyParams); } - if (algorithm != publicKeyInfo.Algorithm.Algorithm.Value) + if (algorithm != publicKeyInfo.Algorithm.Algorithm) { return false; } @@ -583,7 +598,7 @@ namespace Internal.Cryptography.Pal { 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 @@ namespace Internal.Cryptography.Pal 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 877762e..83da553 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 @@ namespace Internal.Cryptography.Pal 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 ae62596..306e047 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal 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 @@ namespace Internal.Cryptography.Pal 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 d1de01a..6507510 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 f6ddd18..59458ec 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 { 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -51,10 +64,22 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 400b063..0eefd38 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 // 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -84,6 +87,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); AsnValueReader defaultReader; 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 a045b54..42d2690 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 TbsCertificate.Encode(writer); SignatureAlgorithm.Encode(writer); - writer.WriteBitString(SignatureValue.Span); + writer.WriteBitString(SignatureValue.Span, 0); writer.PopSequence(tag); } @@ -39,11 +38,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -53,6 +59,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -62,7 +80,7 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 e21a9cd..2cc8c66 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 { 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -51,11 +71,23 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 7325f62..165b2a9 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 { 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -58,10 +71,22 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 a318835..5de3c36 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 CertificationRequestInfo.Encode(writer); SignatureAlgorithm.Encode(writer); - writer.WriteBitString(SignatureValue.Span); + writer.WriteBitString(SignatureValue.Span, 0); writer.PopSequence(tag); } @@ -39,11 +38,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -53,6 +59,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; @@ -62,7 +80,7 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 338bed0..a947100 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 } } - 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -72,6 +85,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); AsnValueReader collectionReader; 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 23af630..64cd141 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -78,6 +84,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); AsnValueReader explicitReader; 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 03164cd..6e075c3 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 } } - 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,15 +88,34 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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(); AsnValueReader collectionReader; 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 e9e3763..abe53a5 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -61,6 +67,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 557f920..84fae67 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 { 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -56,13 +76,25 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); ReadOnlySpan rebindSpan = rebind.Span; 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 c9ea25a..e5b2abe 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 // 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 } } - 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 } } - 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -145,6 +162,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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); AsnValueReader explicitReader; @@ -200,13 +229,13 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 4b2f3ac..7063eaa 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,15 +64,34 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 } 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 1a1af27..5cc3d55 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 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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) @@ -51,6 +57,18 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 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 168cd10..27fbe6a 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 @@ namespace System.Security.Cryptography.X509Certificates { Algorithm = new AlgorithmIdentifierAsn { - Algorithm = PublicKey.Oid, + Algorithm = PublicKey.Oid!.Value!, Parameters = PublicKey.EncodedParameters.RawData, }, SubjectPublicKey = PublicKey.EncodedKeyValue.RawData, @@ -586,22 +587,21 @@ namespace System.Security.Cryptography.X509Certificates 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 b5f7ee0..a212624 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 @@ namespace System.Security.Cryptography.X509Certificates 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 @@ namespace System.Security.Cryptography.X509Certificates }; } - 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 9088a67..b8c9af4 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 @@ namespace System.Security.Cryptography.X509Certificates } 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 @@ namespace System.Security.Cryptography.X509Certificates 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 08ca432..e96b9ff 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 @@ namespace System.Security.Cryptography.X509Certificates 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 0fa48fa..cbe6e86 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 @@ namespace System.Security.Cryptography.X509Certificates 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 7a01885..f3b7b2f 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 @@ namespace System.Security.Cryptography.X509Certificates 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 8796d30..73c2a33 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 @@ namespace System.Security.Cryptography.X509Certificates 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 @@ namespace System.Security.Cryptography.X509Certificates 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 @@ namespace System.Security.Cryptography.X509Certificates 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 5766ce1..1acc0bb 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests } } - if (!idReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory reqDn)) + if (!idReader.TryReadPrimitiveOctetString(out ReadOnlyMemory reqDn)) { idReader.ThrowIfNotEmpty(); } @@ -668,7 +621,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests return CertStatus.Unknown; } - if (!idReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory reqKeyHash)) + if (!idReader.TryReadPrimitiveOctetString(out ReadOnlyMemory reqKeyHash)) { idReader.ThrowIfNotEmpty(); } @@ -699,69 +652,55 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests 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 cec9986..43de608 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 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests ReadOnlyMemory wholeExtension = requestExtensions.PeekEncodedValue(); AsnReader extension = requestExtensions.ReadSequence(); - if (extension.ReadObjectIdentifierAsString() == "1.3.6.1.5.5.7.48.1.2") + if (extension.ReadObjectIdentifier() == "1.3.6.1.5.5.7.48.1.2") { nonceExtension = wholeExtension; } diff --git a/src/libraries/pkg/baseline/packageIndex.json b/src/libraries/pkg/baseline/packageIndex.json index d3fffa8..e1ecdf2 100644 --- a/src/libraries/pkg/baseline/packageIndex.json +++ b/src/libraries/pkg/baseline/packageIndex.json @@ -2873,6 +2873,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 fbfa4e0..558d857 100644 --- a/src/libraries/pkg/descriptions.json +++ b/src/libraries/pkg/descriptions.json @@ -936,6 +936,14 @@ ] }, { + "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.", "CommonTypes": [ -- 2.7.4