summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-05-23 22:44:57 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-05-23 22:44:57 +0700
commit98b3ce6512812cd1d91c60ca0e69d69c32838d8e (patch)
tree8af69c097b389fe494e97401f7808590314a5b6b
parentAdd ValidateBufferArguments (diff)
downloadBouncyCastle.NET-ed25519-98b3ce6512812cd1d91c60ca0e69d69c32838d8e.tar.xz
Work on Stream classes
- consistent naming, ordering
- CanRead/Seek/Write fixes
- argument validation
- some cases of blocking zero-byte Read calls
-rw-r--r--crypto/bzip2/src/CBZip2InputStream.cs84
-rw-r--r--crypto/bzip2/src/CBZip2OutputStream.cs60
-rw-r--r--crypto/src/asn1/BEROctetStringGenerator.cs47
-rw-r--r--crypto/src/asn1/ConstructedBitStream.cs10
-rw-r--r--crypto/src/asn1/ConstructedOctetStream.cs10
-rw-r--r--crypto/src/bcpg/ArmoredInputStream.cs66
-rw-r--r--crypto/src/bcpg/ArmoredOutputStream.cs23
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs28
-rw-r--r--crypto/src/bcpg/BcpgOutputStream.cs75
-rw-r--r--crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs18
-rw-r--r--crypto/src/cms/CMSCompressedDataStreamGenerator.cs14
-rw-r--r--crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs14
-rw-r--r--crypto/src/cms/CMSSignedDataStreamGenerator.cs20
-rw-r--r--crypto/src/crypto/io/CipherStream.cs357
-rw-r--r--crypto/src/crypto/io/DigestSink.cs16
-rw-r--r--crypto/src/crypto/io/DigestStream.cs236
-rw-r--r--crypto/src/crypto/io/MacSink.cs16
-rw-r--r--crypto/src/crypto/io/MacStream.cs229
-rw-r--r--crypto/src/crypto/io/SignerSink.cs16
-rw-r--r--crypto/src/crypto/io/SignerStream.cs230
-rw-r--r--crypto/src/openpgp/PgpEncryptedData.cs54
-rw-r--r--crypto/src/tls/ByteQueueInputStream.cs16
-rw-r--r--crypto/src/tls/ByteQueueOutputStream.cs10
-rw-r--r--crypto/src/tls/TlsStream.cs10
-rw-r--r--crypto/src/tls/crypto/TlsHashSink.cs16
-rw-r--r--crypto/src/tls/crypto/TlsMacSink.cs16
-rw-r--r--crypto/src/util/io/BaseInputStream.cs43
-rw-r--r--crypto/src/util/io/BaseOutputStream.cs53
-rw-r--r--crypto/src/util/io/NullOutputStream.cs7
-rw-r--r--crypto/src/util/io/PushbackStream.cs24
-rw-r--r--crypto/src/util/io/TeeInputStream.cs6
-rw-r--r--crypto/src/util/io/TeeOutputStream.cs6
-rw-r--r--crypto/src/util/zlib/ZInputStream.cs246
-rw-r--r--crypto/src/util/zlib/ZOutputStream.cs272
34 files changed, 1054 insertions, 1294 deletions
diff --git a/crypto/bzip2/src/CBZip2InputStream.cs b/crypto/bzip2/src/CBZip2InputStream.cs
index 82f397d38..7144b6d06 100644
--- a/crypto/bzip2/src/CBZip2InputStream.cs
+++ b/crypto/bzip2/src/CBZip2InputStream.cs
@@ -26,6 +26,7 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Apache.Bzip2
 {
@@ -38,7 +39,8 @@ namespace Org.BouncyCastle.Apache.Bzip2
     * <b>NB:</b> note this class has been modified to read the leading BZ from the
     * start of the BZIP2 stream to make it compatible with other PGP programs.
     */
-    public class CBZip2InputStream : Stream 
+    public class CBZip2InputStream
+        : BaseInputStream 
 	{
         private static void Cadvise() {
             //System.out.Println("CRC Error");
@@ -172,6 +174,27 @@ namespace Org.BouncyCastle.Apache.Bzip2
             return a;
         }
 
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            /*
+             * TODO The base class implementation allows to return partial data if/when ReadByte throws. That would be
+             * be preferable here too (so don't override), but it would require that exceptions cause this instance to
+             * permanently fail, and that needs review.
+             */
+            int pos = 0;
+            while (pos < count)
+            {
+                int b = ReadByte();
+                if (b < 0)
+                    break;
+
+                buffer[offset + pos++] = (byte)b;
+            }
+            return pos;
+        }
+
         public override int ReadByte()
         {
             if (streamEnd)
@@ -911,62 +934,5 @@ namespace Org.BouncyCastle.Apache.Bzip2
             ll8 = new char[n];
             tt = new int[n];
         }
-    
-        public override void Flush() {
-        }
-    
-        public override int Read(byte[] buffer, int offset, int count) {
-            int c = -1;
-            int k;
-            for (k = 0; k < count; ++k) {
-                c = ReadByte();
-                if (c == -1)
-                    break;
-                buffer[k + offset] = (byte)c;
-            }
-            return k;
-        }
-    
-        public override long Seek(long offset, SeekOrigin origin) {
-            return 0;
-        }
-    
-        public override void SetLength(long value) {
-        }
-    
-        public override void Write(byte[] buffer, int offset, int count) {
-        }
-    
-        public override bool CanRead {
-            get {
-                return true;
-            }
-        }
-    
-        public override bool CanSeek {
-            get {
-                return false;
-            }
-        }
-    
-        public override bool CanWrite {
-            get {
-                return false;
-            }
-        }
-    
-        public override long Length {
-            get {
-                return 0;
-            }
-        }
-    
-        public override long Position {
-            get {
-                return 0;
-            }
-            set {
-            }
-        }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/bzip2/src/CBZip2OutputStream.cs b/crypto/bzip2/src/CBZip2OutputStream.cs
index e81f6ffc1..f56ca29b2 100644
--- a/crypto/bzip2/src/CBZip2OutputStream.cs
+++ b/crypto/bzip2/src/CBZip2OutputStream.cs
@@ -27,6 +27,7 @@ using System.Collections;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Apache.Bzip2
 {
@@ -40,7 +41,8 @@ namespace Org.BouncyCastle.Apache.Bzip2
     * <b>NB:</b> note this class has been modified to add a leading BZ to the
     * start of the BZIP2 stream to make it compatible with other PGP programs.
     */
-    public class CBZip2OutputStream : Stream 
+    public class CBZip2OutputStream
+        : BaseOutputStream 
 	{
         protected const int SETMASK = 1 << 21;
         protected const int CLEARMASK = ~SETMASK;
@@ -314,9 +316,9 @@ namespace Org.BouncyCastle.Apache.Bzip2
         * modified by Oliver Merkel, 010128
         *
         */
-        public override void WriteByte(byte b)
+        public override void WriteByte(byte value)
         {
-            if (currentByte == b)
+            if (currentByte == value)
             {
                 runLength++;
                 if (runLength > 254)
@@ -328,14 +330,14 @@ namespace Org.BouncyCastle.Apache.Bzip2
             }
             else if (currentByte == -1)
             {
-                currentByte = b;
+                currentByte = value;
                 runLength++;
             }
             else
             {
                 WriteRun();
                 runLength = 1;
-                currentByte = b;
+                currentByte = value;
             }
         }
 
@@ -1694,53 +1696,5 @@ namespace Org.BouncyCastle.Apache.Bzip2
 
             nMTF = wr;
         }
-
-        public override int Read(byte[] buffer, int offset, int count)
-        {
-            return 0;
-        }
-
-        public override long Seek(long offset, SeekOrigin origin)
-        {
-            return 0;
-        }
-
-        public override void SetLength(long value)
-        {
-        }
-
-        public override void Write(byte[] buffer, int offset, int count)
-        {
-            for (int k = 0; k < count; ++k)
-            {
-                WriteByte(buffer[k + offset]);
-            }
-        }
-
-        public override bool CanRead
-        {
-            get { return false; }
-        }
-
-        public override bool CanSeek
-        {
-            get { return false; }
-        }
-
-        public override bool CanWrite
-        {
-            get { return true; }
-        }
-
-        public override long Length
-        {
-            get { return 0; }
-        }
-
-        public override long Position
-        {
-            get { return 0; }
-            set {}
-        }
     }
 }
diff --git a/crypto/src/asn1/BEROctetStringGenerator.cs b/crypto/src/asn1/BEROctetStringGenerator.cs
index de0a6c0b8..bed40b566 100644
--- a/crypto/src/asn1/BEROctetStringGenerator.cs
+++ b/crypto/src/asn1/BEROctetStringGenerator.cs
@@ -60,48 +60,49 @@ namespace Org.BouncyCastle.Asn1
 				_derOut = Asn1OutputStream.Create(_gen.Out, Asn1Encodable.Der);
 			}
 
-			public override void WriteByte(
-				byte b)
+			public override void Write(byte[] buffer, int offset, int count)
 			{
-				_buf[_off++] = b;
+				Streams.ValidateBufferArguments(buffer, offset, count);
 
-				if (_off == _buf.Length)
-				{
-					DerOctetString.Encode(_derOut, _buf, 0, _off);
-					_off = 0;
-				}
-			}
-
-			public override void Write(byte[] b, int off, int len)
-			{
                 int bufLen = _buf.Length;
                 int available = bufLen - _off;
-                if (len < available)
+                if (count < available)
                 {
-                    Array.Copy(b, off, _buf, _off, len);
-                    _off += len;
+                    Array.Copy(buffer, offset, _buf, _off, count);
+                    _off += count;
                     return;
                 }
 
-                int count = 0;
+                int pos = 0;
                 if (_off > 0)
                 {
-                    Array.Copy(b, off, _buf, _off, available);
-                    count += available;
+                    Array.Copy(buffer, offset, _buf, _off, available);
+                    pos += available;
                     DerOctetString.Encode(_derOut, _buf, 0, bufLen);
                 }
 
                 int remaining;
-                while ((remaining = len - count) >= bufLen)
+                while ((remaining = count - pos) >= bufLen)
                 {
-                    DerOctetString.Encode(_derOut, b, off + count, bufLen);
-                    count += bufLen;
+                    DerOctetString.Encode(_derOut, buffer, offset + pos, bufLen);
+                    pos += bufLen;
                 }
 
-                Array.Copy(b, off + count, _buf, 0, remaining);
+                Array.Copy(buffer, offset + pos, _buf, 0, remaining);
                 this._off = remaining;
             }
 
