/// <summary>
        ///   Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the
        ///   [<see cref="FlagsAttribute"/>] enum specfied by <typeparamref name="TFlagsEnum"/>.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</param>
        /// <typeparam name="TFlagsEnum">Destination enum type</typeparam>
        /// <returns>
        ///   the NamedBitList value converted to a <typeparamref name="TFlagsEnum"/>.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   the next value does not have the correct 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 <typeparamref name="TFlagsEnum"/> value
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <typeparamref name="TFlagsEnum"/> is not an enum type --OR--
        ///   <typeparamref name="TFlagsEnum"/> was not declared with <see cref="FlagsAttribute"/>
        ///   --OR--
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <remarks>
        ///   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 <typeparamref name="TFlagsEnum"/>,
        ///   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:
        ///
        ///   <code>
        ///     KeyUsage ::= BIT STRING {
        ///       digitalSignature   (0),
        ///       nonRepudiation     (1),
        ///       keyEncipherment    (2),
        ///       dataEncipherment   (3),
        ///       keyAgreement       (4),
        ///       keyCertSign        (5),
        ///       cRLSign            (6),
        ///       encipherOnly       (7),
        ///       decipherOnly       (8) }
        ///   </code>
        ///
        ///   <code>
        ///     [Flags]
        ///     enum KeyUsage
        ///     {
        ///         None              = 0,
        ///         DigitalSignature  = 1 &lt;&lt; (0),
        ///         NonRepudiation    = 1 &lt;&lt; (1),
        ///         KeyEncipherment   = 1 &lt;&lt; (2),
        ///         DataEncipherment  = 1 &lt;&lt; (3),
        ///         KeyAgreement      = 1 &lt;&lt; (4),
        ///         KeyCertSign       = 1 &lt;&lt; (5),
        ///         CrlSign           = 1 &lt;&lt; (6),
        ///         EncipherOnly      = 1 &lt;&lt; (7),
        ///         DecipherOnly      = 1 &lt;&lt; (8),
        ///     }
        ///   </code>
        ///
        ///   Note that while the example here uses the KeyUsage NamedBitList from
        ///   <a href="https://tools.ietf.org/html/rfc3280#section-4.2.1.3">RFC 3280 (4.2.1.3)</a>,
        ///   the example enum uses values thar are different from
        ///   System.Security.Cryptography.X509Certificates.X509KeyUsageFlags.
        /// </remarks>
        public TFlagsEnum ReadNamedBitListValue <TFlagsEnum>(Asn1Tag expectedTag) where TFlagsEnum : struct
        {
            Type tFlagsEnum = typeof(TFlagsEnum);

            return((TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum)));
        }
