public static void TryReadPrimitiveBitStringValue_Throws(
            string description,
            AsnEncodingRules ruleSet,
            string inputHex)
        {
            _ = description;
            byte[]    inputData = inputHex.HexToByteArray();
            AsnReader reader    = new AsnReader(inputData, ruleSet);

            Assert.Throws <AsnContentException>(
                () =>
            {
                reader.TryReadPrimitiveBitString(
                    out int unusedBitCount,
                    out ReadOnlyMemory <byte> contents);
            });

            Assert.Throws <AsnContentException>(
                () =>
            {
                reader.TryReadBitString(
                    new byte[inputData.Length],
                    out int unusedBitCount,
                    out int written);
            });

            Assert.Throws <AsnContentException>(
                () => reader.ReadBitString(out int unusedBitCount));
        }
Esempio n. 2
0
        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 <byte> 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 <byte>(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);
        }
        public static void TryCopyBitStringBytes_Fails(AsnEncodingRules ruleSet, string inputHex)
        {
            byte[]    inputData = inputHex.HexToByteArray();
            AsnReader reader    = new AsnReader(inputData, ruleSet);

            bool didRead = reader.TryReadBitString(
                Span <byte> .Empty,
                out int unusedBitCount,
                out int bytesWritten);

            Assert.False(didRead, "reader.TryReadBitString");
            Assert.Equal(0, unusedBitCount);
            Assert.Equal(0, bytesWritten);
        }
        private static void TryReadBitString_Throws_Helper(
            AsnEncodingRules ruleSet,
            byte[] input)
        {
            AsnReader reader = new AsnReader(input, ruleSet);

            Assert.Throws <AsnContentException>(
                () =>
            {
                reader.TryReadBitString(
                    Span <byte> .Empty,
                    out int unusedBitCount,
                    out int bytesWritten);
            });
        }
        public static void TryCopyBitStringBytes_Success_CER_MaxPrimitiveLength()
        {
            // CER says that the maximum encoding length for a BitString primitive is
            // 1000 (999 value bytes and 1 unused bit count byte).
            //
            // So we need 03 [1000] [0x00-0x07] { 998 anythings } [a byte that's legal for the bitmask]
            // 1000 => 0x3E8, so the length encoding is 82 03 E8.
            // 1000 + 3 + 1 == 1003
            byte[] input = new byte[1004];
            input[0] = 0x03;
            input[1] = 0x82;
            input[2] = 0x03;
            input[3] = 0xE8;

            // Unused bits
            input[4] = 0x02;

            // Payload
            input[5]    = 0xA0;
            input[1002] = 0xA5;
            input[1003] = 0xFC;

            byte[] output = new byte[999];

            AsnReader reader = new AsnReader(input, AsnEncodingRules.CER);

            bool success = reader.TryReadBitString(
                output,
                out int unusedBitCount,
                out int bytesWritten);

            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);
        }
        public static void TryCopyBitStringBytes_Success(
            AsnEncodingRules ruleSet,
            string inputHex,
            string expectedHex,
            int expectedUnusedBitCount)
        {
            byte[]    inputData = inputHex.HexToByteArray();
            byte[]    output    = new byte[expectedHex.Length / 2];
            AsnReader reader    = new AsnReader(inputData, ruleSet);

            bool didRead = reader.TryReadBitString(
                output,
                out int unusedBitCount,
                out int bytesWritten);

            Assert.True(didRead, "reader.TryReadBitString");
            Assert.Equal(expectedUnusedBitCount, unusedBitCount);
            Assert.Equal(expectedHex, output.AsSpan(0, bytesWritten).ByteArrayToHex());
        }
        public static void TryCopyBitStringBytes_ExtremelyNested()
        {
            byte[] dataBytes = new byte[4 * 16384];

            // This will build 2^14 nested indefinite length values.
            // In the end, none of them contain any content.
            //
            // For what it's worth, the initial algorithm succeeded at 1017, and StackOverflowed with 1018.
            int end = dataBytes.Length / 2;

            // UNIVERSAL BIT STRING [Constructed]
            const byte Tag = 0x20 | (byte)UniversalTagNumber.BitString;

            for (int i = 0; i < end; i += 2)
            {
                dataBytes[i] = Tag;
                // Indefinite length
                dataBytes[i + 1] = 0x80;
            }

            AsnReader reader = new AsnReader(dataBytes, AsnEncodingRules.BER);

            int bytesWritten;
            int unusedBitCount;

            Assert.True(
                reader.TryReadBitString(Span <byte> .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 <byte>(), output);
        }
        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).
            //
            // So we need 03 [1001] { 1001 0x00s }
            // 1001 => 0x3E9, so the length encoding is 82 03 E9.
            // 1001 + 3 + 1 == 1005
            byte[] input = new byte[1005];
            input[0] = 0x03;
            input[1] = 0x82;
            input[2] = 0x03;
            input[3] = 0xE9;

            AsnReader reader = new AsnReader(input, AsnEncodingRules.CER);

            Assert.Throws <AsnContentException>(
                () =>
            {
                reader.TryReadPrimitiveBitString(
                    out int unusedBitCount,
                    out ReadOnlyMemory <byte> contents);
            });

            Assert.Throws <AsnContentException>(
                () =>
            {
                reader.TryReadBitString(
                    new byte[input.Length],
                    out int unusedBitCount,
                    out int written);
            });

            Assert.Throws <AsnContentException>(
                () => reader.ReadBitString(out int unusedBitCount));
        }
        public static void ExpectedTag_IgnoresConstructed(
            AsnEncodingRules ruleSet,
            string inputHex,
            TagClass tagClass,
            int tagValue)
        {
            byte[]    inputData          = inputHex.HexToByteArray();
            AsnReader reader             = new AsnReader(inputData, ruleSet);
            Asn1Tag   correctConstructed = new Asn1Tag(tagClass, tagValue, true);
            Asn1Tag   correctPrimitive   = new Asn1Tag(tagClass, tagValue, false);

            Assert.True(
                reader.TryReadPrimitiveBitString(
                    out int ubc1,
                    out ReadOnlyMemory <byte> val1,
                    correctConstructed));

            Assert.False(reader.HasData);

            reader = new AsnReader(inputData, ruleSet);

            Assert.True(
                reader.TryReadPrimitiveBitString(
                    out int ubc2,
                    out ReadOnlyMemory <byte> val2,
                    correctPrimitive));

            Assert.False(reader.HasData);

            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);
        }
        public static void TagMustBeCorrect_Custom(AsnEncodingRules ruleSet)
        {
            byte[]    inputData = { 0x87, 2, 0, 0x80 };
            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 <ArgumentException>(
                "expectedTag",
                () => reader.TryReadPrimitiveBitString(out _, out _, Asn1Tag.Null));
            AssertExtensions.Throws <ArgumentException>(
                "expectedTag",
                () => reader.TryReadBitString(output, out _, out _, Asn1Tag.Null));
            AssertExtensions.Throws <ArgumentException>(
                "expectedTag",
                () => reader.ReadBitString(out _, Asn1Tag.Null));

            Assert.True(reader.HasData, "HasData after bad universal tag");

            Assert.Throws <AsnContentException>(() => reader.TryReadPrimitiveBitString(out _, out _));
            Assert.Throws <AsnContentException>(() => reader.TryReadBitString(output, out _, out _));
            Assert.Throws <AsnContentException>(() => reader.ReadBitString(out _));
            Assert.True(reader.HasData, "HasData after default tag");

            Assert.Throws <AsnContentException>(() => reader.TryReadPrimitiveBitString(out _, out _, wrongTag1));
            Assert.Throws <AsnContentException>(() => reader.TryReadBitString(output, out _, out _, wrongTag1));
            Assert.Throws <AsnContentException>(() => reader.ReadBitString(out _, wrongTag1));
            Assert.True(reader.HasData, "HasData after wrong custom class");

            Assert.Throws <AsnContentException>(() => reader.TryReadPrimitiveBitString(out _, out _, wrongTag2));
            Assert.Throws <AsnContentException>(() => reader.TryReadBitString(output, out _, out _, wrongTag2));
            Assert.Throws <AsnContentException>(() => reader.ReadBitString(out _, wrongTag2));
            Assert.True(reader.HasData, "HasData after wrong custom tag value");

            Assert.True(
                reader.TryReadPrimitiveBitString(
                    out int unusedBitCount,
                    out ReadOnlyMemory <byte> 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");
        }
        public static void TryCopyBitStringBytes_Success_CER_MinConstructedLength()
        {
            // CER says that the maximum encoding length for a BitString primitive is
            // 1000 (999 value bytes and 1 unused bit count byte), and that a constructed
            // form must be used for values greater than 1000 bytes, with segments dividing
            // up for each thousand [1000, 1000, ..., len%1000].
            //
            // Bit string primitives are one byte of "unused bits" and the rest are payload,
            // so the minimum constructed payload has total content length 1002:
            // [1000 (1+999), 2 (1+1)]
            //
            // 23 80 (indefinite constructed bit string)
            //    03 82 03 E9 (primitive bit string, 1000 bytes)
            //       00 [999 more payload bytes]
            //    03 02 (primitive bit string, 2 bytes)
            //       uu pp
            //    00 00 (end of contents, 0 bytes)
            // 1010 total.
            byte[] input  = new byte[1012];
            int    offset = 0;

            // CONSTRUCTED BIT STRING (Indefinite)
            input[offset++] = 0x23;
            input[offset++] = 0x80;
            // BIT STRING (1000)
            input[offset++] = 0x03;
            input[offset++] = 0x82;
            input[offset++] = 0x03;
            input[offset++] = 0xE8;

            // Primitive 1: Unused bits MUST be 0.
            input[offset++] = 0x00;

            // Payload (A0 :: A5 FC) (999)
            input[offset]   = 0xA0;
            offset         += 997;
            input[offset++] = 0xA5;
            input[offset++] = 0xFC;

            // BIT STRING (2)
            input[offset++] = 0x03;
            input[offset++] = 0x02;

            // Primitive 2: Unused bits 0-7
            input[offset++] = 0x3;

            // Payload (must have the three least significant bits unset)
            input[offset] = 0b0000_1000;

            byte[] expected = new byte[1000];
            offset             = 0;
            expected[offset]   = 0xA0;
            offset            += 997;
            expected[offset++] = 0xA5;
            expected[offset++] = 0xFC;
            expected[offset]   = 0b0000_1000;

            byte[] output = new byte[1000];

            AsnReader reader = new AsnReader(input, AsnEncodingRules.CER);

            bool success = reader.TryReadBitString(
                output,
                out int unusedBitCount,
                out int bytesWritten);

            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);
        }