summary refs log tree commit diff
path: root/Crypto/src/bcpg/ArmoredInputStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src/bcpg/ArmoredInputStream.cs')
-rw-r--r--Crypto/src/bcpg/ArmoredInputStream.cs517
1 files changed, 517 insertions, 0 deletions
diff --git a/Crypto/src/bcpg/ArmoredInputStream.cs b/Crypto/src/bcpg/ArmoredInputStream.cs
new file mode 100644
index 000000000..0dcc948d3
--- /dev/null
+++ b/Crypto/src/bcpg/ArmoredInputStream.cs
@@ -0,0 +1,517 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * reader for Base64 armored objects - read the headers and then start returning
+    * bytes when the data is reached. An IOException is thrown if the CRC check
+    * fails.
+    */
+    public class ArmoredInputStream
+        : BaseInputStream
+    {
+        /*
+        * set up the decoding table.
+        */
+        private readonly static byte[] decodingTable;
+        static ArmoredInputStream()
+        {
+            decodingTable = new byte[128];
+            for (int i = 'A'; i <= 'Z'; i++)
+            {
+                decodingTable[i] = (byte)(i - 'A');
+            }
+            for (int i = 'a'; i <= 'z'; i++)
+            {
+                decodingTable[i] = (byte)(i - 'a' + 26);
+            }
+            for (int i = '0'; i <= '9'; i++)
+            {
+                decodingTable[i] = (byte)(i - '0' + 52);
+            }
+            decodingTable['+'] = 62;
+            decodingTable['/'] = 63;
+        }
+
+        /**
+        * decode the base 64 encoded input data.
+        *
+        * @return the offset the data starts in out.
+        */
+        private int Decode(
+            int      in0,
+            int      in1,
+            int      in2,
+            int      in3,
+            int[]    result)
+        {
+            if (in3 < 0)
+            {
+                throw new EndOfStreamException("unexpected end of file in armored stream.");
+            }
+
+            int    b1, b2, b3, b4;
+            if (in2 == '=')
+            {
+                b1 = decodingTable[in0] &0xff;
+                b2 = decodingTable[in1] & 0xff;
+                result[2] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+                return 2;
+            }
+            else if (in3 == '=')
+            {
+                b1 = decodingTable[in0];
+                b2 = decodingTable[in1];
+                b3 = decodingTable[in2];
+                result[1] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+                result[2] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+                return 1;
+            }
+            else
+            {
+                b1 = decodingTable[in0];
+                b2 = decodingTable[in1];
+                b3 = decodingTable[in2];
+                b4 = decodingTable[in3];
+                result[0] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+                result[1] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+                result[2] = ((b3 << 6) | b4) & 0xff;
+                return 0;
+            }
+        }
+
+        Stream      input;
+        bool        start = true;
+        int[]       outBuf = new int[3];
+        int         bufPtr = 3;
+        Crc24       crc = new Crc24();
+        bool        crcFound = false;
+        bool        hasHeaders = true;
+        string      header = null;
+        bool        newLineFound = false;
+        bool        clearText = false;
+        bool        restart = false;
+        IList       headerList= Platform.CreateArrayList();
+        int         lastC = 0;
+		bool		isEndOfStream;
+
+        /**
+        * Create a stream for reading a PGP armoured message, parsing up to a header
+        * and then reading the data that follows.
+        *
+        * @param input
+        */
+        public ArmoredInputStream(
+            Stream input)
+			: this(input, true)
+        {
+        }
+
+		/**
+        * Create an armoured input stream which will assume the data starts
+        * straight away, or parse for headers first depending on the value of
+        * hasHeaders.
+        *
+        * @param input
+        * @param hasHeaders true if headers are to be looked for, false otherwise.
+        */
+        public ArmoredInputStream(
+            Stream	input,
+            bool	hasHeaders)
+        {
+            this.input = input;
+            this.hasHeaders = hasHeaders;
+
+			if (hasHeaders)
+            {
+                ParseHeaders();
+            }
+
+			start = false;
+        }
+
+		private bool ParseHeaders()
+        {
+            header = null;
+
+			int		c;
+            int		last = 0;
+            bool	headerFound = false;
+
+            headerList = Platform.CreateArrayList();
+
+			//
+            // if restart we already have a header
+            //
+            if (restart)
+            {
+                headerFound = true;
+            }
+            else
+            {
+                while ((c = input.ReadByte()) >= 0)
+                {
+                    if (c == '-' && (last == 0 || last == '\n' || last == '\r'))
+                    {
+                        headerFound = true;
+                        break;
+                    }
+
+					last = c;
+                }
+            }
+
+			if (headerFound)
+            {
+                StringBuilder    Buffer = new StringBuilder("-");
+                bool             eolReached = false;
+                bool             crLf = false;
+
+				if (restart)    // we've had to look ahead two '-'
+                {
+                    Buffer.Append('-');
+                }
+
+				while ((c = input.ReadByte()) >= 0)
+                {
+                    if (last == '\r' && c == '\n')
+                    {
+                        crLf = true;
+                    }
+                    if (eolReached && (last != '\r' && c == '\n'))
+                    {
+                        break;
+                    }
+                    if (eolReached && c == '\r')
+                    {
+                        break;
+                    }
+                    if (c == '\r' || (last != '\r' && c == '\n'))
+                    {
+						string line = Buffer.ToString();
+						if (line.Trim().Length < 1)
+							break;
+                        headerList.Add(line);
+                        Buffer.Length = 0;
+                    }
+
+                    if (c != '\n' && c != '\r')
+                    {
+                        Buffer.Append((char)c);
+                        eolReached = false;
+                    }
+                    else
+                    {
+                        if (c == '\r' || (last != '\r' && c == '\n'))
+                        {
+                            eolReached = true;
+                        }
+                    }
+
+					last = c;
+                }
+
+				if (crLf)
+                {
+                    input.ReadByte(); // skip last \n
+                }
+            }
+
+			if (headerList.Count > 0)
+            {
+                header = (string) headerList[0];
+            }
+
+			clearText = "-----BEGIN PGP SIGNED MESSAGE-----".Equals(header);
+            newLineFound = true;
+
+			return headerFound;
+        }
+
+		/**
+        * @return true if we are inside the clear text section of a PGP
+        * signed message.
+        */
+        public bool IsClearText()
+        {
+            return clearText;
+        }
+
+		/**
+		 * @return true if the stream is actually at end of file.
+		 */
+		public bool IsEndOfStream()
+		{
+			return isEndOfStream;
+		}
+
+		/**
+        * Return the armor header line (if there is one)
+        * @return the armor header line, null if none present.
+        */
+        public string GetArmorHeaderLine()
+        {
+            return header;
+        }
+
+		/**
+        * Return the armor headers (the lines after the armor header line),
+        * @return an array of armor headers, null if there aren't any.
+        */
+        public string[] GetArmorHeaders()
+        {
+            if (headerList.Count <= 1)
+            {
+                return null;
+            }
+
+			string[] hdrs = new string[headerList.Count - 1];
+            for (int i = 0; i != hdrs.Length; i++)
+            {
+                hdrs[i] = (string) headerList[i + 1];
+            }
+
+			return hdrs;
+        }
+
+		private int ReadIgnoreSpace()
+        {
+            int c;
+            do
+            {
+                c = input.ReadByte();
+            }
+            while (c == ' ' || c == '\t');
+
+			return c;
+        }
+
+		private int ReadIgnoreWhitespace()
+        {
+            int c;
+            do
+            {
+                c = input.ReadByte();
+            }
+            while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
+
+            return c;
+        }
+
+		private int ReadByteClearText()
+        {
+            int c = input.ReadByte();
+
+            if (c == '\r' || (c == '\n' && lastC != '\r'))
+            {
+                newLineFound = true;
+            }
+            else if (newLineFound && c == '-')
+            {
+                c = input.ReadByte();
+                if (c == '-')            // a header, not dash escaped
+                {
+                    clearText = false;
+                    start = true;
+                    restart = true;
+                }
+                else                   // a space - must be a dash escape
+                {
+                    c = input.ReadByte();
+                }
+                newLineFound = false;
+            }
+            else
+            {
+                if (c != '\n' && lastC != '\r')
+                {
+                    newLineFound = false;
+                }
+            }
+
+            lastC = c;
+
+			if (c < 0)
+			{
+				isEndOfStream = true;
+			}
+
+			return c;
+        }
+
+        private int ReadClearText(byte[] buffer, int offset, int count)
+        {
+            int pos = offset;
+            try
+            {
+                int end = offset + count;
+                while (pos < end)
+                {
+                    int c = ReadByteClearText();
+                    if (c == -1)
+                    {
+                        break;
+                    }
+                    buffer[pos++] = (byte) c;
+                }
+            }
+            catch (IOException ioe)
+            {
+                if (pos == offset) throw ioe;
+            }
+
+			return pos - offset;
+        }
+
+        private int DoReadByte()
+        {
+            if (bufPtr > 2 || crcFound)
+            {
+                int c = ReadIgnoreSpace();
+                if (c == '\n' || c == '\r')
+                {
+                    c = ReadIgnoreWhitespace();
+                    if (c == '=')            // crc reached
+                    {
+                        bufPtr = Decode(ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
+
+                        if (bufPtr != 0)
+                        {
+                            throw new IOException("no crc found in armored message.");
+                        }
+
+                        crcFound = true;
+
+                        int i = ((outBuf[0] & 0xff) << 16)
+                            | ((outBuf[1] & 0xff) << 8)
+                            | (outBuf[2] & 0xff);
+
+                        if (i != crc.Value)
+                        {
+                            throw new IOException("crc check failed in armored message.");
+                        }
+
+						return ReadByte();
+                    }
+
+                    if (c == '-')        // end of record reached
+                    {
+                        while ((c = input.ReadByte()) >= 0)
+                        {
+                            if (c == '\n' || c == '\r')
+                            {
+                                break;
+                            }
+                        }
+
+                        if (!crcFound)
+                        {
+                            throw new IOException("crc check not found.");
+                        }
+
+                        crcFound = false;
+                        start = true;
+                        bufPtr = 3;
+
+						if (c < 0)
+						{
+							isEndOfStream = true;
+						}
+
+						return -1;
+                    }
+                }
+
+                if (c < 0)
+                {
+					isEndOfStream = true;
+					return -1;
+                }
+
+                bufPtr = Decode(c, ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
+            }
+
+            return outBuf[bufPtr++];
+        }
+
+        public override int ReadByte()
+        {
+            if (start)
+            {
+                if (hasHeaders)
+                {
+                    ParseHeaders();
+                }
+
+				crc.Reset();
+				start = false;
+            }
+
+			if (clearText)
+            {
+                return ReadByteClearText();
+            }
+
+            int c = DoReadByte();
+
+            crc.Update(c);
+
+            return c;
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            if (start && count > 0)
+            {
+                if (hasHeaders)
+                {
+                    ParseHeaders();
+                }
+                start = false;
+            }
+
+            if (clearText)
+            {
+                return ReadClearText(buffer, offset, count);
+            }
+
+            int pos = offset;
+            try
+            {
+                int end = offset + count;
+                while (pos < end)
+                {
+                    int c = DoReadByte();
+                    crc.Update(c);
+                    if (c == -1)
+                    {
+                        break;
+                    }
+                    buffer[pos++] = (byte) c;
+                }
+            }
+            catch (IOException ioe)
+            {
+                if (pos == offset) throw ioe;
+            }
+
+            return pos - offset;
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                input.Dispose();
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}