Exemplo n.º 1
0
 // 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 Scope PushSetOfCore(Asn1Tag tag)
 {
     Debug.Assert(tag.IsConstructed);
     return(PushTag(tag, UniversalTagNumber.SetOf));
 }
Exemplo n.º 2
0
 // T-REC-X.690-201508 sec 8.9, 8.10
 private void PopSequenceCore(Asn1Tag tag)
 {
     Debug.Assert(tag.IsConstructed);
     PopTag(tag, UniversalTagNumber.Sequence);
 }
Exemplo n.º 3
0
 internal Asn1Tag PeekTag()
 {
     return(Asn1Tag.Decode(_span, out _));
 }
Exemplo n.º 4
0
        private void PopTag(Asn1Tag tag, UniversalTagNumber tagType, bool sortContents = false)
        {
            if (_nestingStack == null || _nestingStack.Count == 0)
            {
                throw new InvalidOperationException(SR.AsnWriter_PopWrongTag);
            }

            (Asn1Tag stackTag, int lenOffset, UniversalTagNumber stackTagType) = _nestingStack.Peek();

            Debug.Assert(tag.IsConstructed);
            if (stackTag != tag || stackTagType != tagType)
            {
                throw new InvalidOperationException(SR.AsnWriter_PopWrongTag);
            }

            _nestingStack.Pop();

            if (sortContents)
            {
                Debug.Assert(tagType == UniversalTagNumber.SetOf);
                SortContents(_buffer, lenOffset + 1, _offset);
            }

            // BER could use the indefinite encoding that CER does.
            // But since the definite encoding form is easier to read (doesn't require a contextual
            // parser to find the end-of-contents marker) some ASN.1 readers (including the previous
            // incarnation of AsnReader) may choose not to support it.
            //
            // So, BER will use the DER rules here, in the interest of broader compatibility.

            // 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 && tagType != UniversalTagNumber.OctetString)
            {
                WriteEndOfContents();
                return;
            }

            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 <byte> src  = _buffer.AsSpan(start, containedLength);
                    Span <byte>         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
            if (shiftSize == 0)
            {
                _buffer[lenOffset] = (byte)containedLength;
                return;
            }

            // We're currently at the end, so ensure we have room for N more bytes.
            EnsureWriteCapacity(shiftSize);

            // Buffer.BlockCopy correctly does forward-overlapped, so use it.
            Buffer.BlockCopy(_buffer, start, _buffer, start + shiftSize, containedLength);

            int tmp = _offset;

            _offset = lenOffset;
            WriteLength(containedLength);
            Debug.Assert(_offset - lenOffset - 1 == shiftSize);
            _offset = tmp + shiftSize;
        }
Exemplo n.º 5
0
        private static int ProcessConstructedBitString(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            Span <byte> destination,
            BitStringCopyAction?copyAction,
            bool isIndefinite,
            out int lastUnusedBitCount,
            out int bytesRead)
        {
            lastUnusedBitCount = 0;
            bytesRead          = 0;
            int lastSegmentLength = MaxCERSegmentSize;

            ReadOnlySpan <byte> cur = source;
            Stack <(int Offset, int Length, bool Indefinite, int BytesRead)>?readerStack = null;
            int         totalLength = 0;
            Asn1Tag     tag         = Asn1Tag.ConstructedBitString;
            Span <byte> 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 <byte> encodedValue = Slice(cur, headerLength, length.Value);

                        ParsePrimitiveBitStringContents(
                            encodedValue,
                            ruleSet,
                            out lastUnusedBitCount,
                            out ReadOnlySpan <byte> 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 <byte> 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);
                        }

                        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 <byte> tmpSpan = source.Slice(topOffset, topLength);
                    cur = tmpSpan.Slice(bytesRead);

                    isIndefinite = wasIndefinite;
                    bytesRead   += pushedBytesRead;
                }
                else
                {
                    return(totalLength);
                }
            }
        }