+			public override void WriteByte(byte value)
+			{
+				_buf[_off++] = value;
+
+				if (_off == _buf.Length)
+				{
+					DerOctetString.Encode(_derOut, _buf, 0, _off);
+					_off = 0;
+				}
+			}
+
 #if PORTABLE
             protected override void Dispose(bool disposing)
             {
@@ -119,7 +120,7 @@ namespace Org.BouncyCastle.Asn1
                 base.Dispose(disposing);
             }
 #else
-            public override void Close()
+			public override void Close()
 			{
 				if (_off != 0)
 				{
diff --git a/crypto/src/asn1/ConstructedBitStream.cs b/crypto/src/asn1/ConstructedBitStream.cs
index 7c9e7c9e4..49f54fc1b 100644
--- a/crypto/src/asn1/ConstructedBitStream.cs
+++ b/crypto/src/asn1/ConstructedBitStream.cs
@@ -29,9 +29,11 @@ namespace Org.BouncyCastle.Asn1
             get { return m_padBits; }
         }
 
-        public override int Read(byte[] buf, int off, int len)
+        public override int Read(byte[] buffer, int offset, int count)
         {
-            if (len < 1)
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            if (count < 1)
                 return 0;
 
             if (m_currentStream == null)
@@ -51,13 +53,13 @@ namespace Org.BouncyCastle.Asn1
 
             for (;;)
             {
-                int numRead = m_currentStream.Read(buf, off + totalRead, len - totalRead);
+                int numRead = m_currentStream.Read(buffer, offset + totalRead, count - totalRead);
 
                 if (numRead > 0)
                 {
                     totalRead += numRead;
 
-                    if (totalRead == len)
+                    if (totalRead == count)
                         return totalRead;
                 }
                 else
diff --git a/crypto/src/asn1/ConstructedOctetStream.cs b/crypto/src/asn1/ConstructedOctetStream.cs
index 5541939b6..12aa14e74 100644
--- a/crypto/src/asn1/ConstructedOctetStream.cs
+++ b/crypto/src/asn1/ConstructedOctetStream.cs
@@ -18,9 +18,11 @@ namespace Org.BouncyCastle.Asn1
 			m_parser = parser;
 		}
 
-		public override int Read(byte[] buf, int off, int len)
+		public override int Read(byte[] buffer, int offset, int count)
 		{
-            if (len < 1)
+			Streams.ValidateBufferArguments(buffer, offset, count);
+
+			if (count < 1)
                 return 0;
 
 			if (m_currentStream == null)
@@ -40,13 +42,13 @@ namespace Org.BouncyCastle.Asn1
 
 			for (;;)
 			{
-				int numRead = m_currentStream.Read(buf, off + totalRead, len - totalRead);
+				int numRead = m_currentStream.Read(buffer, offset + totalRead, count - totalRead);
 
 				if (numRead > 0)
 				{
 					totalRead += numRead;
 
-					if (totalRead == len)
+					if (totalRead == count)
 						return totalRead;
 				}
 				else
diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs
index 2895c379a..cb5c2f91f 100644
--- a/crypto/src/bcpg/ArmoredInputStream.cs
+++ b/crypto/src/bcpg/ArmoredInputStream.cs
@@ -311,6 +311,26 @@ namespace Org.BouncyCastle.Bcpg
             return c;
         }
 
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            /*
+             * TODO Currently can't return partial data when exception thrown (breaking test case), so we don't inherit
+             * the base class implementation. Probably the reason is that throws don't mark this instance as 'failed'.
+             */
+            int pos = 0;
+            while (pos < count)
+            {
+                int b = ReadByte();
+                if (b < 0)
+                    break;
+
+                buffer[offset + pos++] = (byte)b;
+            }
+            return pos;
+        }
+
         public override int ReadByte()
         {
             if (start)
@@ -463,52 +483,6 @@ namespace Org.BouncyCastle.Bcpg
             return c;
         }
 
-        /**
-         * Reads up to <code>len</code> bytes of data from the input stream into
-         * an array of bytes.  An attempt is made to read as many as
-         * <code>len</code> bytes, but a smaller number may be read.
-         * The number of bytes actually read is returned as an integer.
-         *
-         * The first byte read is stored into element <code>b[off]</code>, the
-         * next one into <code>b[off+1]</code>, and so on. The number of bytes read
-         * is, at most, equal to <code>len</code>.
-         *
-         * NOTE: We need to override the custom behavior of Java's {@link InputStream#read(byte[], int, int)},
-         * as the upstream method silently swallows {@link IOException IOExceptions}.
-         * This would cause CRC checksum errors to go unnoticed.
-         *
-         * @see <a href="https://github.com/bcgit/bc-java/issues/998">Related BC bug report</a>
-         * @param b byte array
-         * @param off offset at which we start writing data to the array
-         * @param len number of bytes we write into the array
-         * @return total number of bytes read into the buffer
-         *
-         * @throws IOException if an exception happens AT ANY POINT
-         */
-        public override int Read(byte[] b, int off, int len)
-        {
-            CheckIndexSize(b.Length, off, len);
-
-            int pos = 0;
-            while (pos < len)
-            {
-                int c = ReadByte();
-                if (c < 0)
-                    break;
-
-                b[off + pos++] = (byte)c;
-            }
-            return pos;
-        }
-
-        private void CheckIndexSize(int size, int off, int len)
-        {
-            if (off < 0 || len < 0)
-                throw new IndexOutOfRangeException("Offset and length cannot be negative.");
-            if (off > size - len)
-                throw new IndexOutOfRangeException("Invalid offset and length.");
-        }
-
 #if PORTABLE
         protected override void Dispose(bool disposing)
         {
diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs
index 0df5d1141..97bcbde51 100644
--- a/crypto/src/bcpg/ArmoredOutputStream.cs
+++ b/crypto/src/bcpg/ArmoredOutputStream.cs
@@ -239,45 +239,44 @@ namespace Org.BouncyCastle.Bcpg
             clearText = false;
         }
 
-        public override void WriteByte(
-            byte b)
+        public override void WriteByte(byte value)
         {
             if (clearText)
             {
-                outStream.WriteByte(b);
+                outStream.WriteByte(value);
 
                 if (newLine)
                 {
-                    if (!(b == '\n' && lastb == '\r'))
+                    if (!(value == '\n' && lastb == '\r'))
                     {
                         newLine = false;
                     }
-                    if (b == '-')
+                    if (value == '-')
                     {
                         outStream.WriteByte((byte)' ');
                         outStream.WriteByte((byte)'-');      // dash escape
                     }
                 }
-                if (b == '\r' || (b == '\n' && lastb != '\r'))
+                if (value == '\r' || (value == '\n' && lastb != '\r'))
                 {
                     newLine = true;
                 }
-                lastb = b;
+                lastb = value;
                 return;
             }
 
             if (start)
             {
-                bool newPacket = (b & 0x40) != 0;
+                bool newPacket = (value & 0x40) != 0;
 
                 int tag;
                 if (newPacket)
                 {
-                    tag = b & 0x3f;
+                    tag = value & 0x3f;
                 }
                 else
                 {
-                    tag = (b & 0x3f) >> 2;
+                    tag = (value & 0x3f) >> 2;
                 }
 
                 switch ((PacketTag)tag)
@@ -334,8 +333,8 @@ namespace Org.BouncyCastle.Bcpg
                 }
             }
 
-            crc.Update(b);
-            buf[bufPtr++] = b & 0xff;
+            crc.Update(value);
+            buf[bufPtr++] = value & 0xff;
         }
 
         /**
diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
index 5efef193a..87c3f3812 100644
--- a/crypto/src/bcpg/BcpgInputStream.cs
+++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -1,7 +1,6 @@
 using System;
 using System.IO;
 
-using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
@@ -43,33 +42,22 @@ namespace Org.BouncyCastle.Bcpg
             return m_in.ReadByte();
         }
 
-        public override int Read(
-			byte[]	buffer,
-			int		offset,
-			int		count)
+        public override int Read(byte[] buffer, int offset, int count)
         {
-			// Strangely, when count == 0, we should still attempt to read a byte
-//			if (count == 0)
-//				return 0;
-
 			if (!next)
 				return m_in.Read(buffer, offset, count);
 
-			// We have next byte waiting, so return it
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
 			if (nextB < 0)
-				return 0; // EndOfStream
-
-			if (buffer == null)
-				throw new ArgumentNullException("buffer");
-
-			buffer[offset] = (byte) nextB;
-			next = false;
+				return 0;
 
-			return 1;
+            buffer[offset] = (byte)nextB;
+            next = false;
+            return 1;
         }
 
-		public byte[] ReadAll()
+        public byte[] ReadAll()
         {
 			return Streams.ReadAll(this);
 		}
@@ -323,6 +311,8 @@ namespace Org.BouncyCastle.Bcpg
 
 			public override int Read(byte[] buffer, int offset, int count)
 			{
+                Streams.ValidateBufferArguments(buffer, offset, count);
+
 				do
 				{
 					if (dataLength != 0)
diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs
index 738c28211..fbce0820e 100644
--- a/crypto/src/bcpg/BcpgOutputStream.cs
+++ b/crypto/src/bcpg/BcpgOutputStream.cs
@@ -215,8 +215,7 @@ namespace Org.BouncyCastle.Bcpg
             }
         }
 
-        private void PartialFlush(
-            bool isLast)
+        private void PartialFlush(bool isLast)
         {
             if (isLast)
             {
@@ -232,79 +231,75 @@ namespace Org.BouncyCastle.Bcpg
             partialOffset = 0;
         }
 
-		private void WritePartial(
-            byte b)
+        private void PartialWrite(byte[] buffer, int offset, int count)
         {
-            if (partialOffset == partialBufferLength)
-            {
-                PartialFlush(false);
-            }
-
-			partialBuffer[partialOffset++] = b;
-        }
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
-		private void WritePartial(
-            byte[]	buffer,
-            int		off,
-            int		len)
-        {
             if (partialOffset == partialBufferLength)
             {
                 PartialFlush(false);
             }
 
-            if (len <= (partialBufferLength - partialOffset))
+            if (count <= (partialBufferLength - partialOffset))
             {
-                Array.Copy(buffer, off, partialBuffer, partialOffset, len);
-                partialOffset += len;
+                Array.Copy(buffer, offset, partialBuffer, partialOffset, count);
+                partialOffset += count;
             }
             else
             {
                 int diff = partialBufferLength - partialOffset;
-                Array.Copy(buffer, off, partialBuffer, partialOffset, diff);
-                off += diff;
-                len -= diff;
+                Array.Copy(buffer, offset, partialBuffer, partialOffset, diff);
+                offset += diff;
+                count -= diff;
                 PartialFlush(false);
-                while (len > partialBufferLength)
+                while (count > partialBufferLength)
                 {
-                    Array.Copy(buffer, off, partialBuffer, 0, partialBufferLength);
-                    off += partialBufferLength;
-                    len -= partialBufferLength;
+                    Array.Copy(buffer, offset, partialBuffer, 0, partialBufferLength);
+                    offset += partialBufferLength;
+                    count -= partialBufferLength;
                     PartialFlush(false);
                 }
-                Array.Copy(buffer, off, partialBuffer, 0, len);
-                partialOffset += len;
+                Array.Copy(buffer, offset, partialBuffer, 0, count);
+                partialOffset += count;
             }
         }
-        public override void WriteByte(
-			byte value)
+
+        private void PartialWriteByte(byte value)
+        {
+            if (partialOffset == partialBufferLength)
+            {
+                PartialFlush(false);
+            }
+
+            partialBuffer[partialOffset++] = value;
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
         {
             if (partialBuffer != null)
             {
-                WritePartial(value);
+                PartialWrite(buffer, offset, count);
             }
             else
             {
-                outStr.WriteByte(value);
+                outStr.Write(buffer, offset, count);
             }
         }
-        public override void Write(
-            byte[]	buffer,
-            int		offset,
-            int		count)
+
+        public override void WriteByte(byte value)
         {
             if (partialBuffer != null)
             {
-                WritePartial(buffer, offset, count);
+                PartialWriteByte(value);
             }
             else
             {
-                outStr.Write(buffer, offset, count);
+                outStr.WriteByte(value);
             }
         }
 
-		// Additional helper methods to write primitive types
-		internal virtual void WriteShort(
+        // Additional helper methods to write primitive types
+        internal virtual void WriteShort(
 			short n)
 		{
 			this.Write(
diff --git a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
index b77758d1f..4ac2b34c9 100644
--- a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
@@ -238,18 +238,14 @@ namespace Org.BouncyCastle.Cms
 				this.eiGen = eiGen;
 			}
 
-			public override void WriteByte(
-				byte b)
-			{
-				macStream.WriteByte(b);
-			}
+            public override void Write(byte[] buffer, int offset, int count)
+            {
+                macStream.Write(buffer, offset, count);
+            }
 
-			public override void Write(
-				byte[]	bytes,
-				int		off,
-				int		len)
+			public override void WriteByte(byte value)
 			{
-				macStream.Write(bytes, off, len);
+				macStream.WriteByte(value);
 			}
 
 #if PORTABLE
@@ -274,7 +270,7 @@ namespace Org.BouncyCastle.Cms
                 base.Dispose(disposing);
             }
 #else
-            public override void Close()
+			public override void Close()
 			{
                 Platform.Dispose(macStream);
 
diff --git a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
index 0cb1bb641..1a9513ce6 100644
--- a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
@@ -111,18 +111,14 @@ namespace Org.BouncyCastle.Cms
 				_eiGen = eiGen;
 			}
 
-			public override void WriteByte(
-				byte b)
+			public override void Write(byte[] buffer, int offset, int count)
 			{
-				_out.WriteByte(b);
+				_out.Write(buffer, offset, count);
 			}
 
-			public override void Write(
-				byte[]	bytes,
-				int		off,
-				int		len)
+			public override void WriteByte(byte value)
 			{
-				_out.Write(bytes, off, len);
+				_out.WriteByte(value);
 			}
 
 #if PORTABLE
@@ -141,7 +137,7 @@ namespace Org.BouncyCastle.Cms
                 base.Dispose(disposing);
             }
 #else
-            public override void Close()
+			public override void Close()
 			{
                 Platform.Dispose(_out);
 
diff --git a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
index e0822aa8b..c08b30787 100644
--- a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
@@ -241,18 +241,14 @@ namespace Org.BouncyCastle.Cms
 				_eiGen = eiGen;
 			}
 
-			public override void WriteByte(
-				byte b)
+			public override void Write(byte[] buffer, int offset, int count)
 			{
-				_out.WriteByte(b);
+				_out.Write(buffer, offset, count);
 			}
 
-			public override void Write(
-				byte[]	bytes,
-				int		off,
-				int		len)
+			public override void WriteByte(byte value)
 			{
-				_out.Write(bytes, off, len);
+				_out.WriteByte(value);
 			}
 
 #if PORTABLE
@@ -281,7 +277,7 @@ namespace Org.BouncyCastle.Cms
                 base.Dispose(disposing);
             }
 #else
-            public override void Close()
+			public override void Close()
 			{
                 Platform.Dispose(_out);
 
diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
index e32f95d42..a6835f279 100644
--- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -795,19 +795,15 @@ namespace Org.BouncyCastle.Cms
                 _eiGen = eiGen;
             }
 
-			public override void WriteByte(
-                byte b)
-            {
-                _out.WriteByte(b);
-            }
+			public override void Write(byte[] buffer, int offset, int count)
+			{
+				_out.Write(buffer, offset, count);
+			}
 
-			public override void Write(
-                byte[]	bytes,
-                int		off,
-                int		len)
-            {
-                _out.Write(bytes, off, len);
-            }
+			public override void WriteByte(byte value)
+			{
+				_out.WriteByte(value);
+			}
 
 #if PORTABLE
             protected override void Dispose(bool disposing)
diff --git a/crypto/src/crypto/io/CipherStream.cs b/crypto/src/crypto/io/CipherStream.cs
index b5e683075..06a3d392c 100644
--- a/crypto/src/crypto/io/CipherStream.cs
+++ b/crypto/src/crypto/io/CipherStream.cs
@@ -3,202 +3,58 @@ using System.Diagnostics;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
     public class CipherStream
-		: Stream
+        : Stream
     {
         internal Stream stream;
         internal IBufferedCipher inCipher, outCipher;
         private byte[] mInBuf;
-		private int mInPos;
-		private bool inStreamEnded;
+        private int mInPos;
+        private bool inStreamEnded;
 
-		public CipherStream(
-            Stream			stream,
-            IBufferedCipher	readCipher,
-            IBufferedCipher	writeCipher)
+        public CipherStream(Stream stream, IBufferedCipher readCipher, IBufferedCipher writeCipher)
         {
             this.stream = stream;
 
-			if (readCipher != null)
-			{
-				this.inCipher = readCipher;
-				mInBuf = null;
-			}
-
-			if (writeCipher != null)
-			{
-				this.outCipher = writeCipher;
-			}
-		}
-
-		public IBufferedCipher ReadCipher
-		{
-			get { return inCipher; }
-		}
-
-		public IBufferedCipher WriteCipher
-		{
-			get { return outCipher; }
-		}
-
-		public override int ReadByte()
-        {
-            if (inCipher == null)
-                return stream.ReadByte();
-
-			if (mInBuf == null || mInPos >= mInBuf.Length)
-			{
-				if (!FillInBuf())
-					return -1;
-            }
-
-			return mInBuf[mInPos++];
-        }
-
-		public override int Read(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-        {
-            if (inCipher == null)
-                return stream.Read(buffer, offset, count);
-
-			int num = 0;
-			while (num < count)
-			{
-				if (mInBuf == null || mInPos >= mInBuf.Length)
-				{
-					if (!FillInBuf())
-						break;
-				}
-
-				int numToCopy = System.Math.Min(count - num, mInBuf.Length - mInPos);
-				Array.Copy(mInBuf, mInPos, buffer, offset + num, numToCopy);
-				mInPos += numToCopy;
-				num += numToCopy;
-			}
-
-			return num;
-		}
-
-		private bool FillInBuf()
-        {
-			if (inStreamEnded)
-				return false;
-
-			mInPos = 0;
-
-			do
-			{
-				mInBuf = ReadAndProcessBlock();
-			}
-			while (!inStreamEnded && mInBuf == null);
-
-			return mInBuf != null;
-		}
-
-		private byte[] ReadAndProcessBlock()
-		{
-			int blockSize = inCipher.GetBlockSize();
-			int readSize = (blockSize == 0) ? 256 : blockSize;
-
-			byte[] block = new byte[readSize];
-			int numRead = 0;
-			do
-			{
-				int count = stream.Read(block, numRead, block.Length - numRead);
-				if (count < 1)
-				{
-					inStreamEnded = true;
-					break;
-				}
-				numRead += count;
-			}
-			while (numRead < block.Length);
-
-			Debug.Assert(inStreamEnded || numRead == block.Length);
-
-			byte[] bytes = inStreamEnded
-				?	inCipher.DoFinal(block, 0, numRead)
-				:	inCipher.ProcessBytes(block);
-
-			if (bytes != null && bytes.Length == 0)
-			{
-				bytes = null;
-			}
-
-			return bytes;
-		}
-
-		public override void Write(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-        {
-			Debug.Assert(buffer != null);
-			Debug.Assert(0 <= offset && offset <= buffer.Length);
-			Debug.Assert(count >= 0);
-
-			int end = offset + count;
-
-			Debug.Assert(0 <= end && end <= buffer.Length);
-
-			if (outCipher == null)
+            if (readCipher != null)
             {
-                stream.Write(buffer, offset, count);
-                return;
+                this.inCipher = readCipher;
+                mInBuf = null;
             }
 
-			byte[] data = outCipher.ProcessBytes(buffer, offset, count);
-			if (data != null)
-			{
-				stream.Write(data, 0, data.Length);
-			}
-		}
-
-		public override void WriteByte(
-			byte b)
-        {
-            if (outCipher == null)
+            if (writeCipher != null)
             {
-                stream.WriteByte(b);
-                return;
+                this.outCipher = writeCipher;
             }
+        }
 
-			byte[] data = outCipher.ProcessByte(b);
-			if (data != null)
-			{
-				stream.Write(data, 0, data.Length);
-			}
-		}
+        public IBufferedCipher ReadCipher
+        {
+            get { return inCipher; }
+        }
 
-		public override bool CanRead
+        public IBufferedCipher WriteCipher
         {
-            get { return stream.CanRead && (inCipher != null); }
+            get { return outCipher; }
         }
 
-		public override bool CanWrite
+        public override bool CanRead
         {
-            get { return stream.CanWrite && (outCipher != null); }
+            get { return stream.CanRead; }
         }
 
-		public override bool CanSeek
+        public sealed override bool CanSeek
         {
             get { return false; }
         }
 
-		public sealed override long Length
-		{
-			get { throw new NotSupportedException(); }
-		}
-
-		public sealed override long Position
+        public override bool CanWrite
         {
-            get { throw new NotSupportedException(); }
-            set { throw new NotSupportedException(); }
+            get { return stream.CanWrite; }
         }
 
 #if PORTABLE
@@ -219,12 +75,12 @@ namespace Org.BouncyCastle.Crypto.IO
 #else
         public override void Close()
         {
-			if (outCipher != null)
-			{
-				byte[] data = outCipher.DoFinal();
-				stream.Write(data, 0, data.Length);
-				stream.Flush();
-			}
+            if (outCipher != null)
+            {
+                byte[] data = outCipher.DoFinal();
+                stream.Write(data, 0, data.Length);
+                stream.Flush();
+            }
             Platform.Dispose(stream);
             base.Close();
         }
@@ -232,21 +88,152 @@ namespace Org.BouncyCastle.Crypto.IO
 
         public override void Flush()
         {
-			// Note: outCipher.DoFinal is only called during Close()
-			stream.Flush();
+            // Note: outCipher.DoFinal is only called during Close()
+            stream.Flush();
+        }
+
+        public sealed override long Length
+        {
+            get { throw new NotSupportedException(); }
+        }
+
+        public sealed override long Position
+        {
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            if (inCipher == null)
+                return stream.Read(buffer, offset, count);
+
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            int num = 0;
+            while (num < count)
+            {
+                if (mInBuf == null || mInPos >= mInBuf.Length)
+                {
+                    if (!FillInBuf())
+                        break;
+                }
+
+                int numToCopy = System.Math.Min(count - num, mInBuf.Length - mInPos);
+                Array.Copy(mInBuf, mInPos, buffer, offset + num, numToCopy);
+                mInPos += numToCopy;
+                num += numToCopy;
+            }
+
+            return num;
+        }
+
+        public override int ReadByte()
+        {
+            if (inCipher == null)
+                return stream.ReadByte();
+
+            if (mInBuf == null || mInPos >= mInBuf.Length)
+            {
+                if (!FillInBuf())
+                    return -1;
+            }
+
+            return mInBuf[mInPos++];
+        }
+
+        public sealed override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotSupportedException();
+        }
+
+        public sealed override void SetLength(long length)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            if (outCipher == null)
+            {
+                stream.Write(buffer, offset, count);
+                return;
+            }
+
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            if (count > 0)
+            {
+                byte[] data = outCipher.ProcessBytes(buffer, offset, count);
+                if (data != null)
+                {
+                    stream.Write(data, 0, data.Length);
+                }
+            }
+        }
+
+        public override void WriteByte(byte value)
+        {
+            if (outCipher == null)
+            {
+                stream.WriteByte(value);
+                return;
+            }
+
+            byte[] data = outCipher.ProcessByte(value);
+            if (data != null)
+            {
+                stream.Write(data, 0, data.Length);
+            }
+        }
+
+        private bool FillInBuf()
+        {
+            if (inStreamEnded)
+                return false;
+
+            mInPos = 0;
+
+            do
+            {
+                mInBuf = ReadAndProcessBlock();
+            }
+            while (!inStreamEnded && mInBuf == null);
+
+            return mInBuf != null;
         }
 
-		public sealed override long Seek(
-			long		offset,
-			SeekOrigin	origin)
-		{
-			throw new NotSupportedException();
-		}
+        private byte[] ReadAndProcessBlock()
+        {
+            int blockSize = inCipher.GetBlockSize();
+            int readSize = (blockSize == 0) ? 256 : blockSize;
+
+            byte[] block = new byte[readSize];
+            int numRead = 0;
+            do
+            {
+                int count = stream.Read(block, numRead, block.Length - numRead);
+                if (count < 1)
+                {
+                    inStreamEnded = true;
+                    break;
+                }
+                numRead += count;
+            }
+            while (numRead < block.Length);
 
-		public sealed override void SetLength(
-			long length)
-		{
-			throw new NotSupportedException();
-		}
+            Debug.Assert(inStreamEnded || numRead == block.Length);
+
+            byte[] bytes = inStreamEnded
+                ? inCipher.DoFinal(block, 0, numRead)
+                : inCipher.ProcessBytes(block);
+
+            if (bytes != null && bytes.Length == 0)
+            {
+                bytes = null;
+            }
+
+            return bytes;
+        }
     }
 }
diff --git a/crypto/src/crypto/io/DigestSink.cs b/crypto/src/crypto/io/DigestSink.cs
index 98307e5f7..c2a168bfe 100644
--- a/crypto/src/crypto/io/DigestSink.cs
+++ b/crypto/src/crypto/io/DigestSink.cs
@@ -19,17 +19,19 @@ namespace Org.BouncyCastle.Crypto.IO
             get { return mDigest; }
         }
 
-        public override void WriteByte(byte b)
+        public override void Write(byte[] buffer, int offset, int count)
         {
-            mDigest.Update(b);
-        }
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
-        public override void Write(byte[] buf, int off, int len)
-        {
-            if (len > 0)
+            if (count > 0)
             {
-                mDigest.BlockUpdate(buf, off, len);
+                mDigest.BlockUpdate(buffer, offset, count);
             }
         }
+
+        public override void WriteByte(byte value)
+        {
+            mDigest.Update(value);
+        }
     }
 }
diff --git a/crypto/src/crypto/io/DigestStream.cs b/crypto/src/crypto/io/DigestStream.cs
index dce875792..387f42766 100644
--- a/crypto/src/crypto/io/DigestStream.cs
+++ b/crypto/src/crypto/io/DigestStream.cs
@@ -5,112 +5,44 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
-	public class DigestStream
-		: Stream
-	{
-		protected readonly Stream stream;
-		protected readonly IDigest inDigest;
-		protected readonly IDigest outDigest;
-
-		public DigestStream(
-			Stream	stream,
-			IDigest	readDigest,
-			IDigest	writeDigest)
-		{
-			this.stream = stream;
-			this.inDigest = readDigest;
-			this.outDigest = writeDigest;
-		}
-
-		public virtual IDigest ReadDigest()
-		{
-			return inDigest;
-		}
-
-		public virtual IDigest WriteDigest()
-		{
-			return outDigest;
-		}
-
-		public override int Read(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-		{
-			int n = stream.Read(buffer, offset, count);
-			if (inDigest != null)
-			{
-				if (n > 0)
-				{
-					inDigest.BlockUpdate(buffer, offset, n);
-				}
-			}
-			return n;
-		}
-
-		public override int ReadByte()
-		{
-			int b = stream.ReadByte();
-			if (inDigest != null)
-			{
-				if (b >= 0)
-				{
-					inDigest.Update((byte)b);
-				}
-			}
-			return b;
-		}
-
-		public override void Write(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-		{
-			if (outDigest != null)
-			{
-				if (count > 0)
-				{
-					outDigest.BlockUpdate(buffer, offset, count);
-				}
-			}
-			stream.Write(buffer, offset, count);
-		}
-
-		public override void WriteByte(
-			byte b)
-		{
-			if (outDigest != null)
-			{
-				outDigest.Update(b);
-			}
-			stream.WriteByte(b);
-		}
-
-		public override bool CanRead
-		{
-			get { return stream.CanRead; }
-		}
-
-		public override bool CanWrite
-		{
-			get { return stream.CanWrite; }
-		}
-
-		public override bool CanSeek
-		{
-			get { return stream.CanSeek; }
-		}
-
-		public override long Length
-		{
-			get { return stream.Length; }
-		}
-
-		public override long Position
-		{
-			get { return stream.Position; }
-			set { stream.Position = value; }
-		}
+    public class DigestStream
+        : Stream
+    {
+        protected readonly Stream stream;
+        protected readonly IDigest inDigest;
+        protected readonly IDigest outDigest;
+
+        public DigestStream(Stream stream, IDigest readDigest, IDigest writeDigest)
+        {
+            this.stream = stream;
+            this.inDigest = readDigest;
+            this.outDigest = writeDigest;
+        }
+
+        public virtual IDigest ReadDigest()
+        {
+            return inDigest;
+        }
+
+        public virtual IDigest WriteDigest()
+        {
+            return outDigest;
+        }
+
+        public override bool CanRead
+        {
+            get { return stream.CanRead; }
+        }
+
+        public sealed override bool CanSeek
+        {
+            get { return false; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return stream.CanWrite; }
+        }
 
 #if PORTABLE
         protected override void Dispose(bool disposing)
@@ -122,30 +54,82 @@ namespace Org.BouncyCastle.Crypto.IO
             base.Dispose(disposing);
         }
 #else
-		public override void Close()
-		{
+        public override void Close()
+        {
             Platform.Dispose(stream);
             base.Close();
-		}
+        }
 #endif
 
         public override void Flush()
-		{
-			stream.Flush();
-		}
-
-		public override long Seek(
-			long		offset,
-			SeekOrigin	origin)
-		{
-			return stream.Seek(offset, origin);
-		}
-
-		public override void SetLength(
-			long length)
-		{
-			stream.SetLength(length);
-		}
-	}
+        {
+            stream.Flush();
+        }
+
+        public sealed override long Length
+        {
+            get { throw new NotSupportedException(); }
+        }
+
+        public sealed override long Position
+        {
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            int n = stream.Read(buffer, offset, count);
+
+            if (inDigest != null && n > 0)
+            {
+                inDigest.BlockUpdate(buffer, offset, n);
+            }
+
+            return n;
+        }
+
+        public override int ReadByte()
+        {
+            int b = stream.ReadByte();
+
+            if (inDigest != null && b >= 0)
+            {
+                inDigest.Update((byte)b);
+            }
+
+            return b;
+        }
+
+        public sealed override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotSupportedException();
+        }
+
+        public sealed override void SetLength(long length)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            stream.Write(buffer, offset, count);
+
+            if (outDigest != null && count > 0)
+            {
+                outDigest.BlockUpdate(buffer, offset, count);
+            }
+        }
+
+        public override void WriteByte(byte value)
+        {
+            stream.WriteByte(value);
+
+            if (outDigest != null)
+            {
+                outDigest.Update(value);
+            }
+        }
+    }
 }
 
diff --git a/crypto/src/crypto/io/MacSink.cs b/crypto/src/crypto/io/MacSink.cs
index c4fe7169a..aa72e9047 100644
--- a/crypto/src/crypto/io/MacSink.cs
+++ b/crypto/src/crypto/io/MacSink.cs
@@ -19,17 +19,19 @@ namespace Org.BouncyCastle.Crypto.IO
             get { return mMac; }
         }
 
-        public override void WriteByte(byte b)
+        public override void Write(byte[] buffer, int offset, int count)
         {
-            mMac.Update(b);
-        }
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
-        public override void Write(byte[] buf, int off, int len)
-        {
-            if (len > 0)
+            if (count > 0)
             {
-                mMac.BlockUpdate(buf, off, len);
+                mMac.BlockUpdate(buffer, offset, count);
             }
         }
+
+        public override void WriteByte(byte value)
+        {
+            mMac.Update(value);
+        }
     }
 }
diff --git a/crypto/src/crypto/io/MacStream.cs b/crypto/src/crypto/io/MacStream.cs
index d9b8323b5..c56e00d2c 100644
--- a/crypto/src/crypto/io/MacStream.cs
+++ b/crypto/src/crypto/io/MacStream.cs
@@ -5,111 +5,44 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
-	public class MacStream
-		: Stream
-	{
-		protected readonly Stream stream;
-		protected readonly IMac inMac;
-		protected readonly IMac outMac;
-
-		public MacStream(
-			Stream	stream,
-			IMac	readMac,
-			IMac	writeMac)
-		{
-			this.stream = stream;
-			this.inMac = readMac;
-			this.outMac = writeMac;
-		}
-
-		public virtual IMac ReadMac()
-		{
-			return inMac;
-		}
-
-		public virtual IMac WriteMac()
-		{
-			return outMac;
-		}
-
-		public override int Read(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-		{
-			int n = stream.Read(buffer, offset, count);
-			if (inMac != null)
-			{
-				if (n > 0)
-				{
-					inMac.BlockUpdate(buffer, offset, n);
-				}
-			}
-			return n;
-		}
-
-		public override int ReadByte()
-		{
-			int b = stream.ReadByte();
-			if (inMac != null)
-			{
-				if (b >= 0)
-				{
-					inMac.Update((byte)b);
-				}
-			}
-			return b;
-		}
-
-		public override void Write(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-		{
-			if (outMac != null)
-			{
-				if (count > 0)
-				{
-					outMac.BlockUpdate(buffer, offset, count);
-				}
-			}
-			stream.Write(buffer, offset, count);
-		}
-
-		public override void WriteByte(byte b)
-		{
-			if (outMac != null)
-			{
-				outMac.Update(b);
-			}
-			stream.WriteByte(b);
-		}
-
-		public override bool CanRead
-		{
-			get { return stream.CanRead; }
-		}
-
-		public override bool CanWrite
-		{
-			get { return stream.CanWrite; }
-		}
-
-		public override bool CanSeek
-		{
-			get { return stream.CanSeek; }
-		}
-
-		public override long Length
-		{
-			get { return stream.Length; }
-		}
-
-		public override long Position
-		{
-			get { return stream.Position; }
-			set { stream.Position = value; }
-		}
+    public class MacStream
+        : Stream
+    {
+        protected readonly Stream stream;
+        protected readonly IMac inMac;
+        protected readonly IMac outMac;
+
+        public MacStream(Stream stream, IMac readMac, IMac writeMac)
+        {
+            this.stream = stream;
+            this.inMac = readMac;
+            this.outMac = writeMac;
+        }
+
+        public virtual IMac ReadMac()
+        {
+            return inMac;
+        }
+
+        public virtual IMac WriteMac()
+        {
+            return outMac;
+        }
+
+        public override bool CanRead
+        {
+            get { return stream.CanRead; }
+        }
+
+        public sealed override bool CanSeek
+        {
+            get { return false; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return stream.CanWrite; }
+        }
 
 #if PORTABLE
         protected override void Dispose(bool disposing)
@@ -129,22 +62,74 @@ namespace Org.BouncyCastle.Crypto.IO
 #endif
 
         public override void Flush()
-		{
-			stream.Flush();
-		}
-
-		public override long Seek(
-			long		offset,
-			SeekOrigin	origin)
-		{
-			return stream.Seek(offset,origin);
-		}
-
-		public override void SetLength(
-			long length)
-		{
-			stream.SetLength(length);
-		}
-	}
+        {
+            stream.Flush();
+        }
+
+        public sealed override long Length
+        {
+            get { throw new NotSupportedException(); }
+        }
+
+        public sealed override long Position
+        {
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            int n = stream.Read(buffer, offset, count);
+
+            if (inMac != null && n > 0)
+            {
+                inMac.BlockUpdate(buffer, offset, n);
+            }
+
+            return n;
+        }
+
+        public override int ReadByte()
+        {
+            int b = stream.ReadByte();
+
+            if (inMac != null && b >= 0)
+            {
+                inMac.Update((byte)b);
+            }
+
+            return b;
+        }
+
+        public sealed override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotSupportedException();
+        }
+
+        public sealed override void SetLength(long length)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            stream.Write(buffer, offset, count);
+
+            if (outMac != null && count > 0)
+            {
+                outMac.BlockUpdate(buffer, offset, count);
+            }
+        }
+
+        public override void WriteByte(byte value)
+        {
+            stream.WriteByte(value);
+
+            if (outMac != null)
+            {
+                outMac.Update(value);
+            }
+        }
+    }
 }
 
diff --git a/crypto/src/crypto/io/SignerSink.cs b/crypto/src/crypto/io/SignerSink.cs
index c9bd8b9c8..3485d3cdc 100644
--- a/crypto/src/crypto/io/SignerSink.cs
+++ b/crypto/src/crypto/io/SignerSink.cs
@@ -19,17 +19,19 @@ namespace Org.BouncyCastle.Crypto.IO
             get { return mSigner; }
         }
 
-		public override void WriteByte(byte b)
+		public override void Write(byte[] buffer, int offset, int count)
 		{
-            mSigner.Update(b);
-		}
+			Streams.ValidateBufferArguments(buffer, offset, count);
 
-		public override void Write(byte[] buf, int off, int len)
-		{
-			if (len > 0)
+			if (count > 0)
 			{
-				mSigner.BlockUpdate(buf, off, len);
+				mSigner.BlockUpdate(buffer, offset, count);
 			}
 		}
+
+		public override void WriteByte(byte value)
+		{
+			mSigner.Update(value);
+		}
 	}
 }
diff --git a/crypto/src/crypto/io/SignerStream.cs b/crypto/src/crypto/io/SignerStream.cs
index 1e37c8d34..d25d7e285 100644
--- a/crypto/src/crypto/io/SignerStream.cs
+++ b/crypto/src/crypto/io/SignerStream.cs
@@ -5,112 +5,44 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
-	public class SignerStream
-		: Stream
-	{
-		protected readonly Stream stream;
-		protected readonly ISigner inSigner;
-		protected readonly ISigner outSigner;
-
-		public SignerStream(
-			Stream	stream,
-			ISigner	readSigner,
-			ISigner	writeSigner)
-		{
-			this.stream = stream;
-			this.inSigner = readSigner;
-			this.outSigner = writeSigner;
-		}
-
-		public virtual ISigner ReadSigner()
-		{
-			return inSigner;
-		}
-
-		public virtual ISigner WriteSigner()
-		{
-			return outSigner;
-		}
-
-		public override int Read(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-		{
-			int n = stream.Read(buffer, offset, count);
-			if (inSigner != null)
-			{
-				if (n > 0)
-				{
-					inSigner.BlockUpdate(buffer, offset, n);
-				}
-			}
-			return n;
-		}
-
-		public override int ReadByte()
-		{
-			int b = stream.ReadByte();
-			if (inSigner != null)
-			{
-				if (b >= 0)
-				{
-					inSigner.Update((byte)b);
-				}
-			}
-			return b;
-		}
-
-		public override void Write(
-			byte[]	buffer,
-			int		offset,
-			int		count)
-		{
-			if (outSigner != null)
-			{
-				if (count > 0)
-				{
-					outSigner.BlockUpdate(buffer, offset, count);
-				}
-			}
-			stream.Write(buffer, offset, count);
-		}
-
-		public override void WriteByte(
-			byte b)
-		{
-			if (outSigner != null)
-			{
-				outSigner.Update(b);
-			}
-			stream.WriteByte(b);
-		}
-
-		public override bool CanRead
-		{
-			get { return stream.CanRead; }
-		}
-
-		public override bool CanWrite
-		{
-			get { return stream.CanWrite; }
-		}
-
-		public override bool CanSeek
-		{
-			get { return stream.CanSeek; }
-		}
-
-		public override long Length
-		{
-			get { return stream.Length; }
-		}
-
-		public override long Position
-		{
-			get { return stream.Position; }
-			set { stream.Position = value; }
-		}
+    public class SignerStream
+        : Stream
+    {
+        protected readonly Stream stream;
+        protected readonly ISigner inSigner;
+        protected readonly ISigner outSigner;
+
+        public SignerStream(Stream stream, ISigner readSigner, ISigner writeSigner)
+        {
+            this.stream = stream;
+            this.inSigner = readSigner;
+            this.outSigner = writeSigner;
+        }
+
+        public virtual ISigner ReadSigner()
+        {
+            return inSigner;
+        }
+
+        public virtual ISigner WriteSigner()
+        {
+            return outSigner;
+        }
+
+        public override bool CanRead
+        {
+            get { return stream.CanRead; }
+        }
+
+        public sealed override bool CanSeek
+        {
+            get { return false; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return stream.CanWrite; }
+        }
 
 #if PORTABLE
         protected override void Dispose(bool disposing)
@@ -130,22 +62,74 @@ namespace Org.BouncyCastle.Crypto.IO
 #endif
 
         public override void Flush()
-		{
-			stream.Flush();
-		}
-
-		public override long Seek(
-			long		offset,
-			SeekOrigin	origin)
-		{
-			return stream.Seek(offset, origin);
-		}
-
-		public override void SetLength(
-			long length)
-		{
-			stream.SetLength(length);
-		}
-	}
+        {
+            stream.Flush();
+        }
+
+        public sealed override long Length
+        {
+            get { throw new NotSupportedException(); }
+        }
+
+        public sealed override long Position
+        {
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            int n = stream.Read(buffer, offset, count);
+
+            if (inSigner != null && n > 0)
+            {
+                inSigner.BlockUpdate(buffer, offset, n);
+            }
+
+            return n;
+        }
+
+        public override int ReadByte()
+        {
+            int b = stream.ReadByte();
+
+            if (inSigner != null && b >= 0)
+            {
+                inSigner.Update((byte)b);
+            }
+
+            return b;
+        }
+
+        public sealed override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotSupportedException();
+        }
+
+        public sealed override void SetLength(long length)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            stream.Write(buffer, offset, count);
+
+            if (outSigner != null && count > 0)
+            {
+                outSigner.BlockUpdate(buffer, offset, count);
+            }
+        }
+
+        public override void WriteByte(byte value)
+        {
+            stream.WriteByte(value);
+
+            if (outSigner != null)
+            {
+                outSigner.Update(value);
+            }
+        }
+    }
 }
 
diff --git a/crypto/src/openpgp/PgpEncryptedData.cs b/crypto/src/openpgp/PgpEncryptedData.cs
index 558e0b8a2..d3220fe86 100644
--- a/crypto/src/openpgp/PgpEncryptedData.cs
+++ b/crypto/src/openpgp/PgpEncryptedData.cs
@@ -50,41 +50,43 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				return bufEnd;
 			}
 
-			public override int ReadByte()
-			{
-				if (bufStart < bufEnd)
-					return lookAhead[bufStart++];
+            public override int Read(byte[] buffer, int offset, int count)
+            {
+				Streams.ValidateBufferArguments(buffer, offset, count);
 
-				if (FillBuffer() < 1)
-					return -1;
+                int avail = bufEnd - bufStart;
 
-				return lookAhead[bufStart++];
-			}
+                int pos = offset;
+                while (count > avail)
+                {
+                    Array.Copy(lookAhead, bufStart, buffer, pos, avail);
 
-			public override int Read(byte[] buf, int off, int len)
-			{
-				int avail = bufEnd - bufStart;
+                    bufStart += avail;
+                    pos += avail;
+                    count -= avail;
 
-				int pos = off;
-				while (len > avail)
-				{
-					Array.Copy(lookAhead, bufStart, buf, pos, avail);
+                    if ((avail = FillBuffer()) < 1)
+                        return pos - offset;
+                }
 
-					bufStart += avail;
-					pos += avail;
-					len -= avail;
+                Array.Copy(lookAhead, bufStart, buffer, pos, count);
+                bufStart += count;
 
-					if ((avail = FillBuffer()) < 1)
-						return pos - off;
-				}
+                return pos + count - offset;
+            }
 
-				Array.Copy(lookAhead, bufStart, buf, pos, len);
-				bufStart += len;
+            public override int ReadByte()
+            {
+                if (bufStart < bufEnd)
+                    return lookAhead[bufStart++];
 
-				return pos + len - off;
-			}
+                if (FillBuffer() < 1)
+                    return -1;
+
+                return lookAhead[bufStart++];
+            }
 
-			internal byte[] GetLookAhead()
+            internal byte[] GetLookAhead()
 			{
 				byte[] temp = new byte[LookAheadSize];
 				Array.Copy(lookAhead, bufStart, temp, 0, LookAheadSize);
diff --git a/crypto/src/tls/ByteQueueInputStream.cs b/crypto/src/tls/ByteQueueInputStream.cs
index b59b5d1e7..0b15071ad 100644
--- a/crypto/src/tls/ByteQueueInputStream.cs
+++ b/crypto/src/tls/ByteQueueInputStream.cs
@@ -31,6 +31,15 @@ namespace Org.BouncyCastle.Tls
             return bytesToRead;
         }
 
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            int bytesToRead = System.Math.Min(m_buffer.Available, count);
+            m_buffer.RemoveData(buffer, offset, bytesToRead, 0);
+            return bytesToRead;
+        }
+
         public override int ReadByte()
         {
             if (m_buffer.Available == 0)
@@ -39,13 +48,6 @@ namespace Org.BouncyCastle.Tls
             return m_buffer.RemoveData(1, 0)[0];
         }
 
-        public override int Read(byte[] buf, int off, int len)
-        {
-            int bytesToRead = System.Math.Min(m_buffer.Available, len);
-            m_buffer.RemoveData(buf, off, bytesToRead, 0);
-            return bytesToRead;
-        }
-
         public long Skip(long n)
         {
             int bytesToRemove = System.Math.Min((int)n, m_buffer.Available);
diff --git a/crypto/src/tls/ByteQueueOutputStream.cs b/crypto/src/tls/ByteQueueOutputStream.cs
index 76f04916f..441a3773a 100644
--- a/crypto/src/tls/ByteQueueOutputStream.cs
+++ b/crypto/src/tls/ByteQueueOutputStream.cs
@@ -20,14 +20,16 @@ namespace Org.BouncyCastle.Tls
             get { return m_buffer; }
         }
 
-        public override void WriteByte(byte b)
+        public override void Write(byte[] buffer, int offset, int count)
         {
-            m_buffer.AddData(new byte[]{ b }, 0, 1);
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            m_buffer.AddData(buffer, offset, count);
         }
 
-        public override void Write(byte[] buf, int off, int len)
+        public override void WriteByte(byte value)
         {
-            m_buffer.AddData(buf, off, len);
+            m_buffer.AddData(new byte[]{ value }, 0, 1);
         }
     }
 }
diff --git a/crypto/src/tls/TlsStream.cs b/crypto/src/tls/TlsStream.cs
index 02f8b733e..f3dea1574 100644
--- a/crypto/src/tls/TlsStream.cs
+++ b/crypto/src/tls/TlsStream.cs
@@ -10,12 +10,12 @@ namespace Org.BouncyCastle.Tls
 
         internal TlsStream(TlsProtocol handler)
         {
-            this.m_handler = handler;
+            m_handler = handler;
         }
 
         public override bool CanRead
         {
-            get { return !m_handler.IsClosed; }
+            get { return true; }
         }
 
         public override bool CanSeek
@@ -25,7 +25,7 @@ namespace Org.BouncyCastle.Tls
 
         public override bool CanWrite
         {
-            get { return !m_handler.IsClosed; }
+            get { return true; }
         }
 
 #if PORTABLE
@@ -88,9 +88,9 @@ namespace Org.BouncyCastle.Tls
             m_handler.WriteApplicationData(buffer, offset, count);
         }
 
-        public override void WriteByte(byte b)
+        public override void WriteByte(byte value)
         {
-            Write(new byte[]{ b }, 0, 1);
+            Write(new byte[]{ value }, 0, 1);
         }
     }
 }
diff --git a/crypto/src/tls/crypto/TlsHashSink.cs b/crypto/src/tls/crypto/TlsHashSink.cs
index 64496744d..a1681b0c8 100644
--- a/crypto/src/tls/crypto/TlsHashSink.cs
+++ b/crypto/src/tls/crypto/TlsHashSink.cs
@@ -19,17 +19,19 @@ namespace Org.BouncyCastle.Tls.Crypto
             get { return m_hash; }
         }
 
-        public override void WriteByte(byte b)
+        public override void Write(byte[] buffer, int offset, int count)
         {
-            m_hash.Update(new byte[] { b }, 0, 1);
-        }
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
-        public override void Write(byte[] buf, int off, int len)
-        {
-            if (len > 0)
+            if (count > 0)
             {
-                m_hash.Update(buf, off, len);
+                m_hash.Update(buffer, offset, count);
             }
         }
+
+        public override void WriteByte(byte value)
+        {
+            m_hash.Update(new byte[]{ value }, 0, 1);
+        }
     }
 }
diff --git a/crypto/src/tls/crypto/TlsMacSink.cs b/crypto/src/tls/crypto/TlsMacSink.cs
index 58e65c731..e7d5c70d7 100644
--- a/crypto/src/tls/crypto/TlsMacSink.cs
+++ b/crypto/src/tls/crypto/TlsMacSink.cs
@@ -19,17 +19,19 @@ namespace Org.BouncyCastle.Tls.Crypto
             get { return m_mac; }
         }
 
-        public override void WriteByte(byte b)
+        public override void Write(byte[] buffer, int offset, int count)
         {
-            m_mac.Update(new byte[]{ b }, 0, 1);
-        }
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
-        public override void Write(byte[] buf, int off, int len)
-        {
-            if (len > 0)
+            if (count > 0)
             {
-                m_mac.Update(buf, off, len);
+                m_mac.Update(buffer, offset, count);
             }
         }
+
+        public override void WriteByte(byte value)
+        {
+            m_mac.Update(new byte[]{ value }, 0, 1);
+        }
     }
 }
diff --git a/crypto/src/util/io/BaseInputStream.cs b/crypto/src/util/io/BaseInputStream.cs
index a5613d801..ebe256632 100644
--- a/crypto/src/util/io/BaseInputStream.cs
+++ b/crypto/src/util/io/BaseInputStream.cs
@@ -1,34 +1,15 @@
 using System;
-using System.Diagnostics;
 using System.IO;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
-    public abstract class BaseInputStream : Stream
+    public abstract class BaseInputStream
+        : Stream
     {
-		private bool closed;
-
-		public sealed override bool CanRead { get { return !closed; } }
+        public sealed override bool CanRead { get { return true; } }
         public sealed override bool CanSeek { get { return false; } }
         public sealed override bool CanWrite { get { return false; } }
 
-#if PORTABLE
-        protected override void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                closed = true;
-            }
-            base.Dispose(disposing);
-        }
-#else
-		public override void Close()
-        {
-            closed = true;
-            base.Close();
-        }
-#endif
-
         public sealed override void Flush() {}
         public sealed override long Length { get { throw new NotSupportedException(); } }
         public sealed override long Position
@@ -39,22 +20,26 @@ namespace Org.BouncyCastle.Utilities.IO
 
         public override int Read(byte[] buffer, int offset, int count)
         {
-            int pos = offset;
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            int pos = 0;
             try
             {
-                int end = offset + count;
-                while (pos < end)
+                while (pos < count)
                 {
                     int b = ReadByte();
-                    if (b == -1) break;
-                    buffer[pos++] = (byte) b;
+                    if (b < 0)
+                        break;
+
+                    buffer[offset + pos++] = (byte)b;
                 }
             }
             catch (IOException)
             {
-                if (pos == offset) throw;
+                if (pos == 0)
+                    throw;
             }
-            return pos - offset;
+            return pos;
         }
 
         public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
diff --git a/crypto/src/util/io/BaseOutputStream.cs b/crypto/src/util/io/BaseOutputStream.cs
index 0dbe821de..d9a5b92d6 100644
--- a/crypto/src/util/io/BaseOutputStream.cs
+++ b/crypto/src/util/io/BaseOutputStream.cs
@@ -1,35 +1,16 @@
 using System;
-using System.Diagnostics;
 using System.IO;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
-    public abstract class BaseOutputStream : Stream
+    public abstract class BaseOutputStream
+        : Stream
     {
-		private bool closed;
-
-		public sealed override bool CanRead { get { return false; } }
+        public sealed override bool CanRead { get { return false; } }
         public sealed override bool CanSeek { get { return false; } }
-        public sealed override bool CanWrite { get { return !closed; } }
-
-#if PORTABLE
-        protected override void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                closed = true;
-            }
-            base.Dispose(disposing);
-        }
-#else
-        public override void Close()
-        {
-            closed = true;
-            base.Close();
-        }
-#endif
+        public sealed override bool CanWrite { get { return true; } }
 
-        public override void Flush() { }
+        public override void Flush() {}
         public sealed override long Length { get { throw new NotSupportedException(); } }
         public sealed override long Position
         {
@@ -42,28 +23,22 @@ namespace Org.BouncyCastle.Utilities.IO
 
         public override void Write(byte[] buffer, int offset, int count)
         {
-            Debug.Assert(buffer != null);
-            Debug.Assert(0 <= offset && offset <= buffer.Length);
-            Debug.Assert(count >= 0);
-
-            int end = offset + count;
+            Streams.ValidateBufferArguments(buffer, offset, count);
 
-            Debug.Assert(0 <= end && end <= buffer.Length);
-
-            for (int i = offset; i < end; ++i)
+            for (int i = 0; i < count; ++i)
             {
-                this.WriteByte(buffer[i]);
+                WriteByte(buffer[offset + i]);
             }
         }
 
-		public virtual void Write(params byte[] buffer)
-		{
-			Write(buffer, 0, buffer.Length);
-		}
+        public virtual void Write(params byte[] buffer)
+        {
+            Write(buffer, 0, buffer.Length);
+        }
 
-        public override void WriteByte(byte b)
+        public override void WriteByte(byte value)
         {
-            Write(new byte[]{ b }, 0, 1);
+            Write(new byte[]{ value }, 0, 1);
         }
     }
 }
diff --git a/crypto/src/util/io/NullOutputStream.cs b/crypto/src/util/io/NullOutputStream.cs
index 13877fa13..c435549d2 100644
--- a/crypto/src/util/io/NullOutputStream.cs
+++ b/crypto/src/util/io/NullOutputStream.cs
@@ -5,14 +5,13 @@ namespace Org.BouncyCastle.Utilities.IO
 	internal class NullOutputStream
 		: BaseOutputStream
 	{
-		public override void WriteByte(byte b)
+		public override void Write(byte[] buffer, int offset, int count)
 		{
-			// do nothing
+			Streams.ValidateBufferArguments(buffer, offset, count);
 		}
 
-		public override void Write(byte[] buffer, int offset, int count)
+		public override void WriteByte(byte value)
 		{
-			// do nothing
 		}
 	}
 }
diff --git a/crypto/src/util/io/PushbackStream.cs b/crypto/src/util/io/PushbackStream.cs
index d51e195ca..2ceb64361 100644
--- a/crypto/src/util/io/PushbackStream.cs
+++ b/crypto/src/util/io/PushbackStream.cs
@@ -13,31 +13,33 @@ namespace Org.BouncyCastle.Utilities.IO
 		{
 		}
 
-		public override int ReadByte()
+		public override int Read(byte[] buffer, int offset, int count)
 		{
+			Streams.ValidateBufferArguments(buffer, offset, count);
+
 			if (m_buf != -1)
 			{
-				int tmp = m_buf;
+				if (count < 1)
+					return 0;
+
+				buffer[offset] = (byte)m_buf;
 				m_buf = -1;
-				return tmp;
+				return 1;
 			}
 
-			return base.ReadByte();
+			return base.Read(buffer, offset, count);
 		}
 
-		public override int Read(byte[] buffer, int offset, int count)
+		public override int ReadByte()
 		{
-            if (count < 1)
-                return 0;
-
 			if (m_buf != -1)
 			{
-				buffer[offset] = (byte)m_buf;
+				int tmp = m_buf;
 				m_buf = -1;
-				return 1;
+				return tmp;
 			}
 
-			return base.Read(buffer, offset, count);
+			return base.ReadByte();
 		}
 
 		public virtual void Unread(int b)
diff --git a/crypto/src/util/io/TeeInputStream.cs b/crypto/src/util/io/TeeInputStream.cs
index 6996f3fbb..73ea8fed0 100644
--- a/crypto/src/util/io/TeeInputStream.cs
+++ b/crypto/src/util/io/TeeInputStream.cs
@@ -37,13 +37,13 @@ namespace Org.BouncyCastle.Utilities.IO
 		}
 #endif
 
-        public override int Read(byte[] buf, int off, int len)
+        public override int Read(byte[] buffer, int offset, int count)
 		{
-			int i = input.Read(buf, off, len);
+			int i = input.Read(buffer, offset, count);
 
 			if (i > 0)
 			{
-				tee.Write(buf, off, i);
+				tee.Write(buffer, offset, i);
 			}
 
 			return i;
diff --git a/crypto/src/util/io/TeeOutputStream.cs b/crypto/src/util/io/TeeOutputStream.cs
index a6c7fd5b5..5f447b18b 100644
--- a/crypto/src/util/io/TeeOutputStream.cs
+++ b/crypto/src/util/io/TeeOutputStream.cs
@@ -43,10 +43,10 @@ namespace Org.BouncyCastle.Utilities.IO
 			tee.Write(buffer, offset, count);
 		}
 
-		public override void WriteByte(byte b)
+		public override void WriteByte(byte value)
 		{
-			output.WriteByte(b);
-			tee.WriteByte(b);
+			output.WriteByte(value);
+			tee.WriteByte(value);
 		}
 	}
 }
diff --git a/crypto/src/util/zlib/ZInputStream.cs b/crypto/src/util/zlib/ZInputStream.cs
index ea803fa4f..3e6fcc1be 100644
--- a/crypto/src/util/zlib/ZInputStream.cs
+++ b/crypto/src/util/zlib/ZInputStream.cs
@@ -37,11 +37,13 @@ using System;
 using System.Diagnostics;
 using System.IO;
 
+using Org.BouncyCastle.Utilities.IO;
+
 namespace Org.BouncyCastle.Utilities.Zlib
 {
-	public class ZInputStream
-		: Stream
-	{
+    public class ZInputStream
+        : BaseInputStream
+    {
         private static ZStream GetDefaultZStream(bool nowrap)
         {
             ZStream z = new ZStream();
@@ -51,32 +53,32 @@ namespace Org.BouncyCastle.Utilities.Zlib
 
         private const int BufferSize = 4096;
 
-		protected ZStream z;
-		protected int flushLevel = JZlib.Z_NO_FLUSH;
-		// TODO Allow custom buf
-		protected byte[] buf = new byte[BufferSize];
-		protected byte[] buf1 = new byte[1];
-		protected bool compress;
+        protected ZStream z;
+        protected int flushLevel = JZlib.Z_NO_FLUSH;
+        // TODO Allow custom buf
+        protected byte[] buf = new byte[BufferSize];
+        protected byte[] buf1 = new byte[1];
+        protected bool compress;
 
-		protected Stream input;
-		protected bool closed;
+        protected Stream input;
+        protected bool closed;
 
-		private bool nomoreinput = false;
+        private bool nomoreinput = false;
 
-		public ZInputStream(Stream input)
-			: this(input, false)
-		{
-		}
+        public ZInputStream(Stream input)
+            : this(input, false)
+        {
+        }
 
-		public ZInputStream(Stream input, bool nowrap)
+        public ZInputStream(Stream input, bool nowrap)
             : this(input, GetDefaultZStream(nowrap))
-		{
-		}
+        {
+        }
 
         public ZInputStream(Stream input, ZStream z)
-			: base()
-		{
-			Debug.Assert(input.CanRead);
+            : base()
+        {
+            Debug.Assert(input.CanRead);
 
             if (z == null)
             {
@@ -92,36 +94,32 @@ namespace Org.BouncyCastle.Utilities.Zlib
             this.compress = (z.istate == null);
             this.z = z;
             this.z.next_in = buf;
-			this.z.next_in_index = 0;
-			this.z.avail_in = 0;
-		}
+            this.z.next_in_index = 0;
+            this.z.avail_in = 0;
+        }
 
         public ZInputStream(Stream input, int level)
             : this(input, level, false)
-		{
+        {
         }
 
         public ZInputStream(Stream input, int level, bool nowrap)
-		{
-			Debug.Assert(input.CanRead);
-			
-			this.input = input;
+        {
+            Debug.Assert(input.CanRead);
+
+            this.input = input;
             this.compress = true;
             this.z = new ZStream();
-			this.z.deflateInit(level, nowrap);
-			this.z.next_in = buf;
-			this.z.next_in_index = 0;
-			this.z.avail_in = 0;
-		}
+            this.z.deflateInit(level, nowrap);
+            this.z.next_in = buf;
+            this.z.next_in_index = 0;
+            this.z.avail_in = 0;
+        }
 
-		/*public int available() throws IOException {
+        /*public int available() throws IOException {
 		return inf.finished() ? 0 : 1;
 		}*/
 
-		public sealed override bool CanRead { get { return !closed; } }
-		public sealed override bool CanSeek { get { return false; } }
-		public sealed override bool CanWrite { get { return false; } }
-
 #if PORTABLE
         protected override void Dispose(bool disposing)
         {
@@ -137,102 +135,90 @@ namespace Org.BouncyCastle.Utilities.Zlib
         }
 #else
         public override void Close()
-		{
+        {
             if (closed)
                 return;
 
             closed = true;
             Platform.Dispose(input);
             base.Close();
-		}
+        }
 #endif
 
-		public sealed override void Flush() {}
-
-		public virtual int FlushMode
-		{
-			get { return flushLevel; }
-			set { this.flushLevel = value; }
-		}
-
-		public sealed override long Length { get { throw new NotSupportedException(); } }
-		public sealed override long Position
-		{
-			get { throw new NotSupportedException(); }
-			set { throw new NotSupportedException(); }
-		}
-
-		public override int Read(byte[]	b, int off, int len)
-		{
-			if (len==0)
-				return 0;
-
-			z.next_out = b;
-			z.next_out_index = off;
-			z.avail_out = len;
-
-			int err;
-			do
-			{
-				if (z.avail_in == 0 && !nomoreinput)
-				{
-					// if buffer is empty and more input is available, refill it
-					z.next_in_index = 0;
-					z.avail_in = input.Read(buf, 0, buf.Length); //(bufsize<z.avail_out ? bufsize : z.avail_out));
-
-					if (z.avail_in <= 0)
-					{
-						z.avail_in = 0;
-						nomoreinput = true;
-					}
-				}
-
-				err = compress
-					?	z.deflate(flushLevel)
-					:	z.inflate(flushLevel);
-
-				if (nomoreinput && err == JZlib.Z_BUF_ERROR)
-					return 0;
-				if (err != JZlib.Z_OK && err != JZlib.Z_STREAM_END)
-					// TODO
-//					throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg);
-					throw new IOException((compress ? "de" : "in") + "flating: " + z.msg);
-				if ((nomoreinput || err == JZlib.Z_STREAM_END) && z.avail_out == len)
-					return 0;
-			} 
-			while(z.avail_out == len && err == JZlib.Z_OK);
-			//Console.Error.WriteLine("("+(len-z.avail_out)+")");
-			return len - z.avail_out;
-		}
-
-		public override int ReadByte()
-		{
-			if (Read(buf1, 0, 1) <= 0)
-				return -1;
-			return buf1[0];
-		}
-
-//  public long skip(long n) throws IOException {
-//    int len=512;
-//    if(n<len)
-//      len=(int)n;
-//    byte[] tmp=new byte[len];
-//    return((long)read(tmp));
-//  }
-
-		public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
-		public sealed override void SetLength(long value) { throw new NotSupportedException(); }
-
-		public virtual long TotalIn
-		{
-			get { return z.total_in; }
-		}
-
-		public virtual long TotalOut
-		{
-			get { return z.total_out; }
-		}
-
-		public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
-	}
+        public virtual int FlushMode
+        {
+            get { return flushLevel; }
+            set { this.flushLevel = value; }
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            if (count == 0)
+                return 0;
+
+            z.next_out = buffer;
+            z.next_out_index = offset;
+            z.avail_out = count;
+
+            int err;
+            do
+            {
+                if (z.avail_in == 0 && !nomoreinput)
+                {
+                    // if buffer is empty and more input is available, refill it
+                    z.next_in_index = 0;
+                    z.avail_in = input.Read(buf, 0, buf.Length); //(bufsize<z.avail_out ? bufsize : z.avail_out));
+
+                    if (z.avail_in <= 0)
+                    {
+                        z.avail_in = 0;
+                        nomoreinput = true;
+                    }
+                }
+
+                err = compress
+                    ? z.deflate(flushLevel)
+                    : z.inflate(flushLevel);
+
+                if (nomoreinput && err == JZlib.Z_BUF_ERROR)
+                    return 0;
+                if (err != JZlib.Z_OK && err != JZlib.Z_STREAM_END)
+                    // TODO
+                    //throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg);
+                    throw new IOException((compress ? "de" : "in") + "flating: " + z.msg);
+                if ((nomoreinput || err == JZlib.Z_STREAM_END) && z.avail_out == count)
+                    return 0;
+            }
+            while (z.avail_out == count && err == JZlib.Z_OK);
+            //Console.Error.WriteLine("("+(len-z.avail_out)+")");
+            return count - z.avail_out;
+        }
+
+        public override int ReadByte()
+        {
+            if (Read(buf1, 0, 1) <= 0)
+                return -1;
+            return buf1[0];
+        }
+
+        //  public long skip(long n) throws IOException {
+        //    int len=512;
+        //    if(n<len)
+        //      len=(int)n;
+        //    byte[] tmp=new byte[len];
+        //    return((long)read(tmp));
+        //  }
+
+        public virtual long TotalIn
+        {
+            get { return z.total_in; }
+        }
+
+        public virtual long TotalOut
+        {
+            get { return z.total_out; }
+        }
+    }
 }