Exemple #2
0
        private string ReadObjectIdentifierAsString(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
            // T-REC-X.690-201508 sec 8.19.2 says the minimum length is 1
            if (tag.IsConstructed || length < 1)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            ReadOnlySpan<byte> 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.
            //
            // The OID with the most arcs which were found after a 30 minute search is
            // "1.3.6.1.4.1.311.60.2.1.1" (EV cert jurisdiction of incorporation - locality)
            // which has 11 arcs.
            // The longest "known" segment is 16 bytes, a UUID-as-an-arc value.
            // 16 * 11 = 176 bytes for an "extremely long" OID.
            //
            // So pre-allocate the StringBuilder with at most 1020 characters, an input longer than
            // 255 encoded bytes will just have to re-allocate.
            StringBuilder builder = new StringBuilder(((byte)contents.Length) * 4);

            ReadSubIdentifier(contents, out int bytesRead, out long? smallValue, out BigInteger? largeValue);

            // T-REC-X.690-201508 sec 8.19.4
            // The first two subidentifiers (X.Y) are encoded as (X * 40) + Y, because Y is
            // bounded [0, 39] for X in {0, 1}, and only X in {0, 1, 2} are legal.
            // So:
            // * identifier < 40 => X = 0, Y = identifier.
            // * identifier < 80 => X = 1, Y = identifier - 40.
            // * else: X = 2, Y = identifier - 80.
            byte firstArc;

            if (smallValue != null)
            {
                long firstIdentifier = smallValue.Value;

                if (firstIdentifier < 40)
                {
                    firstArc = 0;
                }
                else if (firstIdentifier < 80)
                {
                    firstArc = 1;
                    firstIdentifier -= 40;
                }
                else
                {
                    firstArc = 2;
                    firstIdentifier -= 80;
                }

                builder.Append(firstArc);
                builder.Append('.');
                builder.Append(firstIdentifier);
            }
            else
            {
                Debug.Assert(largeValue != null);
                BigInteger firstIdentifier = largeValue.Value;

                // We're only here because we were bigger than long.MaxValue, so
                // we're definitely on arc 2.
                Debug.Assert(firstIdentifier > long.MaxValue);

                firstArc = 2;
                firstIdentifier -= 80;

                builder.Append(firstArc);
                builder.Append('.');
                builder.Append(firstIdentifier.ToString());
            }

            contents = contents.Slice(bytesRead);

            while (!contents.IsEmpty)
            {
                ReadSubIdentifier(contents, out bytesRead, out smallValue, out largeValue);
                // Exactly one should be non-null.
                Debug.Assert((smallValue == null) != (largeValue == null));

                builder.Append('.');

                if (smallValue != null)
                {
                    builder.Append(smallValue.Value);
                }
                else
                {
                    builder.Append(largeValue!.Value.ToString());
                }

                contents = contents.Slice(bytesRead);
            }

            totalBytesRead = headerLength + length.Value;
            return builder.ToString();
        }
        internal void Encode(AsnWriter writer, Asn1Tag tag)
        {
            writer.PushSequence(tag);


            // DEFAULT value handler for HashAlgorithm.
            {
                using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER))
                {
                    HashAlgorithm.Encode(tmp);
                    ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan();

                    if (!encoded.SequenceEqual(s_defaultHashAlgorithm))
                    {
                        writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
                        writer.WriteEncodedValue(encoded);
                        writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
                    }
                }
            }


            // DEFAULT value handler for MaskGenAlgorithm.
            {
                using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER))
                {
                    MaskGenAlgorithm.Encode(tmp);
                    ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan();

                    if (!encoded.SequenceEqual(s_defaultMaskGenAlgorithm))
                    {
                        writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1));
                        writer.WriteEncodedValue(encoded);
                        writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1));
                    }
                }
            }


            // DEFAULT value handler for SaltLength.
            {
                using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER))
                {
                    tmp.WriteInteger(SaltLength);
                    ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan();

                    if (!encoded.SequenceEqual(s_defaultSaltLength))
                    {
                        writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2));
                        writer.WriteEncodedValue(encoded);
                        writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2));
                    }
                }
            }


            // DEFAULT value handler for TrailerField.
            {
                using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER))
                {
                    tmp.WriteInteger(TrailerField);
                    ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan();

                    if (!encoded.SequenceEqual(s_defaultTrailerField))
                    {
                        writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3));
                        writer.WriteEncodedValue(encoded);
                        writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3));
                    }
                }
            }

            writer.PopSequence(tag);
        }
        /// <summary>
        ///   Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the
        ///   [<see cref="FlagsAttribute"/>] enum specfied by <paramref name="tFlagsEnum"/>.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</param>
        /// <param name="tFlagsEnum">Type object representing the destination type.</param>
        /// <returns>
        ///   the NamedBitList value converted to a <paramref name="tFlagsEnum"/>.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   the next value does not have the correct 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 <paramref name="tFlagsEnum"/> value
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tFlagsEnum"/> is not an enum type --OR--
        ///   <paramref name="tFlagsEnum"/> was not declared with <see cref="FlagsAttribute"/>
        ///   --OR--
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <seealso cref="ReadNamedBitListValue{TFlagsEnum}(Asn1Tag)"/>
        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);
            Span <byte>           stackSpan = stackalloc byte[sizeLimit];
            ReadOnlyMemory <byte> saveData  = _data;

            // 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 <byte> 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;
            }
        }
