summary refs log tree commit diff
path: root/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
blob: 0e320aa9f3996375e317abe5cbd6b315bd701089 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System;
using System.IO;

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;

namespace Org.BouncyCastle.Cms
{
	/**
	* General class for generating a compressed CMS message stream.
	* <p>
	* A simple example of usage.
	* </p>
	* <pre>
	*      CMSCompressedDataStreamGenerator gen = new CMSCompressedDataStreamGenerator();
	*
	*      Stream cOut = gen.Open(outputStream, CMSCompressedDataStreamGenerator.ZLIB);
	*
	*      cOut.Write(data);
	*
	*      cOut.Close();
	* </pre>
	*/
	public class CmsCompressedDataStreamGenerator
	{
        public static readonly string ZLib = CmsObjectIdentifiers.ZlibCompress.Id;

        private int _bufferSize;

		/**
		* base constructor
		*/
		public CmsCompressedDataStreamGenerator()
		{
		}

		/**
		* Set the underlying string size for encapsulated data
		*
		* @param bufferSize length of octet strings to buffer the data.
		*/
		public void SetBufferSize(int bufferSize)
		{
			_bufferSize = bufferSize;
		}

        public Stream Open(Stream outStream)
        {
            return Open(outStream, CmsObjectIdentifiers.Data.Id, ZLib);
        }

        public Stream Open(Stream outStream, string compressionOid)
		{
			return Open(outStream, CmsObjectIdentifiers.Data.Id, compressionOid);
		}

		public Stream Open(Stream outStream, string contentOid, string compressionOid)
		{
			if (ZLib != compressionOid)
				throw new ArgumentException("Unsupported compression algorithm: " + compressionOid,
					nameof(compressionOid));

			BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);

			sGen.AddObject(CmsObjectIdentifiers.CompressedData);

			//
			// Compressed Data
			//
			BerSequenceGenerator cGen = new BerSequenceGenerator(
				sGen.GetRawOutputStream(), 0, true);

			// CMSVersion
			cGen.AddObject(DerInteger.Zero);

			// CompressionAlgorithmIdentifier
			cGen.AddObject(new AlgorithmIdentifier(CmsObjectIdentifiers.ZlibCompress));

			//
			// Encapsulated ContentInfo
			//
			BerSequenceGenerator eiGen = new BerSequenceGenerator(cGen.GetRawOutputStream());

			eiGen.AddObject(new DerObjectIdentifier(contentOid));

            BerOctetStringGenerator octGen = new BerOctetStringGenerator(eiGen.GetRawOutputStream(), 0, true);
            Stream octetStream = octGen.GetOctetOutputStream(_bufferSize);

            return new CmsCompressedOutputStream(
				Utilities.IO.Compression.ZLib.CompressOutput(octetStream, -1), sGen, cGen, eiGen, octGen);
		}

		private class CmsCompressedOutputStream
			: BaseOutputStream
		{
			private Stream _out;
			private BerSequenceGenerator _sGen;
			private BerSequenceGenerator _cGen;
			private BerSequenceGenerator _eiGen;
			private BerOctetStringGenerator _octGen;

            internal CmsCompressedOutputStream(
				Stream					outStream,
				BerSequenceGenerator	sGen,
				BerSequenceGenerator	cGen,
				BerSequenceGenerator	eiGen,
                BerOctetStringGenerator octGen)
			{
				_out = outStream;
				_sGen = sGen;
				_cGen = cGen;
				_eiGen = eiGen;
				_octGen = octGen;
			}

			public override void Write(byte[] buffer, int offset, int count)
			{
				_out.Write(buffer, offset, count);
			}

#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
            public override void Write(ReadOnlySpan<byte> buffer)
			{
                _out.Write(buffer);
            }
#endif

            public override void WriteByte(byte value)
			{
				_out.WriteByte(value);
			}

            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    _out.Dispose();

					// TODO Parent context(s) should really be be closed explicitly

					_octGen.Dispose();
                    _eiGen.Dispose();
				    _cGen.Dispose();
				    _sGen.Dispose();
                }
                base.Dispose(disposing);
            }
		}
	}
}