summary refs log tree commit diff
path: root/crypto/src/bcpg/BcpgInputStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/bcpg/BcpgInputStream.cs')
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs355
1 files changed, 355 insertions, 0 deletions
diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
new file mode 100644
index 000000000..3c69fbdf5
--- /dev/null
+++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -0,0 +1,355 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Reader for PGP objects.</remarks>
+    public class BcpgInputStream
+        : BaseInputStream
+    {
+        private Stream m_in;
+        private bool next = false;
+        private int nextB;
+
+        internal static BcpgInputStream Wrap(
+			Stream inStr)
+        {
+            if (inStr is BcpgInputStream)
+            {
+                return (BcpgInputStream) inStr;
+            }
+
+            return new BcpgInputStream(inStr);
+        }
+
+        private BcpgInputStream(
+			Stream inputStream)
+        {
+            this.m_in = inputStream;
+        }
+
+        public override int ReadByte()
+        {
+            if (next)
+            {
+                next = false;
+                return nextB;
+            }
+
+            return m_in.ReadByte();
+        }
+
+        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
+
+			if (nextB < 0)
+				return 0; // EndOfStream
+
+			if (buffer == null)
+				throw new ArgumentNullException("buffer");
+
+			buffer[offset] = (byte) nextB;
+			next = false;
+
+			return 1;
+        }
+
+		public byte[] ReadAll()
+        {
+			return Streams.ReadAll(this);
+		}
+
+		public void ReadFully(
+            byte[]	buffer,
+            int		off,
+            int		len)
+        {
+			if (Streams.ReadFully(this, buffer, off, len) < len)
+				throw new EndOfStreamException();
+        }
+
+		public void ReadFully(
+            byte[] buffer)
+        {
+            ReadFully(buffer, 0, buffer.Length);
+        }
+
+		/// <summary>Returns the next packet tag in the stream.</summary>
+        public PacketTag NextPacketTag()
+        {
+            if (!next)
+            {
+                try
+                {
+                    nextB = m_in.ReadByte();
+                }
+                catch (EndOfStreamException)
+                {
+                    nextB = -1;
+                }
+
+                next = true;
+            }
+
+            if (nextB >= 0)
+            {
+                if ((nextB & 0x40) != 0)    // new
+                {
+                    return (PacketTag)(nextB & 0x3f);
+                }
+                else    // old
+                {
+                    return (PacketTag)((nextB & 0x3f) >> 2);
+                }
+            }
+
+            return (PacketTag) nextB;
+        }
+
+        public Packet ReadPacket()
+        {
+            int hdr = this.ReadByte();
+
+            if (hdr < 0)
+            {
+                return null;
+            }
+
+            if ((hdr & 0x80) == 0)
+            {
+                throw new IOException("invalid header encountered");
+            }
+
+            bool newPacket = (hdr & 0x40) != 0;
+            PacketTag tag = 0;
+            int bodyLen = 0;
+            bool partial = false;
+
+            if (newPacket)
+            {
+                tag = (PacketTag)(hdr & 0x3f);
+
+                int l = this.ReadByte();
+
+                if (l < 192)
+                {
+                    bodyLen = l;
+                }
+                else if (l <= 223)
+                {
+                    int b = m_in.ReadByte();
+                    bodyLen = ((l - 192) << 8) + (b) + 192;
+                }
+                else if (l == 255)
+                {
+                    bodyLen = (m_in.ReadByte() << 24) | (m_in.ReadByte() << 16)
+                        |  (m_in.ReadByte() << 8)  | m_in.ReadByte();
+                }
+                else
+                {
+                    partial = true;
+                    bodyLen = 1 << (l & 0x1f);
+                }
+            }
+            else
+            {
+                int lengthType = hdr & 0x3;
+
+                tag = (PacketTag)((hdr & 0x3f) >> 2);
+
+                switch (lengthType)
+                {
+                    case 0:
+                        bodyLen = this.ReadByte();
+                        break;
+                    case 1:
+                        bodyLen = (this.ReadByte() << 8) | this.ReadByte();
+                        break;
+                    case 2:
+                        bodyLen = (this.ReadByte() << 24) | (this.ReadByte() << 16)
+                            | (this.ReadByte() << 8) | this.ReadByte();
+                        break;
+                    case 3:
+                        partial = true;
+                        break;
+                    default:
+                        throw new IOException("unknown length type encountered");
+                }
+            }
+
+            BcpgInputStream objStream;
+            if (bodyLen == 0 && partial)
+            {
+                objStream = this;
+            }
+            else
+            {
+                PartialInputStream pis = new PartialInputStream(this, partial, bodyLen);
+                objStream = new BcpgInputStream(pis);
+            }
+
+            switch (tag)
+            {
+                case PacketTag.Reserved:
+                    return new InputStreamPacket(objStream);
+                case PacketTag.PublicKeyEncryptedSession:
+                    return new PublicKeyEncSessionPacket(objStream);
+                case PacketTag.Signature:
+                    return new SignaturePacket(objStream);
+                case PacketTag.SymmetricKeyEncryptedSessionKey:
+                    return new SymmetricKeyEncSessionPacket(objStream);
+                case PacketTag.OnePassSignature:
+                    return new OnePassSignaturePacket(objStream);
+                case PacketTag.SecretKey:
+                    return new SecretKeyPacket(objStream);
+                case PacketTag.PublicKey:
+                    return new PublicKeyPacket(objStream);
+                case PacketTag.SecretSubkey:
+                    return new SecretSubkeyPacket(objStream);
+                case PacketTag.CompressedData:
+                    return new CompressedDataPacket(objStream);
+                case PacketTag.SymmetricKeyEncrypted:
+                    return new SymmetricEncDataPacket(objStream);
+                case PacketTag.Marker:
+                    return new MarkerPacket(objStream);
+                case PacketTag.LiteralData:
+                    return new LiteralDataPacket(objStream);
+                case PacketTag.Trust:
+                    return new TrustPacket(objStream);
+                case PacketTag.UserId:
+                    return new UserIdPacket(objStream);
+                case PacketTag.UserAttribute:
+                    return new UserAttributePacket(objStream);
+                case PacketTag.PublicSubkey:
+                    return new PublicSubkeyPacket(objStream);
+                case PacketTag.SymmetricEncryptedIntegrityProtected:
+                    return new SymmetricEncIntegrityPacket(objStream);
+                case PacketTag.ModificationDetectionCode:
+                    return new ModDetectionCodePacket(objStream);
+                case PacketTag.Experimental1:
+                case PacketTag.Experimental2:
+                case PacketTag.Experimental3:
+                case PacketTag.Experimental4:
+                    return new ExperimentalPacket(tag, objStream);
+                default:
+                    throw new IOException("unknown packet type encountered: " + tag);
+            }
+        }
+
+		public override void Close()
+		{
+			m_in.Close();
+			base.Close();
+		}
+
+		/// <summary>
+		/// A stream that overlays our input stream, allowing the user to only read a segment of it.
+		/// NB: dataLength will be negative if the segment length is in the upper range above 2**31.
+		/// </summary>
+		private class PartialInputStream
+            : BaseInputStream
+        {
+            private BcpgInputStream m_in;
+            private bool partial;
+            private int dataLength;
+
+            internal PartialInputStream(
+                BcpgInputStream	bcpgIn,
+                bool			partial,
+                int				dataLength)
+            {
+                this.m_in = bcpgIn;
+                this.partial = partial;
+                this.dataLength = dataLength;
+            }
+
+			public override int ReadByte()
+			{
+				do
+				{
+					if (dataLength != 0)
+					{
+						int ch = m_in.ReadByte();
+						if (ch < 0)
+						{
+							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
+						}
+						dataLength--;
+						return ch;
+					}
+				}
+				while (partial && ReadPartialDataLength() >= 0);
+
+				return -1;
+			}
+
+			public override int Read(byte[] buffer, int offset, int count)
+			{
+				do
+				{
+					if (dataLength != 0)
+					{
+						int readLen = (dataLength > count || dataLength < 0) ? count : dataLength;
+						int len = m_in.Read(buffer, offset, readLen);
+						if (len < 1)
+						{
+							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
+						}
+						dataLength -= len;
+						return len;
+					}
+				}
+				while (partial && ReadPartialDataLength() >= 0);
+
+				return 0;
+			}
+
+            private int ReadPartialDataLength()
+            {
+                int l = m_in.ReadByte();
+
+				if (l < 0)
+                {
+                    return -1;
+                }
+
+				partial = false;
+
+				if (l < 192)
+                {
+                    dataLength = l;
+                }
+                else if (l <= 223)
+                {
+                    dataLength = ((l - 192) << 8) + (m_in.ReadByte()) + 192;
+                }
+                else if (l == 255)
+                {
+                    dataLength = (m_in.ReadByte() << 24) | (m_in.ReadByte() << 16)
+                        |  (m_in.ReadByte() << 8)  | m_in.ReadByte();
+                }
+                else
+                {
+                    partial = true;
+                    dataLength = 1 << (l & 0x1f);
+                }
+
+                return 0;
+            }
+        }
+    }
+}