Exemple #5
0
        internal void Encode(AsnWriter writer)
        {
            bool wroteValue = false;

            if (TeletexString != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(UniversalTagNumber.T61String, TeletexString);
                wroteValue = true;
            }

            if (PrintableString != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(UniversalTagNumber.PrintableString, PrintableString);
                wroteValue = true;
            }

            if (UniversalString.HasValue)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                // Validator for tag constraint for UniversalString
                {
                    if (!Asn1Tag.TryDecode(UniversalString.Value.Span, out Asn1Tag validateTag, out _) ||
                        !validateTag.HasSameClassAndValue(new Asn1Tag((UniversalTagNumber)28)))
                    {
                        throw new CryptographicException();
                    }
                }

                writer.WriteEncodedValue(UniversalString.Value.Span);
                wroteValue = true;
            }

            if (Utf8String != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(UniversalTagNumber.UTF8String, Utf8String);
                wroteValue = true;
            }

            if (BmpString != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(UniversalTagNumber.BMPString, BmpString);
                wroteValue = true;
            }

            if (!wroteValue)
            {
                throw new CryptographicException();
            }
        }
        internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory <byte> rebind, out PssParamsAsn decoded)
        {
            decoded = default;
            AsnValueReader sequenceReader = reader.ReadSequence(expectedTag);
            AsnValueReader explicitReader;
            AsnValueReader defaultReader;


            if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
            {
                explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
                System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref explicitReader, rebind, out decoded.HashAlgorithm);
                explicitReader.ThrowIfNotEmpty();
            }
            else
            {
                defaultReader = new AsnValueReader(s_defaultHashAlgorithm, AsnEncodingRules.DER);
                System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.HashAlgorithm);
            }


            if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
            {
                explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1));
                System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref explicitReader, rebind, out decoded.MaskGenAlgorithm);
                explicitReader.ThrowIfNotEmpty();
            }
            else
            {
                defaultReader = new AsnValueReader(s_defaultMaskGenAlgorithm, AsnEncodingRules.DER);
                System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.MaskGenAlgorithm);
            }


            if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2)))
            {
                explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2));

                if (!explicitReader.TryReadInt32(out decoded.SaltLength))
                {
                    explicitReader.ThrowIfNotEmpty();
                }

                explicitReader.ThrowIfNotEmpty();
            }
            else
            {
                defaultReader = new AsnValueReader(s_defaultSaltLength, AsnEncodingRules.DER);

                if (!defaultReader.TryReadInt32(out decoded.SaltLength))
                {
                    defaultReader.ThrowIfNotEmpty();
                }
            }


            if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3)))
            {
                explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3));

                if (!explicitReader.TryReadInt32(out decoded.TrailerField))
                {
                    explicitReader.ThrowIfNotEmpty();
                }

                explicitReader.ThrowIfNotEmpty();
            }
            else
            {
                defaultReader = new AsnValueReader(s_defaultTrailerField, AsnEncodingRules.DER);

                if (!defaultReader.TryReadInt32(out decoded.TrailerField))
                {
                    defaultReader.ThrowIfNotEmpty();
                }
            }


            sequenceReader.ThrowIfNotEmpty();
        }
Exemple #7
0
        /// <summary>
        ///   Write an Object Identifier with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="oidValue">The object identifier to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="CryptographicException">
        ///   <paramref name="oidValue"/> is not a valid dotted decimal
        ///   object identifier
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        public void WriteObjectIdentifier(Asn1Tag tag, ReadOnlySpan <char> oidValue)
        {
            CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier);

            WriteObjectIdentifierCore(tag.AsPrimitive(), oidValue);
        }