Exemplo n.º 6
0
        private static int CopyConstructedOctetString(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            Span <byte> destination,
            bool write,
            bool isIndefinite,
            out int bytesRead)
        {
            bytesRead = 0;
            int lastSegmentLength = MaxCERSegmentSize;

            ReadOnlySpan <byte> cur = source;
            Stack <(int Offset, int Length, bool IsIndefinite, int BytesRead)>?readerStack = null;
            int         totalLength = 0;
            Asn1Tag     tag         = Asn1Tag.ConstructedBitString;
            Span <byte> 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 <byte> 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 <byte> 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 <byte> topSpan = source.Slice(topOffset, topLength);

                    cur = topSpan.Slice(bytesRead);

                    isIndefinite = wasIndefinite;
                    bytesRead   += pushedBytesRead;
                }
                else
                {
                    return(totalLength);
                }
            }
        }
Exemplo n.º 7
0
        // T-REC-X.690-201508 sec 8.3
        private void WriteNonNegativeIntegerCore(Asn1Tag tag, ulong value)
        {
            int valueLength;

            // 0x80 needs two bytes: 0x00 0x80
            if (value < 0x80)
            {
                valueLength = 1;
            }
            else if (value < 0x8000)
            {
                valueLength = 2;
            }
            else if (value < 0x800000)
            {
                valueLength = 3;
            }
            else if (value < 0x80000000)
            {
                valueLength = 4;
            }
            else if (value < 0x80_00000000)
            {
                valueLength = 5;
            }
            else if (value < 0x8000_00000000)
            {
                valueLength = 6;
            }
            else if (value < 0x800000_00000000)
            {
                valueLength = 7;
            }
            else if (value < 0x80000000_00000000)
            {
                valueLength = 8;
            }
            else
            {
                valueLength = 9;
            }

            // Clear the constructed bit, if it was set.
            Debug.Assert(!tag.IsConstructed);
            WriteTag(tag);
            WriteLength(valueLength);

            ulong remaining = value;
            int   idx       = _offset + valueLength - 1;

            do
            {
                _buffer[idx] = (byte)remaining;
                remaining  >>= 8;
                idx--;
            } while (idx >= _offset);

#if DEBUG
            if (valueLength > 1)
            {
                // T-REC-X.690-201508 sec 8.3.2
                // Cannot start with 9 bits of 0 (or 9 bits of 1, but that's not this method).
                Debug.Assert(_buffer[_offset] != 0 || _buffer[_offset + 1] > 0x7F);
            }
#endif

            _offset += valueLength;
        }
Exemplo n.º 8
0
        // T-REC-X.690-201508 sec 8.3
        private void WriteIntegerCore(Asn1Tag tag, long value)
        {
            if (value >= 0)
            {
                WriteNonNegativeIntegerCore(tag, (ulong)value);
                return;
            }

            int valueLength;

            if (value >= sbyte.MinValue)
            {
                valueLength = 1;
            }
            else if (value >= short.MinValue)
            {
                valueLength = 2;
            }
            else if (value >= unchecked ((long)0xFFFFFFFF_FF800000))
            {
                valueLength = 3;
            }
            else if (value >= int.MinValue)
            {
                valueLength = 4;
            }
            else if (value >= unchecked ((long)0xFFFFFF80_00000000))
            {
                valueLength = 5;
            }
            else if (value >= unchecked ((long)0xFFFF8000_00000000))
            {
                valueLength = 6;
            }
            else if (value >= unchecked ((long)0xFF800000_00000000))
            {
                valueLength = 7;
            }
            else
            {
                valueLength = 8;
            }

            Debug.Assert(!tag.IsConstructed);
            WriteTag(tag);
            WriteLength(valueLength);

            long remaining = value;
            int  idx       = _offset + valueLength - 1;

            do
            {
                _buffer[idx] = (byte)remaining;
                remaining  >>= 8;
                idx--;
            } while (idx >= _offset);

#if DEBUG
            if (valueLength > 1)
            {
                // T-REC-X.690-201508 sec 8.3.2
                // Cannot start with 9 bits of 1 (or 9 bits of 0, but that's not this method).
                Debug.Assert(_buffer[_offset] != 0xFF || _buffer[_offset + 1] < 0x80);
            }
#endif

            _offset += valueLength;
        }