// T-REC-X.690-201508 sec 9.2, 8.6 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); this.EnsureWriteCapacity(expectedSize); int savedOffset = this._offset; this.WriteTag(tag.AsConstructed()); // T-REC-X.690-201508 sec 9.1 // Constructed CER uses the indefinite form. this.WriteLength(-1); byte[] ensureNoExtraCopy = this._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 this.WriteTag(primitiveBitString); this.WriteLength(MaxCERSegmentSize); // 0 unused bits in this segment. this._buffer[this._offset] = 0; this._offset++; dest = this._buffer.AsSpan(this._offset); remainingData.Slice(0, MaxCERContentSize).CopyTo(dest); remainingData = remainingData.Slice(MaxCERContentSize); this._offset += MaxCERContentSize; } this.WriteTag(primitiveBitString); this.WriteLength(remainingData.Length + 1); this._buffer[this._offset] = (byte)unusedBitCount; this._offset++; dest = this._buffer.AsSpan(this._offset); remainingData.CopyTo(dest); this._offset += remainingData.Length; this.WriteEndOfContents(); Debug.Assert(this._offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {this._offset - savedOffset}"); Debug.Assert(this._buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(this.WriteConstructedCerBitString)}"); }
// T-REC-X.690-201508 sec 9.2, 8.6 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); WriteTag(tag.AsConstructed()); // T-REC-X.690-201508 sec 9.1 // Constructed CER uses the indefinite form. WriteLength(-1); int fullSegments = Math.DivRem(payload.Length, 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 int expectedSize = fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 2; EnsureWriteCapacity(expectedSize); byte[] ensureNoExtraCopy = _buffer; int savedOffset = _offset; 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); this.WriteTag(tag.AsConstructed()); this.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( 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize) == FullSegmentEncodedSize); 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; this.EnsureWriteCapacity(expectedSize); byte[] ensureNoExtraCopy = this._buffer; int savedOffset = this._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 this.WriteTag(primitiveOctetString); this.WriteLength(MaxCERSegmentSize); dest = this._buffer.AsSpan(this._offset); remainingData.Slice(0, MaxCERSegmentSize).CopyTo(dest); this._offset += MaxCERSegmentSize; remainingData = remainingData.Slice(MaxCERSegmentSize); } this.WriteTag(primitiveOctetString); this.WriteLength(remainingData.Length); dest = this._buffer.AsSpan(this._offset); remainingData.CopyTo(dest); this._offset += remainingData.Length; this.WriteEndOfContents(); Debug.Assert(this._offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {this._offset - savedOffset}"); Debug.Assert(this._buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(this.WriteConstructedCerOctetString)}"); }