Exemple #8
0
        // T-REC-X.690-201508 sec 8.19
        private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan <char> 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);
            }
            if (oidValue[1] != '.')
            {
                throw new CryptographicException(SR.Argument_InvalidOidValue);
            }

            // 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))
            // So length / 2 should prevent any reallocations.
            byte[] tmp       = CryptoPool.Rent(oidValue.Length / 2);
            int    tmpOffset = 0;

            try
            {
                int firstComponent = oidValue[0] switch
                {
                    '0' => 0,
                    '1' => 1,
                    '2' => 2,
                    _ => throw new CryptographicException(SR.Argument_InvalidOidValue),
                };

                // The first two components are special:
                // ITU X.690 8.19.4:
                //   The numerical value of the first subidentifier is derived from the values of the first two
                //   object identifier components in the object identifier value being encoded, using the formula:
                //       (X*40) + Y
                //   where X is the value of the first object identifier component and Y is the value of the
                //   second object identifier component.
                //       NOTE - This packing of the first two object identifier components recognizes that only
                //          three values are allocated from the root node, and at most 39 subsequent values from
                //          nodes reached by X = 0 and X = 1.

                // skip firstComponent and the trailing .
                ReadOnlySpan <char> remaining = oidValue.Slice(2);

                BigInteger subIdentifier = ParseSubIdentifier(ref remaining);
                subIdentifier += 40 * firstComponent;

                int localLen = EncodeSubIdentifier(tmp.AsSpan(tmpOffset), ref subIdentifier);
                tmpOffset += localLen;

                while (!remaining.IsEmpty)
                {
                    subIdentifier = ParseSubIdentifier(ref remaining);
                    localLen      = EncodeSubIdentifier(tmp.AsSpan(tmpOffset), ref subIdentifier);
                    tmpOffset    += localLen;
                }

                Debug.Assert(!tag.IsConstructed);
                WriteTag(tag);
                WriteLength(tmpOffset);
                Buffer.BlockCopy(tmp, 0, _buffer, _offset, tmpOffset);
                _offset += tmpOffset;
            }
            finally
            {
                CryptoPool.Return(tmp, tmpOffset);
            }
        }
Exemple #9
0
        private int ProcessConstructedBitString(
            ReadOnlyMemory <byte> source,
            Span <byte> destination,
            BitStringCopyAction?copyAction,
            bool isIndefinite,
            out int lastUnusedBitCount,
            out int bytesRead)
        {
            lastUnusedBitCount = 0;
            bytesRead          = 0;
            int lastSegmentLength = MaxCERSegmentSize;

            AsnReader?tmpReader = new AsnReader(source, RuleSet);
            Stack <(AsnReader, bool, int)>?readerStack = null;
            int         totalLength = 0;
            Asn1Tag     tag         = Asn1Tag.ConstructedBitString;
            Span <byte> curDest     = destination;

            do
            {
                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);
                        ReadOnlyMemory <byte> encodedValue = Slice(tmpReader._data, headerLength, length.Value);

                        ParsePrimitiveBitStringContents(
                            encodedValue,
                            out lastUnusedBitCount,
                            out ReadOnlyMemory <byte> 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)
                        {
                            (AsnReader topReader, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop();
                            topReader._data = topReader._data.Slice(bytesRead);

                            bytesRead   += pushedBytesRead;
                            isIndefinite = wasIndefinite;
                            tmpReader    = topReader;
                        }
                        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 <(AsnReader, bool, int)>();
                        }

                        readerStack.Push((tmpReader, isIndefinite, bytesRead));

                        tmpReader = new AsnReader(
                            Slice(tmpReader._data, headerLength, length),
                            RuleSet);

                        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)
                {
                    (AsnReader topReader, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop();

                    tmpReader       = topReader;
                    tmpReader._data = tmpReader._data.Slice(bytesRead);

                    isIndefinite = wasIndefinite;
                    bytesRead   += pushedBytesRead;
                }
                else
                {
                    tmpReader = null;
                }
            } while (tmpReader != null);

            return(totalLength);
        }
