using System; using System.IO; using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Asn1 { public class BerOctetStringGenerator : BerGenerator { public BerOctetStringGenerator(Stream outStream) : base(outStream) { WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.OctetString); } public BerOctetStringGenerator(Stream outStream, int tagNo, bool isExplicit) : base(outStream, tagNo, isExplicit) { WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.OctetString); } /// The caller is responsible for disposing the returned before disposing /// this generator. public Stream GetOctetOutputStream() { return GetOctetOutputStream(new byte[1000]); // limit for CER encoding. } /// The caller is responsible for disposing the returned before disposing /// this generator. public Stream GetOctetOutputStream(int bufSize) { return bufSize < 1 ? GetOctetOutputStream() : GetOctetOutputStream(new byte[bufSize]); } /// The caller is responsible for disposing the returned before disposing /// this generator. public Stream GetOctetOutputStream(byte[] buf) { return new BufferedBerOctetStream(GetRawOutputStream(), buf); } private class BufferedBerOctetStream : BaseOutputStream { private byte[] _buf; private int _off; private readonly Asn1OutputStream _derOut; internal BufferedBerOctetStream(Stream outStream, byte[] buf) { _buf = buf; _off = 0; _derOut = Asn1OutputStream.Create(outStream, Asn1Encodable.Der, leaveOpen: true); } public override void Write(byte[] buffer, int offset, int count) { Streams.ValidateBufferArguments(buffer, offset, count); #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER Write(buffer.AsSpan(offset, count)); #else int bufLen = _buf.Length; int available = bufLen - _off; if (count < available) { Array.Copy(buffer, offset, _buf, _off, count); _off += count; return; } int pos = 0; if (_off > 0) { Array.Copy(buffer, offset, _buf, _off, available); pos = available; DerOctetString.Encode(_derOut, _buf, 0, bufLen); //_off = 0; } int remaining; while ((remaining = count - pos) >= bufLen) { DerOctetString.Encode(_derOut, buffer, offset + pos, bufLen); pos += bufLen; } Array.Copy(buffer, offset + pos, _buf, 0, remaining); this._off = remaining; #endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override void Write(ReadOnlySpan buffer) { int bufLen = _buf.Length; int available = bufLen - _off; if (buffer.Length < available) { buffer.CopyTo(_buf.AsSpan(_off)); _off += buffer.Length; return; } if (_off > 0) { DerOctetString.Encode(_derOut, _buf.AsSpan(0, _off), buffer[..available]); buffer = buffer[available..]; //_off = 0; } while (buffer.Length >= bufLen) { DerOctetString.Encode(_derOut, buffer[..bufLen]); buffer = buffer[bufLen..]; } buffer.CopyTo(_buf.AsSpan()); _off = buffer.Length; } #endif public override void WriteByte(byte value) { _buf[_off++] = value; if (_off == _buf.Length) { DerOctetString.Encode(_derOut, _buf, 0, _off); _off = 0; } } protected override void Dispose(bool disposing) { if (disposing) { if (_off != 0) { DerOctetString.Encode(_derOut, _buf, 0, _off); _off = 0; } _derOut.Dispose(); } base.Dispose(disposing); } } } }