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