diff --git a/crypto/src/util/zlib/ZOutputStream.cs b/crypto/src/util/zlib/ZOutputStream.cs
index a1482a07f..dcb93f97b 100644
--- a/crypto/src/util/zlib/ZOutputStream.cs
+++ b/crypto/src/util/zlib/ZOutputStream.cs
@@ -37,11 +37,13 @@ using System;
 using System.Diagnostics;
 using System.IO;
 
+using Org.BouncyCastle.Utilities.IO;
+
 namespace Org.BouncyCastle.Utilities.Zlib
 {
-	public class ZOutputStream
-		: Stream
-	{
+    public class ZOutputStream
+        : BaseOutputStream
+    {
         private static ZStream GetDefaultZStream(bool nowrap)
         {
             ZStream z = new ZStream();
@@ -51,30 +53,30 @@ namespace Org.BouncyCastle.Utilities.Zlib
 
         private const int BufferSize = 4096;
 
-		protected ZStream z;
-		protected int flushLevel = JZlib.Z_NO_FLUSH;
-		// TODO Allow custom buf
-		protected byte[] buf = new byte[BufferSize];
-		protected byte[] buf1 = new byte[1];
-		protected bool compress;
+        protected ZStream z;
+        protected int flushLevel = JZlib.Z_NO_FLUSH;
+        // TODO Allow custom buf
+        protected byte[] buf = new byte[BufferSize];
+        protected byte[] buf1 = new byte[1];
+        protected bool compress;
 
-		protected Stream output;
-		protected bool closed;
+        protected Stream output;
+        protected bool closed;
 
         public ZOutputStream(Stream output)
             : this(output, false)
-		{
-		}
+        {
+        }
 
         public ZOutputStream(Stream output, bool nowrap)
             : this(output, GetDefaultZStream(nowrap))
-		{
-		}
+        {
+        }
 
         public ZOutputStream(Stream output, ZStream z)
-			: base()
-		{
-			Debug.Assert(output.CanWrite);
+            : base()
+        {
+            Debug.Assert(output.CanWrite);
 
             if (z == null)
             {
@@ -89,27 +91,23 @@ namespace Org.BouncyCastle.Utilities.Zlib
             this.output = output;
             this.compress = (z.istate == null);
             this.z = z;
-		}
+        }
 
         public ZOutputStream(Stream output, int level)
-			: this(output, level, false)
-		{
-		}
+            : this(output, level, false)
+        {
+        }
 
-		public ZOutputStream(Stream output, int level, bool nowrap)
-			: base()
-		{
-			Debug.Assert(output.CanWrite);
+        public ZOutputStream(Stream output, int level, bool nowrap)
+            : base()
+        {
+            Debug.Assert(output.CanWrite);
 
-			this.output = output;
+            this.output = output;
             this.compress = true;
             this.z = new ZStream();
-			this.z.deflateInit(level, nowrap);
-		}
-
-		public sealed override bool CanRead { get { return false; } }
-        public sealed override bool CanSeek { get { return false; } }
-        public sealed override bool CanWrite { get { return !closed; } }
+            this.z.deflateInit(level, nowrap);
+        }
 
 #if PORTABLE
         protected override void Dispose(bool disposing)
@@ -124,14 +122,14 @@ namespace Org.BouncyCastle.Utilities.Zlib
             base.Dispose(disposing);
         }
 #else
-		public override void Close()
-		{
-			if (closed)
-				return;
+        public override void Close()
+        {
+            if (closed)
+                return;
 
             DoClose();
             base.Close();
-		}
+        }
 #endif
 
         private void DoClose()
@@ -157,109 +155,101 @@ namespace Org.BouncyCastle.Utilities.Zlib
         }
 
         public virtual void End()
-		{
-			if (z == null)
-				return;
-			if (compress)
-				z.deflateEnd();
-			else
-				z.inflateEnd();
-			z.free();
-			z = null;
-		}
-
-		public virtual void Finish()
-		{
-			do
-			{
-				z.next_out = buf;
-				z.next_out_index = 0;
-				z.avail_out = buf.Length;
-
-				int err = compress
-					?	z.deflate(JZlib.Z_FINISH)
-					:	z.inflate(JZlib.Z_FINISH);
-
-				if (err != JZlib.Z_STREAM_END && err != JZlib.Z_OK)
-					// TODO
-//					throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg);
-					throw new IOException((compress ? "de" : "in") + "flating: " + z.msg);
-
-				int count = buf.Length - z.avail_out;
-				if (count > 0)
-				{
-					output.Write(buf, 0, count);
-				}
-			}
-			while (z.avail_in > 0 || z.avail_out == 0);
-
-			Flush();
-		}
-
-		public override void Flush()
-		{
-			output.Flush();
-		}
-
-		public virtual int FlushMode
-		{
-			get { return flushLevel; }
-			set { this.flushLevel = value; }
-		}
-
-        public sealed override long Length { get { throw new NotSupportedException(); } }
-        public sealed override long Position
         {
-            get { throw new NotSupportedException(); }
-            set { throw new NotSupportedException(); }
+            if (z == null)
+                return;
+            if (compress)
+                z.deflateEnd();
+            else
+                z.inflateEnd();
+            z.free();
+            z = null;
+        }
+
+        public virtual void Finish()
+        {
+            do
+            {
+                z.next_out = buf;
+                z.next_out_index = 0;
+                z.avail_out = buf.Length;
+
+                int err = compress
+                    ? z.deflate(JZlib.Z_FINISH)
+                    : z.inflate(JZlib.Z_FINISH);
+
+                if (err != JZlib.Z_STREAM_END && err != JZlib.Z_OK)
+                    // TODO
+                    //throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg);
+                    throw new IOException((compress ? "de" : "in") + "flating: " + z.msg);
+
+                int count = buf.Length - z.avail_out;
+                if (count > 0)
+                {
+                    output.Write(buf, 0, count);
+                }
+            }
+            while (z.avail_in > 0 || z.avail_out == 0);
+
+            Flush();
+        }
+
+        public override void Flush()
+        {
+            output.Flush();
+        }
+
+        public virtual int FlushMode
+        {
+            get { return flushLevel; }
+            set { this.flushLevel = value; }
+        }
+
+        public virtual long TotalIn
+        {
+            get { return z.total_in; }
+        }
+
+        public virtual long TotalOut
+        {
+            get { return z.total_out; }
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            Streams.ValidateBufferArguments(buffer, offset, count);
+
+            if (count == 0)
+                return;
+
+            z.next_in = buffer;
+            z.next_in_index = offset;
+            z.avail_in = count;
+
+            do
+            {
+                z.next_out = buf;
+                z.next_out_index = 0;
+                z.avail_out = buf.Length;
+
+                int err = compress
+                    ? z.deflate(flushLevel)
+                    : z.inflate(flushLevel);
+
+                if (err != JZlib.Z_OK)
+                    // TODO
+                    //throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg);
+                    throw new IOException((compress ? "de" : "in") + "flating: " + z.msg);
+
+                output.Write(buf, 0, buf.Length - z.avail_out);
+            }
+            while (z.avail_in > 0 || z.avail_out == 0);
+        }
+
+        public override void WriteByte(byte value)
+        {
+            buf1[0] = value;
+            Write(buf1, 0, 1);
         }
-        public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
-        public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
-        public sealed override void SetLength(long value) { throw new NotSupportedException(); }
-
-		public virtual long TotalIn
-		{
-			get { return z.total_in; }
-		}
-
-		public virtual long TotalOut
-		{
-			get { return z.total_out; }
-		}
-
-		public override void Write(byte[] b, int off, int len)
-		{
-			if (len == 0)
-				return;
-
-			z.next_in = b;
-			z.next_in_index = off;
-			z.avail_in = len;
-
-			do
-			{
-				z.next_out = buf;
-				z.next_out_index = 0;
-				z.avail_out = buf.Length;
-
-				int err = compress
-					?	z.deflate(flushLevel)
-					:	z.inflate(flushLevel);
-
-				if (err != JZlib.Z_OK)
-					// TODO
-//					throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg);
-					throw new IOException((compress ? "de" : "in") + "flating: " + z.msg);
-
-				output.Write(buf, 0, buf.Length - z.avail_out);
-			}
-			while (z.avail_in > 0 || z.avail_out == 0);
-		}
-
-		public override void WriteByte(byte b)
-		{
-			buf1[0] = b;
-			Write(buf1, 0, 1);
-		}
-	}
+    }
 }