Exemple #10
0
        // T-REC-X.690-201508 sec 9.2, 8.7
        private void WriteConstructedCerOctetString(Asn1Tag tag, ReadOnlySpan <byte> payload)
        {
            const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize;

            Debug.Assert(payload.Length > MaxCERSegmentSize);

            WriteTag(tag.AsConstructed());
            WriteLength(-1);

            int fullSegments = Math.DivRem(payload.Length, MaxCERSegmentSize, out int lastSegmentSize);

            // 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 (lastSegmentSize == 0)
            {
                remainingEncodedSize = 0;
            }
            else
            {
                // One byte of tag, and minimum one byte of length.
                remainingEncodedSize = 2 + lastSegmentSize + GetEncodedLengthSubsequentByteCount(lastSegmentSize);
            }

            // Reduce the number of copies by pre-calculating the size.
            // +2 for End-Of-Contents
            int expectedSize = fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 2;

            EnsureWriteCapacity(expectedSize);

            byte[] ensureNoExtraCopy = _buffer;
            int    savedOffset       = _offset;

            ReadOnlySpan <byte> remainingData = payload;
            Span <byte>         dest;
            Asn1Tag             primitiveOctetString = Asn1Tag.PrimitiveOctetString;

            while (remainingData.Length > MaxCERSegmentSize)
            {
                // T-REC-X.690-201508 sec 8.7.3.2-note2
                WriteTag(primitiveOctetString);
                WriteLength(MaxCERSegmentSize);

                dest = _buffer.AsSpan(_offset);
                remainingData.Slice(0, MaxCERSegmentSize).CopyTo(dest);

                _offset      += MaxCERSegmentSize;
                remainingData = remainingData.Slice(MaxCERSegmentSize);
            }

            WriteTag(primitiveOctetString);
            WriteLength(remainingData.Length);
            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(WriteConstructedCerOctetString)}");
        }
        public static ReadOnlyMemory <byte> GetOctetStringBytes(this AsnReader reader, Asn1Tag expectedTag)
        {
            if (reader.TryGetPrimitiveOctetStringBytes(expectedTag, out ReadOnlyMemory <byte> contents))
            {
                return(contents);
            }

            // Guaranteed too big, because it has the tag and length.
            int length = reader.PeekEncodedValue().Length;

            byte[] rented = ArrayPool <byte> .Shared.Rent(length);

            try
            {
                if (reader.TryCopyOctetStringBytes(expectedTag, rented, out int bytesWritten))
                {
                    return(new ReadOnlyMemory <byte>(rented.AsSpan(0, bytesWritten).ToArray()));
                }

                Debug.Fail("TryCopyOctetStringBytes produced more data than the encoded size");
                throw new CryptographicException();
            }
            finally
            {
                Array.Clear(rented, 0, length);
                ArrayPool <byte> .Shared.Return(rented);
            }
        }
Exemple #12
0
 public string ReadCharacterString(Asn1Tag expectedTag, UniversalTagNumber encodingType)
 {
     Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);
     return(ReadCharacterString(expectedTag, encodingType, encoding));
 }
Exemple #13
0
        /// <summary>
        ///   Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the
        ///   non-[<see cref="FlagsAttribute"/>] enum specfied by <typeparamref name="TEnum"/>.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</param>
        /// <typeparam name="TEnum">Destination enum type</typeparam>
        /// <returns>
        ///   the Enumerated value converted to a <typeparamref name="TEnum"/>.
        /// </returns>
        /// <remarks>
        ///   This method does not validate that the return value is defined within
        ///   <typeparamref name="TEnum"/>.
        /// </remarks>
        /// <exception cref="CryptographicException">
        ///   the next value does not have the correct 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 <typeparamref name="TEnum"/> value
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <typeparamref name="TEnum"/> is not an enum type --OR--
        ///   <typeparamref name="TEnum"/> was declared with <see cref="FlagsAttribute"/>
        ///   --OR--
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        public TEnum ReadEnumeratedValue <TEnum>(Asn1Tag expectedTag) where TEnum : struct
        {
            Type tEnum = typeof(TEnum);

            return((TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum)));
        }
