private void WriteConstructedCerBitString(Asn1Tag tag, ReadOnlySpan <byte> payload, int unusedBitCount) { const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; // Every segment has an "unused bit count" byte. const int MaxCERContentSize = MaxCERSegmentSize - 1; Debug.Assert(payload.Length > MaxCERContentSize); int expectedSize = DetermineCerBitStringTotalLength(tag, payload.Length); EnsureWriteCapacity(expectedSize); int savedOffset = _offset; WriteTag(tag.AsConstructed()); // T-REC-X.690-201508 sec 9.1 // Constructed CER uses the indefinite form. WriteLength(-1); byte[] ensureNoExtraCopy = _buffer; ReadOnlySpan <byte> remainingData = payload; Span <byte> dest; Asn1Tag primitiveBitString = Asn1Tag.PrimitiveBitString; while (remainingData.Length > MaxCERContentSize) { // T-REC-X.690-201508 sec 8.6.4.1 WriteTag(primitiveBitString); WriteLength(MaxCERSegmentSize); // 0 unused bits in this segment. _buffer[_offset] = 0; _offset++; dest = _buffer.AsSpan(_offset); remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); remainingData = remainingData.Slice(MaxCERContentSize); _offset += MaxCERContentSize; } WriteTag(primitiveBitString); WriteLength(remainingData.Length + 1); _buffer[_offset] = (byte)unusedBitCount; _offset++; 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(WriteConstructedCerBitString)}"); }
// 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)}"); }