예제 #1
0
        private void WriteTag(Asn1Tag tag)
        {
            int spaceRequired = tag.CalculateEncodedSize();

            EnsureWriteCapacity(spaceRequired);

            if (!tag.TryEncode(_buffer.AsSpan(_offset, spaceRequired), out int written) ||
                written != spaceRequired)
            {
                Debug.Fail($"TryWrite failed or written was wrong value ({written} vs {spaceRequired})");
                throw new InvalidOperationException();
            }

            _offset += spaceRequired;
        }
예제 #2
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;
        }
예제 #3
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());
        }