Exemple #14
0
        internal void Encode(AsnWriter writer)
        {
            bool wroteValue = false;

            if (OtherName.HasValue)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                OtherName.Value.Encode(writer, new Asn1Tag(TagClass.ContextSpecific, 0));
                wroteValue = true;
            }

            if (Rfc822Name != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String, Rfc822Name);
                wroteValue = true;
            }

            if (DnsName != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String, DnsName);
                wroteValue = true;
            }

            if (X400Address.HasValue)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                // Validator for tag constraint for X400Address
                {
                    if (!Asn1Tag.TryParse(X400Address.Value.Span, out Asn1Tag validateTag, out _) ||
                        !validateTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3)))
                    {
                        throw new CryptographicException();
                    }
                }

                writer.WriteEncodedValue(X400Address.Value);
                wroteValue = true;
            }

            if (DirectoryName.HasValue)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4));
                writer.WriteEncodedValue(DirectoryName.Value);
                writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4));
                wroteValue = true;
            }

            if (EdiPartyName.HasValue)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                EdiPartyName.Value.Encode(writer, new Asn1Tag(TagClass.ContextSpecific, 5));
                wroteValue = true;
            }

            if (Uri != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String, Uri);
                wroteValue = true;
            }

            if (IPAddress.HasValue)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 7), IPAddress.Value.Span);
                wroteValue = true;
            }

            if (RegisteredId != null)
            {
                if (wroteValue)
                {
                    throw new CryptographicException();
                }

                writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 8), RegisteredId);
                wroteValue = true;
            }

            if (!wroteValue)
            {
                throw new CryptographicException();
            }
        }
Exemple #15
0
        internal static void Decode(AsnReader reader, out GeneralNameAsn decoded)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            decoded = default;
            Asn1Tag   tag = reader.PeekTag();
            AsnReader explicitReader;

            if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0)))
            {
                System.Security.Cryptography.Asn1.OtherNameAsn tmpOtherName;
                System.Security.Cryptography.Asn1.OtherNameAsn.Decode(reader, new Asn1Tag(TagClass.ContextSpecific, 0), out tmpOtherName);
                decoded.OtherName = tmpOtherName;
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1)))
            {
                decoded.Rfc822Name = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String);
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2)))
            {
                decoded.DnsName = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String);
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3)))
            {
                decoded.X400Address = reader.GetEncodedValue();
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4)))
            {
                explicitReader        = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4));
                decoded.DirectoryName = explicitReader.GetEncodedValue();
                explicitReader.ThrowIfNotEmpty();
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5)))
            {
                System.Security.Cryptography.Asn1.EdiPartyNameAsn tmpEdiPartyName;
                System.Security.Cryptography.Asn1.EdiPartyNameAsn.Decode(reader, new Asn1Tag(TagClass.ContextSpecific, 5), out tmpEdiPartyName);
                decoded.EdiPartyName = tmpEdiPartyName;
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6)))
            {
                decoded.Uri = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String);
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7)))
            {
                if (reader.TryGetPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 7), out ReadOnlyMemory <byte> tmpIPAddress))
                {
                    decoded.IPAddress = tmpIPAddress;
                }
                else
                {
                    decoded.IPAddress = reader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 7));
                }
            }
            else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8)))
            {
                decoded.RegisteredId = reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 8));
            }
            else
            {
                throw new CryptographicException();
            }
        }