summary refs log tree commit diff
path: root/crypto/src/bcpg/ArmoredOutputStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/bcpg/ArmoredOutputStream.cs')
-rw-r--r--crypto/src/bcpg/ArmoredOutputStream.cs330
1 files changed, 330 insertions, 0 deletions
diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs
new file mode 100644
index 000000000..b3a32c6f5
--- /dev/null
+++ b/crypto/src/bcpg/ArmoredOutputStream.cs
@@ -0,0 +1,330 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic output stream.
+    */
+    public class ArmoredOutputStream
+        : BaseOutputStream
+    {
+        private static readonly byte[] encodingTable =
+        {
+            (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+            (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+            (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+            (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+            (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+            (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+            (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+            (byte)'v',
+            (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+            (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+            (byte)'7', (byte)'8', (byte)'9',
+            (byte)'+', (byte)'/'
+        };
+
+        /**
+         * encode the input data producing a base 64 encoded byte array.
+         */
+        private static void Encode(
+            Stream	outStream,
+            int[]	data,
+            int		len)
+        {
+			Debug.Assert(len > 0);
+			Debug.Assert(len < 4);
+
+			byte[] bs = new byte[4];
+			int d1 = data[0];
+			bs[0] = encodingTable[(d1 >> 2) & 0x3f];
+
+			switch (len)
+            {
+				case 1:
+				{
+					bs[1] = encodingTable[(d1 << 4) & 0x3f];
+					bs[2] = (byte)'=';
+					bs[3] = (byte)'=';
+					break;
+				}
+				case 2:
+				{
+					int d2 = data[1];
+					bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
+					bs[2] = encodingTable[(d2 << 2) & 0x3f];
+					bs[3] = (byte)'=';
+					break;
+				}
+				case 3:
+				{
+					int d2 = data[1];
+					int d3 = data[2];
+					bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
+					bs[2] = encodingTable[((d2 << 2) | (d3 >> 6)) & 0x3f];
+					bs[3] = encodingTable[d3 & 0x3f];
+					break;
+				}
+            }
+
+			outStream.Write(bs, 0, bs.Length);
+        }
+
+        private readonly Stream outStream;
+        private int[]           buf = new int[3];
+        private int             bufPtr = 0;
+        private Crc24           crc = new Crc24();
+        private int             chunkCount = 0;
+        private int             lastb;
+
+        private bool            start = true;
+        private bool            clearText = false;
+        private bool            newLine = false;
+
+        private string          type;
+
+        private static readonly string	nl = Platform.NewLine;
+        private static readonly string	headerStart = "-----BEGIN PGP ";
+        private static readonly string	headerTail = "-----";
+        private static readonly string	footerStart = "-----END PGP ";
+        private static readonly string	footerTail = "-----";
+
+        private static readonly string version = "BCPG C# v"
+			+ Assembly.GetExecutingAssembly().GetName().Version;
+
+		private readonly IDictionary headers;
+
+		public ArmoredOutputStream(Stream outStream)
+        {
+            this.outStream = outStream;
+            this.headers = Platform.CreateHashtable();
+            this.headers["Version"] = version;
+        }
+
+        public ArmoredOutputStream(Stream outStream, IDictionary headers)
+        {
+            this.outStream = outStream;
+            this.headers = Platform.CreateHashtable(headers);
+            this.headers["Version"] = version;
+        }
+
+        /**
+         * Set an additional header entry.
+         *
+         * @param name the name of the header entry.
+         * @param v the value of the header entry.
+         */
+        public void SetHeader(
+            string name,
+            string v)
+        {
+            headers[name] = v;
+        }
+
+        /**
+         * Reset the headers to only contain a Version string.
+         */
+        public void ResetHeaders()
+        {
+            headers.Clear();
+            headers["Version"] = version;
+        }
+
+        /**
+         * Start a clear text signed message.
+         * @param hashAlgorithm
+         */
+        public void BeginClearText(
+            HashAlgorithmTag    hashAlgorithm)
+        {
+            string    hash;
+
+            switch (hashAlgorithm)
+            {
+            case HashAlgorithmTag.Sha1:
+                hash = "SHA1";
+                break;
+            case HashAlgorithmTag.Sha256:
+                hash = "SHA256";
+                break;
+            case HashAlgorithmTag.Sha384:
+                hash = "SHA384";
+                break;
+            case HashAlgorithmTag.Sha512:
+                hash = "SHA512";
+                break;
+            case HashAlgorithmTag.MD2:
+                hash = "MD2";
+                break;
+            case HashAlgorithmTag.MD5:
+                hash = "MD5";
+                break;
+            case HashAlgorithmTag.RipeMD160:
+                hash = "RIPEMD160";
+                break;
+            default:
+                throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm);
+            }
+
+			DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + nl);
+            DoWrite("Hash: " + hash + nl + nl);
+
+			clearText = true;
+            newLine = true;
+            lastb = 0;
+        }
+
+        public void EndClearText()
+        {
+            clearText = false;
+        }
+
+        public override void WriteByte(
+            byte b)
+        {
+            if (clearText)
+            {
+                outStream.WriteByte(b);
+
+                if (newLine)
+                {
+                    if (!(b == '\n' && lastb == '\r'))
+                    {
+                        newLine = false;
+                    }
+                    if (b == '-')
+                    {
+                        outStream.WriteByte((byte)' ');
+                        outStream.WriteByte((byte)'-');      // dash escape
+                    }
+                }
+                if (b == '\r' || (b == '\n' && lastb != '\r'))
+                {
+                    newLine = true;
+                }
+                lastb = b;
+                return;
+            }
+
+            if (start)
+            {
+                bool newPacket = (b & 0x40) != 0;
+
+				int tag;
+                if (newPacket)
+                {
+                    tag = b & 0x3f;
+                }
+                else
+                {
+                    tag = (b & 0x3f) >> 2;
+                }
+
+                switch ((PacketTag)tag)
+                {
+                case PacketTag.PublicKey:
+                    type = "PUBLIC KEY BLOCK";
+                    break;
+                case PacketTag.SecretKey:
+                    type = "PRIVATE KEY BLOCK";
+                    break;
+                case PacketTag.Signature:
+                    type = "SIGNATURE";
+                    break;
+                default:
+                    type = "MESSAGE";
+				    break;
+                }
+
+                DoWrite(headerStart + type + headerTail + nl);
+                WriteHeaderEntry("Version", (string) headers["Version"]);
+
+                foreach (DictionaryEntry de in headers)
+                {
+                    string k = (string) de.Key;
+                    if (k != "Version")
+                    {
+                        string v = (string) de.Value;
+                        WriteHeaderEntry(k, v);
+                    }
+                }
+
+                DoWrite(nl);
+
+                start = false;
+            }
+
+            if (bufPtr == 3)
+            {
+                Encode(outStream, buf, bufPtr);
+                bufPtr = 0;
+                if ((++chunkCount & 0xf) == 0)
+                {
+                    DoWrite(nl);
+                }
+            }
+
+            crc.Update(b);
+            buf[bufPtr++] = b & 0xff;
+        }
+
+        /**
+         * <b>Note</b>: close does nor close the underlying stream. So it is possible to write
+         * multiple objects using armoring to a single stream.
+         */
+        public override void Close()
+        {
+            if (type != null)
+            {
+				if (bufPtr > 0)
+				{
+					Encode(outStream, buf, bufPtr);
+				}
+
+                DoWrite(nl + '=');
+
+                int crcV = crc.Value;
+
+				buf[0] = ((crcV >> 16) & 0xff);
+                buf[1] = ((crcV >> 8) & 0xff);
+                buf[2] = (crcV & 0xff);
+
+                Encode(outStream, buf, 3);
+
+                DoWrite(nl);
+                DoWrite(footerStart);
+                DoWrite(type);
+                DoWrite(footerTail);
+                DoWrite(nl);
+
+                outStream.Flush();
+
+                type = null;
+                start = true;
+				base.Close();
+			}
+        }
+
+		private void WriteHeaderEntry(
+			string	name,
+			string	v)
+        {
+            DoWrite(name + ": " + v + nl);
+        }
+
+		private void DoWrite(
+			string s)
+        {
+            byte[] bs = Strings.ToAsciiByteArray(s);
+			outStream.Write(bs, 0, bs.Length);
+        }
+    }
+}