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);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/ArmoredOutputStream.cs b/Crypto/src/bcpg/ArmoredOutputStream.cs
new file mode 100644
index 000000000..7ee26886a
--- /dev/null
+++ b/Crypto/src/bcpg/ArmoredOutputStream.cs
@@ -0,0 +1,333 @@
+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" + AssemblyInfo.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>: Dispose does nor Dispose the underlying stream. So it is possible to write
+ * multiple objects using armoring to a single stream.
+ */
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ 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.Dispose(disposing);
+ }
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/BcpgInputStream.cs b/Crypto/src/bcpg/BcpgInputStream.cs
new file mode 100644
index 000000000..b4afab901
--- /dev/null
+++ b/Crypto/src/bcpg/BcpgInputStream.cs
@@ -0,0 +1,359 @@
+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);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ m_in.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ /// <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;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/BcpgObject.cs b/Crypto/src/bcpg/BcpgObject.cs
new file mode 100644
index 000000000..92811c3d3
--- /dev/null
+++ b/Crypto/src/bcpg/BcpgObject.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for a PGP object.</remarks>
+ public abstract class BcpgObject
+ {
+ public virtual byte[] GetEncoded()
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.WriteObject(this);
+
+ return bOut.ToArray();
+ }
+
+ public abstract void Encode(BcpgOutputStream bcpgOut);
+ }
+}
+
diff --git a/Crypto/src/bcpg/BcpgOutputStream.cs b/Crypto/src/bcpg/BcpgOutputStream.cs
new file mode 100644
index 000000000..365053e11
--- /dev/null
+++ b/Crypto/src/bcpg/BcpgOutputStream.cs
@@ -0,0 +1,394 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic output stream.</remarks>
+ public class BcpgOutputStream
+ : BaseOutputStream
+ {
+ internal static BcpgOutputStream Wrap(
+ Stream outStr)
+ {
+ if (outStr is BcpgOutputStream)
+ {
+ return (BcpgOutputStream) outStr;
+ }
+
+ return new BcpgOutputStream(outStr);
+ }
+
+ private Stream outStr;
+ private byte[] partialBuffer;
+ private int partialBufferLength;
+ private int partialPower;
+ private int partialOffset;
+ private const int BufferSizePower = 16; // 2^16 size buffer on long files
+
+ /// <summary>Create a stream representing a general packet.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ public BcpgOutputStream(
+ Stream outStr)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ }
+
+ /// <summary>Create a stream representing an old style partial object.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">The packet tag for the object.</param>
+ public BcpgOutputStream(
+ Stream outStr,
+ PacketTag tag)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ this.WriteHeader(tag, true, true, 0);
+ }
+
+ /// <summary>Create a stream representing a general packet.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">Packet tag.</param>
+ /// <param name="length">Size of chunks making up the packet.</param>
+ /// <param name="oldFormat">If true, the header is written out in old format.</param>
+ public BcpgOutputStream(
+ Stream outStr,
+ PacketTag tag,
+ long length,
+ bool oldFormat)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+
+ if (length > 0xFFFFFFFFL)
+ {
+ this.WriteHeader(tag, false, true, 0);
+ this.partialBufferLength = 1 << BufferSizePower;
+ this.partialBuffer = new byte[partialBufferLength];
+ this.partialPower = BufferSizePower;
+ this.partialOffset = 0;
+ }
+ else
+ {
+ this.WriteHeader(tag, oldFormat, false, length);
+ }
+ }
+
+ /// <summary>Create a new style partial input stream buffered into chunks.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">Packet tag.</param>
+ /// <param name="length">Size of chunks making up the packet.</param>
+ public BcpgOutputStream(
+ Stream outStr,
+ PacketTag tag,
+ long length)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ this.WriteHeader(tag, false, false, length);
+ }
+
+ /// <summary>Create a new style partial input stream buffered into chunks.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">Packet tag.</param>
+ /// <param name="buffer">Buffer to use for collecting chunks.</param>
+ public BcpgOutputStream(
+ Stream outStr,
+ PacketTag tag,
+ byte[] buffer)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ this.WriteHeader(tag, false, true, 0);
+
+ this.partialBuffer = buffer;
+
+ uint length = (uint) partialBuffer.Length;
+ for (partialPower = 0; length != 1; partialPower++)
+ {
+ length >>= 1;
+ }
+
+ if (partialPower > 30)
+ {
+ throw new IOException("Buffer cannot be greater than 2^30 in length.");
+ }
+ this.partialBufferLength = 1 << partialPower;
+ this.partialOffset = 0;
+ }
+
+ private void WriteNewPacketLength(
+ long bodyLen)
+ {
+ if (bodyLen < 192)
+ {
+ outStr.WriteByte((byte)bodyLen);
+ }
+ else if (bodyLen <= 8383)
+ {
+ bodyLen -= 192;
+
+ outStr.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+ outStr.WriteByte((byte)bodyLen);
+ }
+ else
+ {
+ outStr.WriteByte(0xff);
+ outStr.WriteByte((byte)(bodyLen >> 24));
+ outStr.WriteByte((byte)(bodyLen >> 16));
+ outStr.WriteByte((byte)(bodyLen >> 8));
+ outStr.WriteByte((byte)bodyLen);
+ }
+ }
+
+ private void WriteHeader(
+ PacketTag tag,
+ bool oldPackets,
+ bool partial,
+ long bodyLen)
+ {
+ int hdr = 0x80;
+
+ if (partialBuffer != null)
+ {
+ PartialFlush(true);
+ partialBuffer = null;
+ }
+
+ if (oldPackets)
+ {
+ hdr |= ((int) tag) << 2;
+
+ if (partial)
+ {
+ this.WriteByte((byte)(hdr | 0x03));
+ }
+ else
+ {
+ if (bodyLen <= 0xff)
+ {
+ this.WriteByte((byte) hdr);
+ this.WriteByte((byte)bodyLen);
+ }
+ else if (bodyLen <= 0xffff)
+ {
+ this.WriteByte((byte)(hdr | 0x01));
+ this.WriteByte((byte)(bodyLen >> 8));
+ this.WriteByte((byte)(bodyLen));
+ }
+ else
+ {
+ this.WriteByte((byte)(hdr | 0x02));
+ this.WriteByte((byte)(bodyLen >> 24));
+ this.WriteByte((byte)(bodyLen >> 16));
+ this.WriteByte((byte)(bodyLen >> 8));
+ this.WriteByte((byte)bodyLen);
+ }
+ }
+ }
+ else
+ {
+ hdr |= 0x40 | (int) tag;
+ this.WriteByte((byte) hdr);
+
+ if (partial)
+ {
+ partialOffset = 0;
+ }
+ else
+ {
+ this.WriteNewPacketLength(bodyLen);
+ }
+ }
+ }
+
+ private void PartialFlush(
+ bool isLast)
+ {
+ if (isLast)
+ {
+ WriteNewPacketLength(partialOffset);
+ outStr.Write(partialBuffer, 0, partialOffset);
+ }
+ else
+ {
+ outStr.WriteByte((byte)(0xE0 | partialPower));
+ outStr.Write(partialBuffer, 0, partialBufferLength);
+ }
+
+ partialOffset = 0;
+ }
+
+ private void WritePartial(
+ byte b)
+ {
+ if (partialOffset == partialBufferLength)
+ {
+ PartialFlush(false);
+ }
+
+ partialBuffer[partialOffset++] = b;
+ }
+
+ private void WritePartial(
+ byte[] buffer,
+ int off,
+ int len)
+ {
+ if (partialOffset == partialBufferLength)
+ {
+ PartialFlush(false);
+ }
+
+ if (len <= (partialBufferLength - partialOffset))
+ {
+ Array.Copy(buffer, off, partialBuffer, partialOffset, len);
+ partialOffset += len;
+ }
+ else
+ {
+ int diff = partialBufferLength - partialOffset;
+ Array.Copy(buffer, off, partialBuffer, partialOffset, diff);
+ off += diff;
+ len -= diff;
+ PartialFlush(false);
+ while (len > partialBufferLength)
+ {
+ Array.Copy(buffer, off, partialBuffer, 0, partialBufferLength);
+ off += partialBufferLength;
+ len -= partialBufferLength;
+ PartialFlush(false);
+ }
+ Array.Copy(buffer, off, partialBuffer, 0, len);
+ partialOffset += len;
+ }
+ }
+ public override void WriteByte(
+ byte value)
+ {
+ if (partialBuffer != null)
+ {
+ WritePartial(value);
+ }
+ else
+ {
+ outStr.WriteByte(value);
+ }
+ }
+ public override void Write(
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ if (partialBuffer != null)
+ {
+ WritePartial(buffer, offset, count);
+ }
+ else
+ {
+ outStr.Write(buffer, offset, count);
+ }
+ }
+
+ // Additional helper methods to write primitive types
+ internal virtual void WriteShort(
+ short n)
+ {
+ this.Write(
+ (byte)(n >> 8),
+ (byte)n);
+ }
+ internal virtual void WriteInt(
+ int n)
+ {
+ this.Write(
+ (byte)(n >> 24),
+ (byte)(n >> 16),
+ (byte)(n >> 8),
+ (byte)n);
+ }
+ internal virtual void WriteLong(
+ long n)
+ {
+ this.Write(
+ (byte)(n >> 56),
+ (byte)(n >> 48),
+ (byte)(n >> 40),
+ (byte)(n >> 32),
+ (byte)(n >> 24),
+ (byte)(n >> 16),
+ (byte)(n >> 8),
+ (byte)n);
+ }
+
+ public void WritePacket(
+ ContainedPacket p)
+ {
+ p.Encode(this);
+ }
+
+ internal void WritePacket(
+ PacketTag tag,
+ byte[] body,
+ bool oldFormat)
+ {
+ this.WriteHeader(tag, oldFormat, false, body.Length);
+ this.Write(body);
+ }
+
+ public void WriteObject(
+ BcpgObject bcpgObject)
+ {
+ bcpgObject.Encode(this);
+ }
+
+ public void WriteObjects(
+ params BcpgObject[] v)
+ {
+ foreach (BcpgObject o in v)
+ {
+ o.Encode(this);
+ }
+ }
+
+ /// <summary>Flush the underlying stream.</summary>
+ public override void Flush()
+ {
+ outStr.Flush();
+ }
+
+ /// <summary>Finish writing out the current packet without closing the underlying stream.</summary>
+ public void Finish()
+ {
+ if (partialBuffer != null)
+ {
+ PartialFlush(true);
+ partialBuffer = null;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ this.Finish();
+ outStr.Flush();
+ outStr.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/CompressedDataPacket.cs b/Crypto/src/bcpg/CompressedDataPacket.cs
new file mode 100644
index 000000000..2432825eb
--- /dev/null
+++ b/Crypto/src/bcpg/CompressedDataPacket.cs
@@ -0,0 +1,24 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Generic compressed data object.</remarks>
+ public class CompressedDataPacket
+ : InputStreamPacket
+ {
+ private readonly CompressionAlgorithmTag algorithm;
+
+ internal CompressedDataPacket(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ this.algorithm = (CompressionAlgorithmTag) bcpgIn.ReadByte();
+ }
+
+ /// <summary>The algorithm tag value.</summary>
+ public CompressionAlgorithmTag Algorithm
+ {
+ get { return algorithm; }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/CompressionAlgorithmTags.cs b/Crypto/src/bcpg/CompressionAlgorithmTags.cs
new file mode 100644
index 000000000..0e452298e
--- /dev/null
+++ b/Crypto/src/bcpg/CompressionAlgorithmTags.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic tags for compression algorithms.</remarks>
+ public enum CompressionAlgorithmTag
+ {
+ Uncompressed = 0, // Uncompressed
+ Zip = 1, // ZIP (RFC 1951)
+ ZLib = 2, // ZLIB (RFC 1950)
+ BZip2 = 3, // BZ2
+ }
+}
diff --git a/Crypto/src/bcpg/ContainedPacket.cs b/Crypto/src/bcpg/ContainedPacket.cs
new file mode 100644
index 000000000..e8f387ca4
--- /dev/null
+++ b/Crypto/src/bcpg/ContainedPacket.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic type for a PGP packet.</remarks>
+ public abstract class ContainedPacket
+ : Packet
+ {
+ public byte[] GetEncoded()
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.WritePacket(this);
+
+ return bOut.ToArray();
+ }
+
+ public abstract void Encode(BcpgOutputStream bcpgOut);
+ }
+}
diff --git a/Crypto/src/bcpg/Crc24.cs b/Crypto/src/bcpg/Crc24.cs
new file mode 100644
index 000000000..97846f4fb
--- /dev/null
+++ b/Crypto/src/bcpg/Crc24.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ public class Crc24
+ {
+ private const int Crc24Init = 0x0b704ce;
+ private const int Crc24Poly = 0x1864cfb;
+
+ private int crc = Crc24Init;
+
+ public Crc24()
+ {
+ }
+
+ public void Update(
+ int b)
+ {
+ crc ^= b << 16;
+ for (int i = 0; i < 8; i++)
+ {
+ crc <<= 1;
+ if ((crc & 0x1000000) != 0)
+ {
+ crc ^= Crc24Poly;
+ }
+ }
+ }
+
+ [Obsolete("Use 'Value' property instead")]
+ public int GetValue()
+ {
+ return crc;
+ }
+
+ public int Value
+ {
+ get { return crc; }
+ }
+
+ public void Reset()
+ {
+ crc = Crc24Init;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/DsaPublicBcpgKey.cs b/Crypto/src/bcpg/DsaPublicBcpgKey.cs
new file mode 100644
index 000000000..61159567c
--- /dev/null
+++ b/Crypto/src/bcpg/DsaPublicBcpgKey.cs
@@ -0,0 +1,80 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for a DSA public key.</remarks>
+ public class DsaPublicBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ private readonly MPInteger p, q, g, y;
+
+ /// <param name="bcpgIn">The stream to read the packet from.</param>
+ public DsaPublicBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.p = new MPInteger(bcpgIn);
+ this.q = new MPInteger(bcpgIn);
+ this.g = new MPInteger(bcpgIn);
+ this.y = new MPInteger(bcpgIn);
+ }
+
+ public DsaPublicBcpgKey(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ BigInteger y)
+ {
+ this.p = new MPInteger(p);
+ this.q = new MPInteger(q);
+ this.g = new MPInteger(g);
+ this.y = new MPInteger(y);
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObjects(p, q, g, y);
+ }
+
+ public BigInteger G
+ {
+ get { return g.Value; }
+ }
+
+ public BigInteger P
+ {
+ get { return p.Value; }
+ }
+
+ public BigInteger Q
+ {
+ get { return q.Value; }
+ }
+
+ public BigInteger Y
+ {
+ get { return y.Value; }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/DsaSecretBcpgKey.cs b/Crypto/src/bcpg/DsaSecretBcpgKey.cs
new file mode 100644
index 000000000..41835d419
--- /dev/null
+++ b/Crypto/src/bcpg/DsaSecretBcpgKey.cs
@@ -0,0 +1,61 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for a DSA secret key.</remarks>
+ public class DsaSecretBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ internal MPInteger x;
+
+ /**
+ * @param in
+ */
+ public DsaSecretBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.x = new MPInteger(bcpgIn);
+ }
+
+ public DsaSecretBcpgKey(
+ BigInteger x)
+ {
+ this.x = new MPInteger(x);
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObject(x);
+ }
+
+ /**
+ * @return x
+ */
+ public BigInteger X
+ {
+ get { return x.Value; }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/ElGamalPublicBcpgKey.cs b/Crypto/src/bcpg/ElGamalPublicBcpgKey.cs
new file mode 100644
index 000000000..808e427b2
--- /dev/null
+++ b/Crypto/src/bcpg/ElGamalPublicBcpgKey.cs
@@ -0,0 +1,71 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an ElGamal public key.</remarks>
+ public class ElGamalPublicBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ internal MPInteger p, g, y;
+
+ public ElGamalPublicBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.p = new MPInteger(bcpgIn);
+ this.g = new MPInteger(bcpgIn);
+ this.y = new MPInteger(bcpgIn);
+ }
+
+ public ElGamalPublicBcpgKey(
+ BigInteger p,
+ BigInteger g,
+ BigInteger y)
+ {
+ this.p = new MPInteger(p);
+ this.g = new MPInteger(g);
+ this.y = new MPInteger(y);
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public BigInteger P
+ {
+ get { return p.Value; }
+ }
+
+ public BigInteger G
+ {
+ get { return g.Value; }
+ }
+
+ public BigInteger Y
+ {
+ get { return y.Value; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObjects(p, g, y);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/ElGamalSecretBcpgKey.cs b/Crypto/src/bcpg/ElGamalSecretBcpgKey.cs
new file mode 100644
index 000000000..2d95b29b1
--- /dev/null
+++ b/Crypto/src/bcpg/ElGamalSecretBcpgKey.cs
@@ -0,0 +1,61 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an ElGamal secret key.</remarks>
+ public class ElGamalSecretBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ internal MPInteger x;
+
+ /**
+ * @param in
+ */
+ public ElGamalSecretBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.x = new MPInteger(bcpgIn);
+ }
+
+ /**
+ * @param x
+ */
+ public ElGamalSecretBcpgKey(
+ BigInteger x)
+ {
+ this.x = new MPInteger(x);
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ public BigInteger X
+ {
+ get { return x.Value; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObject(x);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/ExperimentalPacket.cs b/Crypto/src/bcpg/ExperimentalPacket.cs
new file mode 100644
index 000000000..36a254be1
--- /dev/null
+++ b/Crypto/src/bcpg/ExperimentalPacket.cs
@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for an experimental packet.</remarks>
+ public class ExperimentalPacket
+ : ContainedPacket //, PublicKeyAlgorithmTag
+ {
+ private readonly PacketTag tag;
+ private readonly byte[] contents;
+
+ internal ExperimentalPacket(
+ PacketTag tag,
+ BcpgInputStream bcpgIn)
+ {
+ this.tag = tag;
+
+ this.contents = bcpgIn.ReadAll();
+ }
+
+ public PacketTag Tag
+ {
+ get { return tag; }
+ }
+
+ public byte[] GetContents()
+ {
+ return (byte[]) contents.Clone();
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(tag, contents, true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/HashAlgorithmTags.cs b/Crypto/src/bcpg/HashAlgorithmTags.cs
new file mode 100644
index 000000000..96c009153
--- /dev/null
+++ b/Crypto/src/bcpg/HashAlgorithmTags.cs
@@ -0,0 +1,19 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic tags for hash algorithms.</remarks>
+ public enum HashAlgorithmTag
+ {
+ MD5 = 1, // MD5
+ Sha1 = 2, // SHA-1
+ RipeMD160 = 3, // RIPE-MD/160
+ DoubleSha = 4, // Reserved for double-width SHA (experimental)
+ MD2 = 5, // MD2
+ Tiger192 = 6, // Reserved for TIGER/192
+ Haval5pass160 = 7, // Reserved for HAVAL (5 pass, 160-bit)
+
+ Sha256 = 8, // SHA-256
+ Sha384 = 9, // SHA-384
+ Sha512 = 10, // SHA-512
+ Sha224 = 11, // SHA-224
+ }
+}
diff --git a/Crypto/src/bcpg/IBcpgKey.cs b/Crypto/src/bcpg/IBcpgKey.cs
new file mode 100644
index 000000000..275461772
--- /dev/null
+++ b/Crypto/src/bcpg/IBcpgKey.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base interface for a PGP key.</remarks>
+ public interface IBcpgKey
+ {
+ /// <summary>
+ /// The base format for this key - in the case of the symmetric keys it will generally
+ /// be raw indicating that the key is just a straight byte representation, for an asymmetric
+ /// key the format will be PGP, indicating the key is a string of MPIs encoded in PGP format.
+ /// </summary>
+ /// <returns>"RAW" or "PGP".</returns>
+ string Format { get; }
+ }
+}
diff --git a/Crypto/src/bcpg/InputStreamPacket.cs b/Crypto/src/bcpg/InputStreamPacket.cs
new file mode 100644
index 000000000..c45efab7b
--- /dev/null
+++ b/Crypto/src/bcpg/InputStreamPacket.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ public class InputStreamPacket
+ : Packet
+ {
+ private readonly BcpgInputStream bcpgIn;
+
+ public InputStreamPacket(
+ BcpgInputStream bcpgIn)
+ {
+ this.bcpgIn = bcpgIn;
+ }
+
+ /// <summary>Note: you can only read from this once...</summary>
+ public BcpgInputStream GetInputStream()
+ {
+ return bcpgIn;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/LiteralDataPacket.cs b/Crypto/src/bcpg/LiteralDataPacket.cs
new file mode 100644
index 000000000..63a2c6d44
--- /dev/null
+++ b/Crypto/src/bcpg/LiteralDataPacket.cs
@@ -0,0 +1,57 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Generic literal data packet.</remarks>
+ public class LiteralDataPacket
+ : InputStreamPacket
+ {
+ private int format;
+ private byte[] fileName;
+ private long modDate;
+
+ internal LiteralDataPacket(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ format = bcpgIn.ReadByte();
+ int len = bcpgIn.ReadByte();
+
+ fileName = new byte[len];
+ for (int i = 0; i != len; ++i)
+ {
+ fileName[i] = (byte)bcpgIn.ReadByte();
+ }
+
+ modDate = (((uint)bcpgIn.ReadByte() << 24)
+ | ((uint)bcpgIn.ReadByte() << 16)
+ | ((uint)bcpgIn.ReadByte() << 8)
+ | (uint)bcpgIn.ReadByte()) * 1000L;
+ }
+
+ /// <summary>The format tag value.</summary>
+ public int Format
+ {
+ get { return format; }
+ }
+
+ /// <summary>The modification time of the file in milli-seconds (since Jan 1, 1970 UTC)</summary>
+ public long ModificationTime
+ {
+ get { return modDate; }
+ }
+
+ public string FileName
+ {
+ get { return Strings.FromUtf8ByteArray(fileName); }
+ }
+
+ public byte[] GetRawFileName()
+ {
+ return Arrays.Clone(fileName);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/MPInteger.cs b/Crypto/src/bcpg/MPInteger.cs
new file mode 100644
index 000000000..441407244
--- /dev/null
+++ b/Crypto/src/bcpg/MPInteger.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>A multiple precision integer</remarks>
+ public class MPInteger
+ : BcpgObject
+ {
+ private readonly BigInteger val;
+
+ public MPInteger(
+ BcpgInputStream bcpgIn)
+ {
+ if (bcpgIn == null)
+ throw new ArgumentNullException("bcpgIn");
+
+ int length = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+ byte[] bytes = new byte[(length + 7) / 8];
+
+ bcpgIn.ReadFully(bytes);
+
+ this.val = new BigInteger(1, bytes);
+ }
+
+ public MPInteger(
+ BigInteger val)
+ {
+ if (val == null)
+ throw new ArgumentNullException("val");
+ if (val.SignValue < 0)
+ throw new ArgumentException("Values must be positive", "val");
+
+ this.val = val;
+ }
+
+ public BigInteger Value
+ {
+ get { return val; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteShort((short) val.BitLength);
+ bcpgOut.Write(val.ToByteArrayUnsigned());
+ }
+
+ internal static void Encode(
+ BcpgOutputStream bcpgOut,
+ BigInteger val)
+ {
+ bcpgOut.WriteShort((short) val.BitLength);
+ bcpgOut.Write(val.ToByteArrayUnsigned());
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/MarkerPacket.cs b/Crypto/src/bcpg/MarkerPacket.cs
new file mode 100644
index 000000000..4dc4b5a83
--- /dev/null
+++ b/Crypto/src/bcpg/MarkerPacket.cs
@@ -0,0 +1,24 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic type for a marker packet.</remarks>
+ public class MarkerPacket
+ : ContainedPacket
+ {
+ // "PGP"
+ byte[] marker = { (byte)0x50, (byte)0x47, (byte)0x50 };
+
+ public MarkerPacket(
+ BcpgInputStream bcpgIn)
+ {
+ bcpgIn.ReadFully(marker);
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.Marker, marker, true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/ModDetectionCodePacket.cs b/Crypto/src/bcpg/ModDetectionCodePacket.cs
new file mode 100644
index 000000000..6bb23645a
--- /dev/null
+++ b/Crypto/src/bcpg/ModDetectionCodePacket.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for a modification detection code packet.</remarks>
+ public class ModDetectionCodePacket
+ : ContainedPacket
+ {
+ private readonly byte[] digest;
+
+ internal ModDetectionCodePacket(
+ BcpgInputStream bcpgIn)
+ {
+ if (bcpgIn == null)
+ throw new ArgumentNullException("bcpgIn");
+
+ this.digest = new byte[20];
+ bcpgIn.ReadFully(this.digest);
+ }
+
+ public ModDetectionCodePacket(
+ byte[] digest)
+ {
+ if (digest == null)
+ throw new ArgumentNullException("digest");
+
+ this.digest = (byte[]) digest.Clone();
+ }
+
+ public byte[] GetDigest()
+ {
+ return (byte[]) digest.Clone();
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.ModificationDetectionCode, digest, false);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/OnePassSignaturePacket.cs b/Crypto/src/bcpg/OnePassSignaturePacket.cs
new file mode 100644
index 000000000..b67df0a52
--- /dev/null
+++ b/Crypto/src/bcpg/OnePassSignaturePacket.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Generic signature object</remarks>
+ public class OnePassSignaturePacket
+ : ContainedPacket
+ {
+ private int version;
+ private int sigType;
+ private HashAlgorithmTag hashAlgorithm;
+ private PublicKeyAlgorithmTag keyAlgorithm;
+ private long keyId;
+ private int nested;
+
+ internal OnePassSignaturePacket(
+ BcpgInputStream bcpgIn)
+ {
+ version = bcpgIn.ReadByte();
+ sigType = bcpgIn.ReadByte();
+ hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
+ keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+
+ keyId |= (long)bcpgIn.ReadByte() << 56;
+ keyId |= (long)bcpgIn.ReadByte() << 48;
+ keyId |= (long)bcpgIn.ReadByte() << 40;
+ keyId |= (long)bcpgIn.ReadByte() << 32;
+ keyId |= (long)bcpgIn.ReadByte() << 24;
+ keyId |= (long)bcpgIn.ReadByte() << 16;
+ keyId |= (long)bcpgIn.ReadByte() << 8;
+ keyId |= (uint)bcpgIn.ReadByte();
+
+ nested = bcpgIn.ReadByte();
+ }
+
+ public OnePassSignaturePacket(
+ int sigType,
+ HashAlgorithmTag hashAlgorithm,
+ PublicKeyAlgorithmTag keyAlgorithm,
+ long keyId,
+ bool isNested)
+ {
+ this.version = 3;
+ this.sigType = sigType;
+ this.hashAlgorithm = hashAlgorithm;
+ this.keyAlgorithm = keyAlgorithm;
+ this.keyId = keyId;
+ this.nested = (isNested) ? 0 : 1;
+ }
+
+ public int SignatureType
+ {
+ get { return sigType; }
+ }
+
+ /// <summary>The encryption algorithm tag.</summary>
+ public PublicKeyAlgorithmTag KeyAlgorithm
+ {
+ get { return keyAlgorithm; }
+ }
+
+ /// <summary>The hash algorithm tag.</summary>
+ public HashAlgorithmTag HashAlgorithm
+ {
+ get { return hashAlgorithm; }
+ }
+
+ public long KeyId
+ {
+ get { return keyId; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.Write(
+ (byte) version,
+ (byte) sigType,
+ (byte) hashAlgorithm,
+ (byte) keyAlgorithm);
+
+ pOut.WriteLong(keyId);
+
+ pOut.WriteByte((byte) nested);
+
+ bcpgOut.WritePacket(PacketTag.OnePassSignature, bOut.ToArray(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/OutputStreamPacket.cs b/Crypto/src/bcpg/OutputStreamPacket.cs
new file mode 100644
index 000000000..aa8316dcb
--- /dev/null
+++ b/Crypto/src/bcpg/OutputStreamPacket.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ public abstract class OutputStreamPacket
+ {
+ private readonly BcpgOutputStream bcpgOut;
+
+ internal OutputStreamPacket(
+ BcpgOutputStream bcpgOut)
+ {
+ if (bcpgOut == null)
+ throw new ArgumentNullException("bcpgOut");
+
+ this.bcpgOut = bcpgOut;
+ }
+
+ public abstract BcpgOutputStream Open();
+
+ public abstract void Close();
+ }
+}
+
diff --git a/Crypto/src/bcpg/Packet.cs b/Crypto/src/bcpg/Packet.cs
new file mode 100644
index 000000000..83f6d1f74
--- /dev/null
+++ b/Crypto/src/bcpg/Packet.cs
@@ -0,0 +1,7 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ public class Packet
+ //: PacketTag
+ {
+ }
+}
diff --git a/Crypto/src/bcpg/PacketTags.cs b/Crypto/src/bcpg/PacketTags.cs
new file mode 100644
index 000000000..5a53d4e95
--- /dev/null
+++ b/Crypto/src/bcpg/PacketTags.cs
@@ -0,0 +1,30 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic PGP packet tag types.</remarks>
+ public enum PacketTag
+ {
+ Reserved = 0, // Reserved - a packet tag must not have this value
+ PublicKeyEncryptedSession = 1, // Public-Key Encrypted Session Key Packet
+ Signature = 2, // Signature Packet
+ SymmetricKeyEncryptedSessionKey = 3, // Symmetric-Key Encrypted Session Key Packet
+ OnePassSignature = 4, // One-Pass Signature Packet
+ SecretKey = 5, // Secret Key Packet
+ PublicKey = 6, // Public Key Packet
+ SecretSubkey = 7, // Secret Subkey Packet
+ CompressedData = 8, // Compressed Data Packet
+ SymmetricKeyEncrypted = 9, // Symmetrically Encrypted Data Packet
+ Marker = 10, // Marker Packet
+ LiteralData = 11, // Literal Data Packet
+ Trust = 12, // Trust Packet
+ UserId = 13, // User ID Packet
+ PublicSubkey = 14, // Public Subkey Packet
+ UserAttribute = 17, // User attribute
+ SymmetricEncryptedIntegrityProtected = 18, // Symmetric encrypted, integrity protected
+ ModificationDetectionCode = 19, // Modification detection code
+
+ Experimental1 = 60, // Private or Experimental Values
+ Experimental2 = 61,
+ Experimental3 = 62,
+ Experimental4 = 63
+ }
+}
diff --git a/Crypto/src/bcpg/PublicKeyAlgorithmTags.cs b/Crypto/src/bcpg/PublicKeyAlgorithmTags.cs
new file mode 100644
index 000000000..85ae548eb
--- /dev/null
+++ b/Crypto/src/bcpg/PublicKeyAlgorithmTags.cs
@@ -0,0 +1,28 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Public Key Algorithm tag numbers.</remarks>
+ public enum PublicKeyAlgorithmTag
+ {
+ RsaGeneral = 1, // RSA (Encrypt or Sign)
+ RsaEncrypt = 2, // RSA Encrypt-Only
+ RsaSign = 3, // RSA Sign-Only
+ ElGamalEncrypt = 16, // Elgamal (Encrypt-Only), see [ELGAMAL]
+ Dsa = 17, // DSA (Digital Signature Standard)
+ EC = 18, // Reserved for Elliptic Curve
+ ECDsa = 19, // Reserved for ECDSA
+ ElGamalGeneral = 20, // Elgamal (Encrypt or Sign)
+ DiffieHellman = 21, // Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME)
+
+ Experimental_1 = 100,
+ Experimental_2 = 101,
+ Experimental_3 = 102,
+ Experimental_4 = 103,
+ Experimental_5 = 104,
+ Experimental_6 = 105,
+ Experimental_7 = 106,
+ Experimental_8 = 107,
+ Experimental_9 = 108,
+ Experimental_10 = 109,
+ Experimental_11 = 110,
+ }
+}
diff --git a/Crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/Crypto/src/bcpg/PublicKeyEncSessionPacket.cs
new file mode 100644
index 000000000..d10605f1d
--- /dev/null
+++ b/Crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -0,0 +1,103 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for a PGP public key.</remarks>
+ public class PublicKeyEncSessionPacket
+ : ContainedPacket //, PublicKeyAlgorithmTag
+ {
+ private int version;
+ private long keyId;
+ private PublicKeyAlgorithmTag algorithm;
+ private BigInteger[] data;
+
+ internal PublicKeyEncSessionPacket(
+ BcpgInputStream bcpgIn)
+ {
+ version = bcpgIn.ReadByte();
+
+ keyId |= (long)bcpgIn.ReadByte() << 56;
+ keyId |= (long)bcpgIn.ReadByte() << 48;
+ keyId |= (long)bcpgIn.ReadByte() << 40;
+ keyId |= (long)bcpgIn.ReadByte() << 32;
+ keyId |= (long)bcpgIn.ReadByte() << 24;
+ keyId |= (long)bcpgIn.ReadByte() << 16;
+ keyId |= (long)bcpgIn.ReadByte() << 8;
+ keyId |= (uint)bcpgIn.ReadByte();
+
+ algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+
+ switch ((PublicKeyAlgorithmTag) algorithm)
+ {
+ case PublicKeyAlgorithmTag.RsaEncrypt:
+ case PublicKeyAlgorithmTag.RsaGeneral:
+ data = new BigInteger[]{ new MPInteger(bcpgIn).Value };
+ break;
+ case PublicKeyAlgorithmTag.ElGamalEncrypt:
+ case PublicKeyAlgorithmTag.ElGamalGeneral:
+ data = new BigInteger[]
+ {
+ new MPInteger(bcpgIn).Value,
+ new MPInteger(bcpgIn).Value
+ };
+ break;
+ default:
+ throw new IOException("unknown PGP public key algorithm encountered");
+ }
+ }
+
+ public PublicKeyEncSessionPacket(
+ long keyId,
+ PublicKeyAlgorithmTag algorithm,
+ BigInteger[] data)
+ {
+ this.version = 3;
+ this.keyId = keyId;
+ this.algorithm = algorithm;
+ this.data = (BigInteger[]) data.Clone();
+ }
+
+ public int Version
+ {
+ get { return version; }
+ }
+
+ public long KeyId
+ {
+ get { return keyId; }
+ }
+
+ public PublicKeyAlgorithmTag Algorithm
+ {
+ get { return algorithm; }
+ }
+
+ public BigInteger[] GetEncSessionKey()
+ {
+ return (BigInteger[]) data.Clone();
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.WriteByte((byte) version);
+
+ pOut.WriteLong(keyId);
+
+ pOut.WriteByte((byte)algorithm);
+
+ for (int i = 0; i != data.Length; i++)
+ {
+ MPInteger.Encode(pOut, data[i]);
+ }
+
+ bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/PublicKeyPacket.cs b/Crypto/src/bcpg/PublicKeyPacket.cs
new file mode 100644
index 000000000..32d43149b
--- /dev/null
+++ b/Crypto/src/bcpg/PublicKeyPacket.cs
@@ -0,0 +1,115 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for a PGP public key.</remarks>
+ public class PublicKeyPacket
+ : ContainedPacket //, PublicKeyAlgorithmTag
+ {
+ private int version;
+ private long time;
+ private int validDays;
+ private PublicKeyAlgorithmTag algorithm;
+ private IBcpgKey key;
+
+ internal PublicKeyPacket(
+ BcpgInputStream bcpgIn)
+ {
+ version = bcpgIn.ReadByte();
+
+ time = ((uint)bcpgIn.ReadByte() << 24) | ((uint)bcpgIn.ReadByte() << 16)
+ | ((uint)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte();
+
+ if (version <= 3)
+ {
+ validDays = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+ }
+
+ algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+
+ switch ((PublicKeyAlgorithmTag) algorithm)
+ {
+ case PublicKeyAlgorithmTag.RsaEncrypt:
+ case PublicKeyAlgorithmTag.RsaGeneral:
+ case PublicKeyAlgorithmTag.RsaSign:
+ key = new RsaPublicBcpgKey(bcpgIn);
+ break;
+ case PublicKeyAlgorithmTag.Dsa:
+ key = new DsaPublicBcpgKey(bcpgIn);
+ break;
+ case PublicKeyAlgorithmTag.ElGamalEncrypt:
+ case PublicKeyAlgorithmTag.ElGamalGeneral:
+ key = new ElGamalPublicBcpgKey(bcpgIn);
+ break;
+ default:
+ throw new IOException("unknown PGP public key algorithm encountered");
+ }
+ }
+
+ /// <summary>Construct a version 4 public key packet.</summary>
+ public PublicKeyPacket(
+ PublicKeyAlgorithmTag algorithm,
+ DateTime time,
+ IBcpgKey key)
+ {
+ this.version = 4;
+ this.time = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L;
+ this.algorithm = algorithm;
+ this.key = key;
+ }
+
+ public int Version
+ {
+ get { return version; }
+ }
+
+ public PublicKeyAlgorithmTag Algorithm
+ {
+ get { return algorithm; }
+ }
+
+ public int ValidDays
+ {
+ get { return validDays; }
+ }
+
+ public DateTime GetTime()
+ {
+ return DateTimeUtilities.UnixMsToDateTime(time * 1000L);
+ }
+
+ public IBcpgKey Key
+ {
+ get { return key; }
+ }
+
+ public byte[] GetEncodedContents()
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.WriteByte((byte) version);
+ pOut.WriteInt((int) time);
+
+ if (version <= 3)
+ {
+ pOut.WriteShort((short) validDays);
+ }
+
+ pOut.WriteByte((byte) algorithm);
+
+ pOut.WriteObject((BcpgObject)key);
+
+ return bOut.ToArray();
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/PublicSubkeyPacket.cs b/Crypto/src/bcpg/PublicSubkeyPacket.cs
new file mode 100644
index 000000000..6e1aeda98
--- /dev/null
+++ b/Crypto/src/bcpg/PublicSubkeyPacket.cs
@@ -0,0 +1,30 @@
+using System;
+using System.IO;
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for a PGP public subkey</remarks>
+ public class PublicSubkeyPacket
+ : PublicKeyPacket
+ {
+ internal PublicSubkeyPacket(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ }
+
+ /// <summary>Construct a version 4 public subkey packet.</summary>
+ public PublicSubkeyPacket(
+ PublicKeyAlgorithmTag algorithm,
+ DateTime time,
+ IBcpgKey key)
+ : base(algorithm, time, key)
+ {
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.PublicSubkey, GetEncodedContents(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/RsaPublicBcpgKey.cs b/Crypto/src/bcpg/RsaPublicBcpgKey.cs
new file mode 100644
index 000000000..fd2313c89
--- /dev/null
+++ b/Crypto/src/bcpg/RsaPublicBcpgKey.cs
@@ -0,0 +1,66 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an RSA public key.</remarks>
+ public class RsaPublicBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ private readonly MPInteger n, e;
+
+ /// <summary>Construct an RSA public key from the passed in stream.</summary>
+ public RsaPublicBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.n = new MPInteger(bcpgIn);
+ this.e = new MPInteger(bcpgIn);
+ }
+
+ /// <param name="n">The modulus.</param>
+ /// <param name="e">The public exponent.</param>
+ public RsaPublicBcpgKey(
+ BigInteger n,
+ BigInteger e)
+ {
+ this.n = new MPInteger(n);
+ this.e = new MPInteger(e);
+ }
+
+ public BigInteger PublicExponent
+ {
+ get { return e.Value; }
+ }
+
+ public BigInteger Modulus
+ {
+ get { return n.Value; }
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObjects(n, e);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/RsaSecretBcpgKey.cs b/Crypto/src/bcpg/RsaSecretBcpgKey.cs
new file mode 100644
index 000000000..5c04d9f85
--- /dev/null
+++ b/Crypto/src/bcpg/RsaSecretBcpgKey.cs
@@ -0,0 +1,114 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an RSA secret (or priate) key.</remarks>
+ public class RsaSecretBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ private readonly MPInteger d, p, q, u;
+ private readonly BigInteger expP, expQ, crt;
+
+ public RsaSecretBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.d = new MPInteger(bcpgIn);
+ this.p = new MPInteger(bcpgIn);
+ this.q = new MPInteger(bcpgIn);
+ this.u = new MPInteger(bcpgIn);
+
+ this.expP = d.Value.Remainder(p.Value.Subtract(BigInteger.One));
+ this.expQ = d.Value.Remainder(q.Value.Subtract(BigInteger.One));
+ this.crt = q.Value.ModInverse(p.Value);
+ }
+
+ public RsaSecretBcpgKey(
+ BigInteger d,
+ BigInteger p,
+ BigInteger q)
+ {
+ // PGP requires (p < q)
+ int cmp = p.CompareTo(q);
+ if (cmp >= 0)
+ {
+ if (cmp == 0)
+ throw new ArgumentException("p and q cannot be equal");
+
+ BigInteger tmp = p;
+ p = q;
+ q = tmp;
+ }
+
+ this.d = new MPInteger(d);
+ this.p = new MPInteger(p);
+ this.q = new MPInteger(q);
+ this.u = new MPInteger(p.ModInverse(q));
+
+ this.expP = d.Remainder(p.Subtract(BigInteger.One));
+ this.expQ = d.Remainder(q.Subtract(BigInteger.One));
+ this.crt = q.ModInverse(p);
+ }
+
+ public BigInteger Modulus
+ {
+ get { return p.Value.Multiply(q.Value); }
+ }
+
+ public BigInteger PrivateExponent
+ {
+ get { return d.Value; }
+ }
+
+ public BigInteger PrimeP
+ {
+ get { return p.Value; }
+ }
+
+ public BigInteger PrimeQ
+ {
+ get { return q.Value; }
+ }
+
+ public BigInteger PrimeExponentP
+ {
+ get { return expP; }
+ }
+
+ public BigInteger PrimeExponentQ
+ {
+ get { return expQ; }
+ }
+
+ public BigInteger CrtCoefficient
+ {
+ get { return crt; }
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObjects(d, p, q, u);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/S2k.cs b/Crypto/src/bcpg/S2k.cs
new file mode 100644
index 000000000..de08c016c
--- /dev/null
+++ b/Crypto/src/bcpg/S2k.cs
@@ -0,0 +1,147 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>The string to key specifier class.</remarks>
+ public class S2k
+ : BcpgObject
+ {
+ private const int ExpBias = 6;
+
+ public const int Simple = 0;
+ public const int Salted = 1;
+ public const int SaltedAndIterated = 3;
+ public const int GnuDummyS2K = 101;
+
+ internal int type;
+ internal HashAlgorithmTag algorithm;
+ internal byte[] iv;
+ internal int itCount = -1;
+ internal int protectionMode = -1;
+
+ internal S2k(
+ Stream inStr)
+ {
+ type = inStr.ReadByte();
+ algorithm = (HashAlgorithmTag) inStr.ReadByte();
+
+ //
+ // if this happens we have a dummy-S2k packet.
+ //
+ if (type != GnuDummyS2K)
+ {
+ if (type != 0)
+ {
+ iv = new byte[8];
+ if (Streams.ReadFully(inStr, iv, 0, iv.Length) < iv.Length)
+ throw new EndOfStreamException();
+
+ if (type == 3)
+ {
+ itCount = inStr.ReadByte();
+ }
+ }
+ }
+ else
+ {
+ inStr.ReadByte(); // G
+ inStr.ReadByte(); // N
+ inStr.ReadByte(); // U
+ protectionMode = inStr.ReadByte(); // protection mode
+ }
+ }
+
+ public S2k(
+ HashAlgorithmTag algorithm)
+ {
+ this.type = 0;
+ this.algorithm = algorithm;
+ }
+
+ public S2k(
+ HashAlgorithmTag algorithm,
+ byte[] iv)
+ {
+ this.type = 1;
+ this.algorithm = algorithm;
+ this.iv = iv;
+ }
+
+ public S2k(
+ HashAlgorithmTag algorithm,
+ byte[] iv,
+ int itCount)
+ {
+ this.type = 3;
+ this.algorithm = algorithm;
+ this.iv = iv;
+ this.itCount = itCount;
+ }
+
+ public int Type
+ {
+ get { return type; }
+ }
+
+ /// <summary>The hash algorithm.</summary>
+ public HashAlgorithmTag HashAlgorithm
+ {
+ get { return algorithm; }
+ }
+
+ /// <summary>The IV for the key generation algorithm.</summary>
+ public byte[] GetIV()
+ {
+ return Arrays.Clone(iv);
+ }
+
+ [Obsolete("Use 'IterationCount' property instead")]
+ public long GetIterationCount()
+ {
+ return IterationCount;
+ }
+
+ /// <summary>The iteration count</summary>
+ public long IterationCount
+ {
+ get { return (16 + (itCount & 15)) << ((itCount >> 4) + ExpBias); }
+ }
+
+ /// <summary>The protection mode - only if GnuDummyS2K</summary>
+ public int ProtectionMode
+ {
+ get { return protectionMode; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteByte((byte) type);
+ bcpgOut.WriteByte((byte) algorithm);
+
+ if (type != GnuDummyS2K)
+ {
+ if (type != 0)
+ {
+ bcpgOut.Write(iv);
+ }
+
+ if (type == 3)
+ {
+ bcpgOut.WriteByte((byte) itCount);
+ }
+ }
+ else
+ {
+ bcpgOut.WriteByte((byte) 'G');
+ bcpgOut.WriteByte((byte) 'N');
+ bcpgOut.WriteByte((byte) 'U');
+ bcpgOut.WriteByte((byte) protectionMode);
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SecretKeyPacket.cs b/Crypto/src/bcpg/SecretKeyPacket.cs
new file mode 100644
index 000000000..d9ceab4f1
--- /dev/null
+++ b/Crypto/src/bcpg/SecretKeyPacket.cs
@@ -0,0 +1,170 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for a PGP secret key.</remarks>
+ public class SecretKeyPacket
+ : ContainedPacket //, PublicKeyAlgorithmTag
+ {
+ public const int UsageNone = 0x00;
+ public const int UsageChecksum = 0xff;
+ public const int UsageSha1 = 0xfe;
+
+ private PublicKeyPacket pubKeyPacket;
+ private readonly byte[] secKeyData;
+ private int s2kUsage;
+ private SymmetricKeyAlgorithmTag encAlgorithm;
+ private S2k s2k;
+ private byte[] iv;
+
+ internal SecretKeyPacket(
+ BcpgInputStream bcpgIn)
+ {
+ if (this is SecretSubkeyPacket)
+ {
+ pubKeyPacket = new PublicSubkeyPacket(bcpgIn);
+ }
+ else
+ {
+ pubKeyPacket = new PublicKeyPacket(bcpgIn);
+ }
+
+ s2kUsage = bcpgIn.ReadByte();
+
+ if (s2kUsage == UsageChecksum || s2kUsage == UsageSha1)
+ {
+ encAlgorithm = (SymmetricKeyAlgorithmTag) bcpgIn.ReadByte();
+ s2k = new S2k(bcpgIn);
+ }
+ else
+ {
+ encAlgorithm = (SymmetricKeyAlgorithmTag) s2kUsage;
+ }
+
+ if (!(s2k != null && s2k.Type == S2k.GnuDummyS2K && s2k.ProtectionMode == 0x01))
+ {
+ if (s2kUsage != 0)
+ {
+ if (((int) encAlgorithm) < 7)
+ {
+ iv = new byte[8];
+ }
+ else
+ {
+ iv = new byte[16];
+ }
+ bcpgIn.ReadFully(iv);
+ }
+ }
+
+ secKeyData = bcpgIn.ReadAll();
+ }
+
+ public SecretKeyPacket(
+ PublicKeyPacket pubKeyPacket,
+ SymmetricKeyAlgorithmTag encAlgorithm,
+ S2k s2k,
+ byte[] iv,
+ byte[] secKeyData)
+ {
+ this.pubKeyPacket = pubKeyPacket;
+ this.encAlgorithm = encAlgorithm;
+
+ if (encAlgorithm != SymmetricKeyAlgorithmTag.Null)
+ {
+ this.s2kUsage = UsageChecksum;
+ }
+ else
+ {
+ this.s2kUsage = UsageNone;
+ }
+
+ this.s2k = s2k;
+ this.iv = Arrays.Clone(iv);
+ this.secKeyData = secKeyData;
+ }
+
+ public SecretKeyPacket(
+ PublicKeyPacket pubKeyPacket,
+ SymmetricKeyAlgorithmTag encAlgorithm,
+ int s2kUsage,
+ S2k s2k,
+ byte[] iv,
+ byte[] secKeyData)
+ {
+ this.pubKeyPacket = pubKeyPacket;
+ this.encAlgorithm = encAlgorithm;
+ this.s2kUsage = s2kUsage;
+ this.s2k = s2k;
+ this.iv = Arrays.Clone(iv);
+ this.secKeyData = secKeyData;
+ }
+
+ public SymmetricKeyAlgorithmTag EncAlgorithm
+ {
+ get { return encAlgorithm; }
+ }
+
+ public int S2kUsage
+ {
+ get { return s2kUsage; }
+ }
+
+ public byte[] GetIV()
+ {
+ return Arrays.Clone(iv);
+ }
+
+ public S2k S2k
+ {
+ get { return s2k; }
+ }
+
+ public PublicKeyPacket PublicKeyPacket
+ {
+ get { return pubKeyPacket; }
+ }
+
+ public byte[] GetSecretKeyData()
+ {
+ return secKeyData;
+ }
+
+ public byte[] GetEncodedContents()
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.Write(pubKeyPacket.GetEncodedContents());
+
+ pOut.WriteByte((byte) s2kUsage);
+
+ if (s2kUsage == UsageChecksum || s2kUsage == UsageSha1)
+ {
+ pOut.WriteByte((byte) encAlgorithm);
+ pOut.WriteObject(s2k);
+ }
+
+ if (iv != null)
+ {
+ pOut.Write(iv);
+ }
+
+ if (secKeyData != null && secKeyData.Length > 0)
+ {
+ pOut.Write(secKeyData);
+ }
+
+ return bOut.ToArray();
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.SecretKey, GetEncodedContents(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SecretSubkeyPacket.cs b/Crypto/src/bcpg/SecretSubkeyPacket.cs
new file mode 100644
index 000000000..8f1746942
--- /dev/null
+++ b/Crypto/src/bcpg/SecretSubkeyPacket.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic packet for a PGP secret key.</remarks>
+ public class SecretSubkeyPacket
+ : SecretKeyPacket
+ {
+ internal SecretSubkeyPacket(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ }
+
+ public SecretSubkeyPacket(
+ PublicKeyPacket pubKeyPacket,
+ SymmetricKeyAlgorithmTag encAlgorithm,
+ S2k s2k,
+ byte[] iv,
+ byte[] secKeyData)
+ : base(pubKeyPacket, encAlgorithm, s2k, iv, secKeyData)
+ {
+ }
+
+ public SecretSubkeyPacket(
+ PublicKeyPacket pubKeyPacket,
+ SymmetricKeyAlgorithmTag encAlgorithm,
+ int s2kUsage,
+ S2k s2k,
+ byte[] iv,
+ byte[] secKeyData)
+ : base(pubKeyPacket, encAlgorithm, s2kUsage, s2k, iv, secKeyData)
+ {
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.SecretSubkey, GetEncodedContents(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SignaturePacket.cs b/Crypto/src/bcpg/SignaturePacket.cs
new file mode 100644
index 000000000..605ce84c4
--- /dev/null
+++ b/Crypto/src/bcpg/SignaturePacket.cs
@@ -0,0 +1,472 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Generic signature packet.</remarks>
+ public class SignaturePacket
+ : ContainedPacket //, PublicKeyAlgorithmTag
+ {
+ private int version;
+ private int signatureType;
+ private long creationTime;
+ private long keyId;
+ private PublicKeyAlgorithmTag keyAlgorithm;
+ private HashAlgorithmTag hashAlgorithm;
+ private MPInteger[] signature;
+ private byte[] fingerprint;
+ private SignatureSubpacket[] hashedData;
+ private SignatureSubpacket[] unhashedData;
+ private byte[] signatureEncoding;
+
+ internal SignaturePacket(
+ BcpgInputStream bcpgIn)
+ {
+ version = bcpgIn.ReadByte();
+
+ if (version == 3 || version == 2)
+ {
+// int l =
+ bcpgIn.ReadByte();
+
+ signatureType = bcpgIn.ReadByte();
+ creationTime = (((long)bcpgIn.ReadByte() << 24) | ((long)bcpgIn.ReadByte() << 16)
+ | ((long)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte()) * 1000L;
+
+ keyId |= (long)bcpgIn.ReadByte() << 56;
+ keyId |= (long)bcpgIn.ReadByte() << 48;
+ keyId |= (long)bcpgIn.ReadByte() << 40;
+ keyId |= (long)bcpgIn.ReadByte() << 32;
+ keyId |= (long)bcpgIn.ReadByte() << 24;
+ keyId |= (long)bcpgIn.ReadByte() << 16;
+ keyId |= (long)bcpgIn.ReadByte() << 8;
+ keyId |= (uint)bcpgIn.ReadByte();
+
+ keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+ hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
+ }
+ else if (version == 4)
+ {
+ signatureType = bcpgIn.ReadByte();
+ keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+ hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
+
+ int hashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+ byte[] hashed = new byte[hashedLength];
+
+ bcpgIn.ReadFully(hashed);
+
+ //
+ // read the signature sub packet data.
+ //
+ SignatureSubpacketsParser sIn = new SignatureSubpacketsParser(
+ new MemoryStream(hashed, false));
+
+ IList v = Platform.CreateArrayList();
+ SignatureSubpacket sub;
+ while ((sub = sIn.ReadPacket()) != null)
+ {
+ v.Add(sub);
+ }
+
+ hashedData = new SignatureSubpacket[v.Count];
+
+ for (int i = 0; i != hashedData.Length; i++)
+ {
+ SignatureSubpacket p = (SignatureSubpacket)v[i];
+ if (p is IssuerKeyId)
+ {
+ keyId = ((IssuerKeyId)p).KeyId;
+ }
+ else if (p is SignatureCreationTime)
+ {
+ creationTime = DateTimeUtilities.DateTimeToUnixMs(
+ ((SignatureCreationTime)p).GetTime());
+ }
+
+ hashedData[i] = p;
+ }
+
+ int unhashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+ byte[] unhashed = new byte[unhashedLength];
+
+ bcpgIn.ReadFully(unhashed);
+
+ sIn = new SignatureSubpacketsParser(new MemoryStream(unhashed, false));
+
+ v.Clear();
+
+ while ((sub = sIn.ReadPacket()) != null)
+ {
+ v.Add(sub);
+ }
+
+ unhashedData = new SignatureSubpacket[v.Count];
+
+ for (int i = 0; i != unhashedData.Length; i++)
+ {
+ SignatureSubpacket p = (SignatureSubpacket)v[i];
+ if (p is IssuerKeyId)
+ {
+ keyId = ((IssuerKeyId)p).KeyId;
+ }
+
+ unhashedData[i] = p;
+ }
+ }
+ else
+ {
+ throw new Exception("unsupported version: " + version);
+ }
+
+ fingerprint = new byte[2];
+ bcpgIn.ReadFully(fingerprint);
+
+ switch (keyAlgorithm)
+ {
+ case PublicKeyAlgorithmTag.RsaGeneral:
+ case PublicKeyAlgorithmTag.RsaSign:
+ MPInteger v = new MPInteger(bcpgIn);
+ signature = new MPInteger[]{ v };
+ break;
+ case PublicKeyAlgorithmTag.Dsa:
+ MPInteger r = new MPInteger(bcpgIn);
+ MPInteger s = new MPInteger(bcpgIn);
+ signature = new MPInteger[]{ r, s };
+ break;
+ case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes.
+ case PublicKeyAlgorithmTag.ElGamalGeneral:
+ MPInteger p = new MPInteger(bcpgIn);
+ MPInteger g = new MPInteger(bcpgIn);
+ MPInteger y = new MPInteger(bcpgIn);
+ signature = new MPInteger[]{ p, g, y };
+ break;
+ default:
+ if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11)
+ {
+ signature = null;
+ MemoryStream bOut = new MemoryStream();
+ int ch;
+ while ((ch = bcpgIn.ReadByte()) >= 0)
+ {
+ bOut.WriteByte((byte) ch);
+ }
+ signatureEncoding = bOut.ToArray();
+ }
+ else
+ {
+ throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Generate a version 4 signature packet.
+ *
+ * @param signatureType
+ * @param keyAlgorithm
+ * @param hashAlgorithm
+ * @param hashedData
+ * @param unhashedData
+ * @param fingerprint
+ * @param signature
+ */
+ public SignaturePacket(
+ int signatureType,
+ long keyId,
+ PublicKeyAlgorithmTag keyAlgorithm,
+ HashAlgorithmTag hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerprint,
+ MPInteger[] signature)
+ : this(4, signatureType, keyId, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerprint, signature)
+ {
+ }
+
+ /**
+ * Generate a version 2/3 signature packet.
+ *
+ * @param signatureType
+ * @param keyAlgorithm
+ * @param hashAlgorithm
+ * @param fingerprint
+ * @param signature
+ */
+ public SignaturePacket(
+ int version,
+ int signatureType,
+ long keyId,
+ PublicKeyAlgorithmTag keyAlgorithm,
+ HashAlgorithmTag hashAlgorithm,
+ long creationTime,
+ byte[] fingerprint,
+ MPInteger[] signature)
+ : this(version, signatureType, keyId, keyAlgorithm, hashAlgorithm, null, null, fingerprint, signature)
+ {
+ this.creationTime = creationTime;
+ }
+
+ public SignaturePacket(
+ int version,
+ int signatureType,
+ long keyId,
+ PublicKeyAlgorithmTag keyAlgorithm,
+ HashAlgorithmTag hashAlgorithm,
+ SignatureSubpacket[] hashedData,
+ SignatureSubpacket[] unhashedData,
+ byte[] fingerprint,
+ MPInteger[] signature)
+ {
+ this.version = version;
+ this.signatureType = signatureType;
+ this.keyId = keyId;
+ this.keyAlgorithm = keyAlgorithm;
+ this.hashAlgorithm = hashAlgorithm;
+ this.hashedData = hashedData;
+ this.unhashedData = unhashedData;
+ this.fingerprint = fingerprint;
+ this.signature = signature;
+
+ if (hashedData != null)
+ {
+ setCreationTime();
+ }
+ }
+
+ public int Version
+ {
+ get { return version; }
+ }
+
+ public int SignatureType
+ {
+ get { return signatureType; }
+ }
+
+ /**
+ * return the keyId
+ * @return the keyId that created the signature.
+ */
+ public long KeyId
+ {
+ get { return keyId; }
+ }
+
+ /**
+ * return the signature trailer that must be included with the data
+ * to reconstruct the signature
+ *
+ * @return byte[]
+ */
+ public byte[] GetSignatureTrailer()
+ {
+ byte[] trailer = null;
+
+ if (version == 3)
+ {
+ trailer = new byte[5];
+
+ long time = creationTime / 1000L;
+
+ trailer[0] = (byte)signatureType;
+ trailer[1] = (byte)(time >> 24);
+ trailer[2] = (byte)(time >> 16);
+ trailer[3] = (byte)(time >> 8);
+ trailer[4] = (byte)(time);
+ }
+ else
+ {
+ MemoryStream sOut = new MemoryStream();
+
+ sOut.WriteByte((byte)this.Version);
+ sOut.WriteByte((byte)this.SignatureType);
+ sOut.WriteByte((byte)this.KeyAlgorithm);
+ sOut.WriteByte((byte)this.HashAlgorithm);
+
+ MemoryStream hOut = new MemoryStream();
+ SignatureSubpacket[] hashed = this.GetHashedSubPackets();
+
+ for (int i = 0; i != hashed.Length; i++)
+ {
+ hashed[i].Encode(hOut);
+ }
+
+ byte[] data = hOut.ToArray();
+
+ sOut.WriteByte((byte)(data.Length >> 8));
+ sOut.WriteByte((byte)data.Length);
+ sOut.Write(data, 0, data.Length);
+
+ byte[] hData = sOut.ToArray();
+
+ sOut.WriteByte((byte)this.Version);
+ sOut.WriteByte((byte)0xff);
+ sOut.WriteByte((byte)(hData.Length>> 24));
+ sOut.WriteByte((byte)(hData.Length >> 16));
+ sOut.WriteByte((byte)(hData.Length >> 8));
+ sOut.WriteByte((byte)(hData.Length));
+
+ trailer = sOut.ToArray();
+ }
+
+ return trailer;
+ }
+
+ public PublicKeyAlgorithmTag KeyAlgorithm
+ {
+ get { return keyAlgorithm; }
+ }
+
+ public HashAlgorithmTag HashAlgorithm
+ {
+ get { return hashAlgorithm; }
+ }
+
+ /**
+ * return the signature as a set of integers - note this is normalised to be the
+ * ASN.1 encoding of what appears in the signature packet.
+ */
+ public MPInteger[] GetSignature()
+ {
+ return signature;
+ }
+
+ /**
+ * Return the byte encoding of the signature section.
+ * @return uninterpreted signature bytes.
+ */
+ public byte[] GetSignatureBytes()
+ {
+ if (signatureEncoding != null)
+ {
+ return (byte[]) signatureEncoding.Clone();
+ }
+
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream bcOut = new BcpgOutputStream(bOut);
+
+ foreach (MPInteger sigObj in signature)
+ {
+ try
+ {
+ bcOut.WriteObject(sigObj);
+ }
+ catch (IOException e)
+ {
+ throw new Exception("internal error: " + e);
+ }
+ }
+
+ return bOut.ToArray();
+ }
+
+ public SignatureSubpacket[] GetHashedSubPackets()
+ {
+ return hashedData;
+ }
+
+ public SignatureSubpacket[] GetUnhashedSubPackets()
+ {
+ return unhashedData;
+ }
+
+ /// <summary>Return the creation time in milliseconds since 1 Jan., 1970 UTC.</summary>
+ public long CreationTime
+ {
+ get { return creationTime; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.WriteByte((byte) version);
+
+ if (version == 3 || version == 2)
+ {
+ pOut.Write(
+ 5, // the length of the next block
+ (byte) signatureType);
+
+ pOut.WriteInt((int)(creationTime / 1000L));
+
+ pOut.WriteLong(keyId);
+
+ pOut.Write(
+ (byte) keyAlgorithm,
+ (byte) hashAlgorithm);
+ }
+ else if (version == 4)
+ {
+ pOut.Write(
+ (byte) signatureType,
+ (byte) keyAlgorithm,
+ (byte) hashAlgorithm);
+
+ EncodeLengthAndData(pOut, GetEncodedSubpackets(hashedData));
+
+ EncodeLengthAndData(pOut, GetEncodedSubpackets(unhashedData));
+ }
+ else
+ {
+ throw new IOException("unknown version: " + version);
+ }
+
+ pOut.Write(fingerprint);
+
+ if (signature != null)
+ {
+ pOut.WriteObjects(signature);
+ }
+ else
+ {
+ pOut.Write(signatureEncoding);
+ }
+
+ bcpgOut.WritePacket(PacketTag.Signature, bOut.ToArray(), true);
+ }
+
+ private static void EncodeLengthAndData(
+ BcpgOutputStream pOut,
+ byte[] data)
+ {
+ pOut.WriteShort((short) data.Length);
+ pOut.Write(data);
+ }
+
+ private static byte[] GetEncodedSubpackets(
+ SignatureSubpacket[] ps)
+ {
+ MemoryStream sOut = new MemoryStream();
+
+ foreach (SignatureSubpacket p in ps)
+ {
+ p.Encode(sOut);
+ }
+
+ return sOut.ToArray();
+ }
+
+ private void setCreationTime()
+ {
+ foreach (SignatureSubpacket p in hashedData)
+ {
+ if (p is SignatureCreationTime)
+ {
+ creationTime = DateTimeUtilities.DateTimeToUnixMs(
+ ((SignatureCreationTime)p).GetTime());
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SignatureSubpacket.cs b/Crypto/src/bcpg/SignatureSubpacket.cs
new file mode 100644
index 000000000..ac26f8a3c
--- /dev/null
+++ b/Crypto/src/bcpg/SignatureSubpacket.cs
@@ -0,0 +1,76 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic type for a PGP Signature sub-packet.</remarks>
+ public class SignatureSubpacket
+ {
+ private readonly SignatureSubpacketTag type;
+ private readonly bool critical;
+
+ internal readonly byte[] data;
+
+ protected internal SignatureSubpacket(
+ SignatureSubpacketTag type,
+ bool critical,
+ byte[] data)
+ {
+ this.type = type;
+ this.critical = critical;
+ this.data = data;
+ }
+
+ public SignatureSubpacketTag SubpacketType
+ {
+ get { return type; }
+ }
+
+ public bool IsCritical()
+ {
+ return critical;
+ }
+
+ /// <summary>Return the generic data making up the packet.</summary>
+ public byte[] GetData()
+ {
+ return (byte[]) data.Clone();
+ }
+
+ public void Encode(
+ Stream os)
+ {
+ int bodyLen = data.Length + 1;
+
+ if (bodyLen < 192)
+ {
+ os.WriteByte((byte)bodyLen);
+ }
+ else if (bodyLen <= 8383)
+ {
+ bodyLen -= 192;
+
+ os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+ os.WriteByte((byte)bodyLen);
+ }
+ else
+ {
+ os.WriteByte(0xff);
+ os.WriteByte((byte)(bodyLen >> 24));
+ os.WriteByte((byte)(bodyLen >> 16));
+ os.WriteByte((byte)(bodyLen >> 8));
+ os.WriteByte((byte)bodyLen);
+ }
+
+ if (critical)
+ {
+ os.WriteByte((byte)(0x80 | (int) type));
+ }
+ else
+ {
+ os.WriteByte((byte) type);
+ }
+
+ os.Write(data, 0, data.Length);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SignatureSubpacketTags.cs b/Crypto/src/bcpg/SignatureSubpacketTags.cs
new file mode 100644
index 000000000..1a8e254c0
--- /dev/null
+++ b/Crypto/src/bcpg/SignatureSubpacketTags.cs
@@ -0,0 +1,33 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic PGP signature sub-packet tag types.
+ */
+ public enum SignatureSubpacketTag
+ {
+ CreationTime = 2, // signature creation time
+ ExpireTime = 3, // signature expiration time
+ Exportable = 4, // exportable certification
+ TrustSig = 5, // trust signature
+ RegExp = 6, // regular expression
+ Revocable = 7, // revocable
+ KeyExpireTime = 9, // key expiration time
+ Placeholder = 10, // placeholder for backward compatibility
+ PreferredSymmetricAlgorithms = 11, // preferred symmetric algorithms
+ RevocationKey = 12, // revocation key
+ IssuerKeyId = 16, // issuer key ID
+ NotationData = 20, // notation data
+ PreferredHashAlgorithms = 21, // preferred hash algorithms
+ PreferredCompressionAlgorithms = 22, // preferred compression algorithms
+ KeyServerPreferences = 23, // key server preferences
+ PreferredKeyServer = 24, // preferred key server
+ PrimaryUserId = 25, // primary user id
+ PolicyUrl = 26, // policy URL
+ KeyFlags = 27, // key flags
+ SignerUserId = 28, // signer's user id
+ RevocationReason = 29, // reason for revocation
+ Features = 30, // features
+ SignatureTarget = 31, // signature target
+ EmbeddedSignature = 32 // embedded signature
+ }
+}
diff --git a/Crypto/src/bcpg/SignatureSubpacketsReader.cs b/Crypto/src/bcpg/SignatureSubpacketsReader.cs
new file mode 100644
index 000000000..8dd1af332
--- /dev/null
+++ b/Crypto/src/bcpg/SignatureSubpacketsReader.cs
@@ -0,0 +1,88 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * reader for signature sub-packets
+ */
+ public class SignatureSubpacketsParser
+ {
+ private readonly Stream input;
+
+ public SignatureSubpacketsParser(
+ Stream input)
+ {
+ this.input = input;
+ }
+
+ public SignatureSubpacket ReadPacket()
+ {
+ int l = input.ReadByte();
+ if (l < 0)
+ return null;
+
+ int bodyLen = 0;
+ if (l < 192)
+ {
+ bodyLen = l;
+ }
+ else if (l <= 223)
+ {
+ bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192;
+ }
+ else if (l == 255)
+ {
+ bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
+ | (input.ReadByte() << 8) | input.ReadByte();
+ }
+ else
+ {
+ // TODO Error?
+ }
+
+ int tag = input.ReadByte();
+ if (tag < 0)
+ throw new EndOfStreamException("unexpected EOF reading signature sub packet");
+
+ byte[] data = new byte[bodyLen - 1];
+ if (Streams.ReadFully(input, data) < data.Length)
+ throw new EndOfStreamException();
+
+ bool isCritical = ((tag & 0x80) != 0);
+ SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);
+ switch (type)
+ {
+ case SignatureSubpacketTag.CreationTime:
+ return new SignatureCreationTime(isCritical, data);
+ case SignatureSubpacketTag.KeyExpireTime:
+ return new KeyExpirationTime(isCritical, data);
+ case SignatureSubpacketTag.ExpireTime:
+ return new SignatureExpirationTime(isCritical, data);
+ case SignatureSubpacketTag.Revocable:
+ return new Revocable(isCritical, data);
+ case SignatureSubpacketTag.Exportable:
+ return new Exportable(isCritical, data);
+ case SignatureSubpacketTag.IssuerKeyId:
+ return new IssuerKeyId(isCritical, data);
+ case SignatureSubpacketTag.TrustSig:
+ return new TrustSignature(isCritical, data);
+ case SignatureSubpacketTag.PreferredCompressionAlgorithms:
+ case SignatureSubpacketTag.PreferredHashAlgorithms:
+ case SignatureSubpacketTag.PreferredSymmetricAlgorithms:
+ return new PreferredAlgorithms(type, isCritical, data);
+ case SignatureSubpacketTag.KeyFlags:
+ return new KeyFlags(isCritical, data);
+ case SignatureSubpacketTag.PrimaryUserId:
+ return new PrimaryUserId(isCritical, data);
+ case SignatureSubpacketTag.SignerUserId:
+ return new SignerUserId(isCritical, data);
+ case SignatureSubpacketTag.NotationData:
+ return new NotationData(isCritical, data);
+ }
+ return new SignatureSubpacket(type, isCritical, data);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SymmetricEncDataPacket.cs b/Crypto/src/bcpg/SymmetricEncDataPacket.cs
new file mode 100644
index 000000000..17ee55bb7
--- /dev/null
+++ b/Crypto/src/bcpg/SymmetricEncDataPacket.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic type for a symmetric key encrypted packet.</remarks>
+ public class SymmetricEncDataPacket
+ : InputStreamPacket
+ {
+ public SymmetricEncDataPacket(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs b/Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs
new file mode 100644
index 000000000..a9b6d0678
--- /dev/null
+++ b/Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs
@@ -0,0 +1,18 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ public class SymmetricEncIntegrityPacket
+ : InputStreamPacket
+ {
+ internal readonly int version;
+
+ internal SymmetricEncIntegrityPacket(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ version = bcpgIn.ReadByte();
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs b/Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs
new file mode 100644
index 000000000..7633b1dba
--- /dev/null
+++ b/Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic tags for symmetric key algorithms
+ */
+ public enum SymmetricKeyAlgorithmTag
+ {
+ Null = 0, // Plaintext or unencrypted data
+ Idea = 1, // IDEA [IDEA]
+ TripleDes = 2, // Triple-DES (DES-EDE, as per spec -168 bit key derived from 192)
+ Cast5 = 3, // Cast5 (128 bit key, as per RFC 2144)
+ Blowfish = 4, // Blowfish (128 bit key, 16 rounds) [Blowfish]
+ Safer = 5, // Safer-SK128 (13 rounds) [Safer]
+ Des = 6, // Reserved for DES/SK
+ Aes128 = 7, // Reserved for AES with 128-bit key
+ Aes192 = 8, // Reserved for AES with 192-bit key
+ Aes256 = 9, // Reserved for AES with 256-bit key
+ Twofish = 10 // Reserved for Twofish
+ }
+}
diff --git a/Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs b/Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
new file mode 100644
index 000000000..0381fa386
--- /dev/null
+++ b/Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic type for a symmetric encrypted session key packet
+ */
+ public class SymmetricKeyEncSessionPacket
+ : ContainedPacket
+ {
+ private int version;
+ private SymmetricKeyAlgorithmTag encAlgorithm;
+ private S2k s2k;
+ private readonly byte[] secKeyData;
+
+ public SymmetricKeyEncSessionPacket(
+ BcpgInputStream bcpgIn)
+ {
+ version = bcpgIn.ReadByte();
+ encAlgorithm = (SymmetricKeyAlgorithmTag) bcpgIn.ReadByte();
+
+ s2k = new S2k(bcpgIn);
+
+ secKeyData = bcpgIn.ReadAll();
+ }
+
+ public SymmetricKeyEncSessionPacket(
+ SymmetricKeyAlgorithmTag encAlgorithm,
+ S2k s2k,
+ byte[] secKeyData)
+ {
+ this.version = 4;
+ this.encAlgorithm = encAlgorithm;
+ this.s2k = s2k;
+ this.secKeyData = secKeyData;
+ }
+
+ /**
+ * @return int
+ */
+ public SymmetricKeyAlgorithmTag EncAlgorithm
+ {
+ get { return encAlgorithm; }
+ }
+
+ /**
+ * @return S2k
+ */
+ public S2k S2k
+ {
+ get { return s2k; }
+ }
+
+ /**
+ * @return byte[]
+ */
+ public byte[] GetSecKeyData()
+ {
+ return secKeyData;
+ }
+
+ /**
+ * @return int
+ */
+ public int Version
+ {
+ get { return version; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ MemoryStream bOut = new MemoryStream();
+ BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+ pOut.Write(
+ (byte) version,
+ (byte) encAlgorithm);
+
+ pOut.WriteObject(s2k);
+
+ if (secKeyData != null && secKeyData.Length > 0)
+ {
+ pOut.Write(secKeyData);
+ }
+
+ bcpgOut.WritePacket(PacketTag.SymmetricKeyEncryptedSessionKey, bOut.ToArray(), true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/TrustPacket.cs b/Crypto/src/bcpg/TrustPacket.cs
new file mode 100644
index 000000000..6f1969c2a
--- /dev/null
+++ b/Crypto/src/bcpg/TrustPacket.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <summary>Basic type for a trust packet.</summary>
+ public class TrustPacket
+ : ContainedPacket
+ {
+ private readonly byte[] levelAndTrustAmount;
+
+ public TrustPacket(
+ BcpgInputStream bcpgIn)
+ {
+ MemoryStream bOut = new MemoryStream();
+
+ int ch;
+ while ((ch = bcpgIn.ReadByte()) >= 0)
+ {
+ bOut.WriteByte((byte) ch);
+ }
+
+ levelAndTrustAmount = bOut.ToArray();
+ }
+
+ public TrustPacket(
+ int trustCode)
+ {
+ this.levelAndTrustAmount = new byte[]{ (byte) trustCode };
+ }
+
+ public byte[] GetLevelAndTrustAmount()
+ {
+ return (byte[]) levelAndTrustAmount.Clone();
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.Trust, levelAndTrustAmount, true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/UserAttributePacket.cs b/Crypto/src/bcpg/UserAttributePacket.cs
new file mode 100644
index 000000000..20e3598ab
--- /dev/null
+++ b/Crypto/src/bcpg/UserAttributePacket.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic type for a user attribute packet.
+ */
+ public class UserAttributePacket
+ : ContainedPacket
+ {
+ private readonly UserAttributeSubpacket[] subpackets;
+
+ public UserAttributePacket(
+ BcpgInputStream bcpgIn)
+ {
+ UserAttributeSubpacketsParser sIn = new UserAttributeSubpacketsParser(bcpgIn);
+ UserAttributeSubpacket sub;
+
+ IList v = Platform.CreateArrayList();
+ while ((sub = sIn.ReadPacket()) != null)
+ {
+ v.Add(sub);
+ }
+
+ subpackets = new UserAttributeSubpacket[v.Count];
+
+ for (int i = 0; i != subpackets.Length; i++)
+ {
+ subpackets[i] = (UserAttributeSubpacket)v[i];
+ }
+ }
+
+ public UserAttributePacket(
+ UserAttributeSubpacket[] subpackets)
+ {
+ this.subpackets = subpackets;
+ }
+
+ public UserAttributeSubpacket[] GetSubpackets()
+ {
+ return subpackets;
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ MemoryStream bOut = new MemoryStream();
+
+ for (int i = 0; i != subpackets.Length; i++)
+ {
+ subpackets[i].Encode(bOut);
+ }
+
+ bcpgOut.WritePacket(PacketTag.UserAttribute, bOut.ToArray(), false);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/UserAttributeSubpacket.cs b/Crypto/src/bcpg/UserAttributeSubpacket.cs
new file mode 100644
index 000000000..bd49d2150
--- /dev/null
+++ b/Crypto/src/bcpg/UserAttributeSubpacket.cs
@@ -0,0 +1,86 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic type for a user attribute sub-packet.
+ */
+ public class UserAttributeSubpacket
+ {
+ private readonly UserAttributeSubpacketTag type;
+ private readonly byte[] data;
+
+ internal UserAttributeSubpacket(
+ UserAttributeSubpacketTag type,
+ byte[] data)
+ {
+ this.type = type;
+ this.data = data;
+ }
+
+ public UserAttributeSubpacketTag SubpacketType
+ {
+ get { return type; }
+ }
+
+ /**
+ * return the generic data making up the packet.
+ */
+ public byte[] GetData()
+ {
+ return data;
+ }
+
+ public void Encode(
+ Stream os)
+ {
+ int bodyLen = data.Length + 1;
+
+ if (bodyLen < 192)
+ {
+ os.WriteByte((byte)bodyLen);
+ }
+ else if (bodyLen <= 8383)
+ {
+ bodyLen -= 192;
+
+ os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+ os.WriteByte((byte)bodyLen);
+ }
+ else
+ {
+ os.WriteByte(0xff);
+ os.WriteByte((byte)(bodyLen >> 24));
+ os.WriteByte((byte)(bodyLen >> 16));
+ os.WriteByte((byte)(bodyLen >> 8));
+ os.WriteByte((byte)bodyLen);
+ }
+
+ os.WriteByte((byte) type);
+ os.Write(data, 0, data.Length);
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ UserAttributeSubpacket other = obj as UserAttributeSubpacket;
+
+ if (other == null)
+ return false;
+
+ return type == other.type
+ && Arrays.AreEqual(data, other.data);
+ }
+
+ public override int GetHashCode()
+ {
+ return type.GetHashCode() ^ Arrays.GetHashCode(data);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/UserAttributeSubpacketTags.cs b/Crypto/src/bcpg/UserAttributeSubpacketTags.cs
new file mode 100644
index 000000000..7a9cd1d5d
--- /dev/null
+++ b/Crypto/src/bcpg/UserAttributeSubpacketTags.cs
@@ -0,0 +1,10 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic PGP user attribute sub-packet tag types.
+ */
+ public enum UserAttributeSubpacketTag
+ {
+ ImageAttribute = 1
+ }
+}
diff --git a/Crypto/src/bcpg/UserAttributeSubpacketsReader.cs b/Crypto/src/bcpg/UserAttributeSubpacketsReader.cs
new file mode 100644
index 000000000..2e5ea0f3e
--- /dev/null
+++ b/Crypto/src/bcpg/UserAttributeSubpacketsReader.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Bcpg.Attr;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * reader for user attribute sub-packets
+ */
+ public class UserAttributeSubpacketsParser
+ {
+ private readonly Stream input;
+
+ public UserAttributeSubpacketsParser(
+ Stream input)
+ {
+ this.input = input;
+ }
+
+ public UserAttributeSubpacket ReadPacket()
+ {
+ int l = input.ReadByte();
+ if (l < 0)
+ return null;
+
+ int bodyLen = 0;
+ if (l < 192)
+ {
+ bodyLen = l;
+ }
+ else if (l <= 223)
+ {
+ bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192;
+ }
+ else if (l == 255)
+ {
+ bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
+ | (input.ReadByte() << 8) | input.ReadByte();
+ }
+ else
+ {
+ // TODO Error?
+ }
+
+ int tag = input.ReadByte();
+ if (tag < 0)
+ throw new EndOfStreamException("unexpected EOF reading user attribute sub packet");
+
+ byte[] data = new byte[bodyLen - 1];
+ if (Streams.ReadFully(input, data) < data.Length)
+ throw new EndOfStreamException();
+
+ UserAttributeSubpacketTag type = (UserAttributeSubpacketTag) tag;
+ switch (type)
+ {
+ case UserAttributeSubpacketTag.ImageAttribute:
+ return new ImageAttrib(data);
+ }
+ return new UserAttributeSubpacket(type, data);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/UserIdPacket.cs b/Crypto/src/bcpg/UserIdPacket.cs
new file mode 100644
index 000000000..a175e74a6
--- /dev/null
+++ b/Crypto/src/bcpg/UserIdPacket.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Basic type for a user ID packet.
+ */
+ public class UserIdPacket
+ : ContainedPacket
+ {
+ private readonly byte[] idData;
+
+ public UserIdPacket(
+ BcpgInputStream bcpgIn)
+ {
+ this.idData = bcpgIn.ReadAll();
+ }
+
+ public UserIdPacket(
+ string id)
+ {
+ this.idData = Encoding.UTF8.GetBytes(id);
+ }
+
+ public string GetId()
+ {
+ return Encoding.UTF8.GetString(idData, 0, idData.Length);
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WritePacket(PacketTag.UserId, idData, true);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/attr/ImageAttrib.cs b/Crypto/src/bcpg/attr/ImageAttrib.cs
new file mode 100644
index 000000000..73490791c
--- /dev/null
+++ b/Crypto/src/bcpg/attr/ImageAttrib.cs
@@ -0,0 +1,68 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg.Attr
+{
+ /// <remarks>Basic type for a image attribute packet.</remarks>
+ public class ImageAttrib
+ : UserAttributeSubpacket
+ {
+ public enum Format : byte
+ {
+ Jpeg = 1
+ }
+
+ private static readonly byte[] Zeroes = new byte[12];
+
+ private int hdrLength;
+ private int _version;
+ private int _encoding;
+ private byte[] imageData;
+
+ public ImageAttrib(
+ byte[] data)
+ : base(UserAttributeSubpacketTag.ImageAttribute, data)
+ {
+ hdrLength = ((data[1] & 0xff) << 8) | (data[0] & 0xff);
+ _version = data[2] & 0xff;
+ _encoding = data[3] & 0xff;
+
+ imageData = new byte[data.Length - hdrLength];
+ Array.Copy(data, hdrLength, imageData, 0, imageData.Length);
+ }
+
+ public ImageAttrib(
+ Format imageType,
+ byte[] imageData)
+ : this(ToByteArray(imageType, imageData))
+ {
+ }
+
+ private static byte[] ToByteArray(
+ Format imageType,
+ byte[] imageData)
+ {
+ MemoryStream bOut = new MemoryStream();
+ bOut.WriteByte(0x10); bOut.WriteByte(0x00); bOut.WriteByte(0x01);
+ bOut.WriteByte((byte) imageType);
+ bOut.Write(Zeroes, 0, Zeroes.Length);
+ bOut.Write(imageData, 0, imageData.Length);
+ return bOut.ToArray();
+ }
+
+ public int Version
+ {
+ get { return _version; }
+ }
+
+ public int Encoding
+ {
+ get { return _encoding; }
+ }
+
+ public byte[] GetImageData()
+ {
+ return imageData;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/EmbeddedSignature.cs b/Crypto/src/bcpg/sig/EmbeddedSignature.cs
new file mode 100644
index 000000000..e47604ac8
--- /dev/null
+++ b/Crypto/src/bcpg/sig/EmbeddedSignature.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * Packet embedded signature
+ */
+ public class EmbeddedSignature
+ : SignatureSubpacket
+ {
+ public EmbeddedSignature(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.EmbeddedSignature, critical, data)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/Exportable.cs b/Crypto/src/bcpg/sig/Exportable.cs
new file mode 100644
index 000000000..4455c3814
--- /dev/null
+++ b/Crypto/src/bcpg/sig/Exportable.cs
@@ -0,0 +1,47 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving signature creation time.
+ */
+ public class Exportable
+ : SignatureSubpacket
+ {
+ private static byte[] BooleanToByteArray(bool val)
+ {
+ byte[] data = new byte[1];
+
+ if (val)
+ {
+ data[0] = 1;
+ return data;
+ }
+ else
+ {
+ return data;
+ }
+ }
+
+ public Exportable(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.Exportable, critical, data)
+ {
+ }
+
+ public Exportable(
+ bool critical,
+ bool isExportable)
+ : base(SignatureSubpacketTag.Exportable, critical, BooleanToByteArray(isExportable))
+ {
+ }
+
+ public bool IsExportable()
+ {
+ return data[0] != 0;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/IssuerKeyId.cs b/Crypto/src/bcpg/sig/IssuerKeyId.cs
new file mode 100644
index 000000000..91490d33b
--- /dev/null
+++ b/Crypto/src/bcpg/sig/IssuerKeyId.cs
@@ -0,0 +1,61 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving signature creation time.
+ */
+ public class IssuerKeyId
+ : SignatureSubpacket
+ {
+ protected static byte[] KeyIdToBytes(
+ long keyId)
+ {
+ byte[] data = new byte[8];
+
+ data[0] = (byte)(keyId >> 56);
+ data[1] = (byte)(keyId >> 48);
+ data[2] = (byte)(keyId >> 40);
+ data[3] = (byte)(keyId >> 32);
+ data[4] = (byte)(keyId >> 24);
+ data[5] = (byte)(keyId >> 16);
+ data[6] = (byte)(keyId >> 8);
+ data[7] = (byte)keyId;
+
+ return data;
+ }
+
+ public IssuerKeyId(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.IssuerKeyId, critical, data)
+ {
+ }
+
+ public IssuerKeyId(
+ bool critical,
+ long keyId)
+ : base(SignatureSubpacketTag.IssuerKeyId, critical, KeyIdToBytes(keyId))
+ {
+ }
+
+ public long KeyId
+ {
+ get
+ {
+ long keyId = ((long)(data[0] & 0xff) << 56)
+ | ((long)(data[1] & 0xff) << 48)
+ | ((long)(data[2] & 0xff) << 40)
+ | ((long)(data[3] & 0xff) << 32)
+ | ((long)(data[4] & 0xff) << 24)
+ | ((long)(data[5] & 0xff) << 16)
+ | ((long)(data[6] & 0xff) << 8)
+ | ((long)data[7] & 0xff);
+
+ return keyId;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/KeyExpirationTime.cs b/Crypto/src/bcpg/sig/KeyExpirationTime.cs
new file mode 100644
index 000000000..23b4cac29
--- /dev/null
+++ b/Crypto/src/bcpg/sig/KeyExpirationTime.cs
@@ -0,0 +1,56 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving time after creation at which the key expires.
+ */
+ public class KeyExpirationTime
+ : SignatureSubpacket
+ {
+ protected static byte[] TimeToBytes(
+ long t)
+ {
+ byte[] data = new byte[4];
+
+ data[0] = (byte)(t >> 24);
+ data[1] = (byte)(t >> 16);
+ data[2] = (byte)(t >> 8);
+ data[3] = (byte)t;
+
+ return data;
+ }
+
+ public KeyExpirationTime(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.KeyExpireTime, critical, data)
+ {
+ }
+
+ public KeyExpirationTime(
+ bool critical,
+ long seconds)
+ : base(SignatureSubpacketTag.KeyExpireTime, critical, TimeToBytes(seconds))
+ {
+ }
+
+ /**
+ * Return the number of seconds after creation time a key is valid for.
+ *
+ * @return second count for key validity.
+ */
+ public long Time
+ {
+ get
+ {
+ long time = ((long)(data[0] & 0xff) << 24) | ((long)(data[1] & 0xff) << 16)
+ | ((long)(data[2] & 0xff) << 8) | ((long)data[3] & 0xff);
+
+ return time;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/KeyFlags.cs b/Crypto/src/bcpg/sig/KeyFlags.cs
new file mode 100644
index 000000000..0592301b3
--- /dev/null
+++ b/Crypto/src/bcpg/sig/KeyFlags.cs
@@ -0,0 +1,74 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * Packet holding the key flag values.
+ */
+ public class KeyFlags
+ : SignatureSubpacket
+ {
+ public const int CertifyOther = 0x01;
+ public const int SignData = 0x02;
+ public const int EncryptComms = 0x04;
+ public const int EncryptStorage = 0x08;
+ public const int Split = 0x10;
+ public const int Authentication = 0x20;
+ public const int Shared = 0x80;
+
+ private static byte[] IntToByteArray(
+ int v)
+ {
+ byte[] tmp = new byte[4];
+ int size = 0;
+
+ for (int i = 0; i != 4; i++)
+ {
+ tmp[i] = (byte)(v >> (i * 8));
+ if (tmp[i] != 0)
+ {
+ size = i;
+ }
+ }
+
+ byte[] data = new byte[size + 1];
+ Array.Copy(tmp, 0, data, 0, data.Length);
+ return data;
+ }
+
+ public KeyFlags(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.KeyFlags, critical, data)
+ {
+ }
+
+ public KeyFlags(
+ bool critical,
+ int flags)
+ : base(SignatureSubpacketTag.KeyFlags, critical, IntToByteArray(flags))
+ {
+ }
+
+ /// <summary>
+ /// Return the flag values contained in the first 4 octets (note: at the moment
+ /// the standard only uses the first one).
+ /// </summary>
+ public int Flags
+ {
+ get
+ {
+ int flags = 0;
+
+ for (int i = 0; i != data.Length; i++)
+ {
+ flags |= (data[i] & 0xff) << (i * 8);
+ }
+
+ return flags;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/NotationData.cs b/Crypto/src/bcpg/sig/NotationData.cs
new file mode 100644
index 000000000..ccc9aa745
--- /dev/null
+++ b/Crypto/src/bcpg/sig/NotationData.cs
@@ -0,0 +1,112 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * Class provided a NotationData object according to
+ * RFC2440, Chapter 5.2.3.15. Notation Data
+ */
+ public class NotationData
+ : SignatureSubpacket
+ {
+ public const int HeaderFlagLength = 4;
+ public const int HeaderNameLength = 2;
+ public const int HeaderValueLength = 2;
+
+ public NotationData(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.NotationData, critical, data)
+ {
+ }
+
+ public NotationData(
+ bool critical,
+ bool humanReadable,
+ string notationName,
+ string notationValue)
+ : base(SignatureSubpacketTag.NotationData, critical,
+ createData(humanReadable, notationName, notationValue))
+ {
+ }
+
+ private static byte[] createData(
+ bool humanReadable,
+ string notationName,
+ string notationValue)
+ {
+ MemoryStream os = new MemoryStream();
+
+ // (4 octets of flags, 2 octets of name length (M),
+ // 2 octets of value length (N),
+ // M octets of name data,
+ // N octets of value data)
+
+ // flags
+ os.WriteByte(humanReadable ? (byte)0x80 : (byte)0x00);
+ os.WriteByte(0x0);
+ os.WriteByte(0x0);
+ os.WriteByte(0x0);
+
+ byte[] nameData, valueData = null;
+ int nameLength, valueLength;
+
+ nameData = Encoding.UTF8.GetBytes(notationName);
+ nameLength = System.Math.Min(nameData.Length, 0xFF);
+
+ valueData = Encoding.UTF8.GetBytes(notationValue);
+ valueLength = System.Math.Min(valueData.Length, 0xFF);
+
+ // name length
+ os.WriteByte((byte)(nameLength >> 8));
+ os.WriteByte((byte)(nameLength >> 0));
+
+ // value length
+ os.WriteByte((byte)(valueLength >> 8));
+ os.WriteByte((byte)(valueLength >> 0));
+
+ // name
+ os.Write(nameData, 0, nameLength);
+
+ // value
+ os.Write(valueData, 0, valueLength);
+
+ return os.ToArray();
+ }
+
+ public bool IsHumanReadable
+ {
+ get { return data[0] == (byte)0x80; }
+ }
+
+ public string GetNotationName()
+ {
+ int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0));
+ int namePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength;
+
+ return Encoding.UTF8.GetString(data, namePos, nameLength);
+ }
+
+ public string GetNotationValue()
+ {
+ int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0));
+ int valueLength = ((data[HeaderFlagLength + HeaderNameLength] << 8) + (data[HeaderFlagLength + HeaderNameLength + 1] << 0));
+ int valuePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength + nameLength;
+
+ return Encoding.UTF8.GetString(data, valuePos, valueLength);
+ }
+
+ public byte[] GetNotationValueBytes()
+ {
+ int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0));
+ int valueLength = ((data[HeaderFlagLength + HeaderNameLength] << 8) + (data[HeaderFlagLength + HeaderNameLength + 1] << 0));
+ int valuePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength + nameLength;
+
+ byte[] bytes = new byte[valueLength];
+ Array.Copy(data, valuePos, bytes, 0, valueLength);
+ return bytes;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/PreferredAlgorithms.cs b/Crypto/src/bcpg/sig/PreferredAlgorithms.cs
new file mode 100644
index 000000000..0f282a38c
--- /dev/null
+++ b/Crypto/src/bcpg/sig/PreferredAlgorithms.cs
@@ -0,0 +1,54 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving signature creation time.
+ */
+ public class PreferredAlgorithms
+ : SignatureSubpacket
+ {
+ private static byte[] IntToByteArray(
+ int[] v)
+ {
+ byte[] data = new byte[v.Length];
+
+ for (int i = 0; i != v.Length; i++)
+ {
+ data[i] = (byte)v[i];
+ }
+
+ return data;
+ }
+
+ public PreferredAlgorithms(
+ SignatureSubpacketTag type,
+ bool critical,
+ byte[] data)
+ : base(type, critical, data)
+ {
+ }
+
+ public PreferredAlgorithms(
+ SignatureSubpacketTag type,
+ bool critical,
+ int[] preferences)
+ : base(type, critical, IntToByteArray(preferences))
+ {
+ }
+
+ public int[] GetPreferences()
+ {
+ int[] v = new int[data.Length];
+
+ for (int i = 0; i != v.Length; i++)
+ {
+ v[i] = data[i] & 0xff;
+ }
+
+ return v;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/PrimaryUserId.cs b/Crypto/src/bcpg/sig/PrimaryUserId.cs
new file mode 100644
index 000000000..fc0353afd
--- /dev/null
+++ b/Crypto/src/bcpg/sig/PrimaryUserId.cs
@@ -0,0 +1,48 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving whether or not the signature is signed using the primary user ID for the key.
+ */
+ public class PrimaryUserId
+ : SignatureSubpacket
+ {
+ private static byte[] BooleanToByteArray(
+ bool val)
+ {
+ byte[] data = new byte[1];
+
+ if (val)
+ {
+ data[0] = 1;
+ return data;
+ }
+ else
+ {
+ return data;
+ }
+ }
+
+ public PrimaryUserId(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.PrimaryUserId, critical, data)
+ {
+ }
+
+ public PrimaryUserId(
+ bool critical,
+ bool isPrimaryUserId)
+ : base(SignatureSubpacketTag.PrimaryUserId, critical, BooleanToByteArray(isPrimaryUserId))
+ {
+ }
+
+ public bool IsPrimaryUserId()
+ {
+ return data[0] != 0;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/Revocable.cs b/Crypto/src/bcpg/sig/Revocable.cs
new file mode 100644
index 000000000..b5e94feec
--- /dev/null
+++ b/Crypto/src/bcpg/sig/Revocable.cs
@@ -0,0 +1,48 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving whether or not is revocable.
+ */
+ public class Revocable
+ : SignatureSubpacket
+ {
+ private static byte[] BooleanToByteArray(
+ bool value)
+ {
+ byte[] data = new byte[1];
+
+ if (value)
+ {
+ data[0] = 1;
+ return data;
+ }
+ else
+ {
+ return data;
+ }
+ }
+
+ public Revocable(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.Revocable, critical, data)
+ {
+ }
+
+ public Revocable(
+ bool critical,
+ bool isRevocable)
+ : base(SignatureSubpacketTag.Revocable, critical, BooleanToByteArray(isRevocable))
+ {
+ }
+
+ public bool IsRevocable()
+ {
+ return data[0] != 0;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/RevocationKey.cs b/Crypto/src/bcpg/sig/RevocationKey.cs
new file mode 100644
index 000000000..5e388246b
--- /dev/null
+++ b/Crypto/src/bcpg/sig/RevocationKey.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <summary>
+ /// Represents revocation key OpenPGP signature sub packet.
+ /// </summary>
+ public class RevocationKey
+ : SignatureSubpacket
+ {
+ // 1 octet of class,
+ // 1 octet of public-key algorithm ID,
+ // 20 octets of fingerprint
+ public RevocationKey(
+ bool isCritical,
+ byte[] data)
+ : base(SignatureSubpacketTag.RevocationKey, isCritical, data)
+ {
+ }
+
+ public RevocationKey(
+ bool isCritical,
+ RevocationKeyTag signatureClass,
+ PublicKeyAlgorithmTag keyAlgorithm,
+ byte[] fingerprint)
+ : base(SignatureSubpacketTag.RevocationKey, isCritical,
+ CreateData(signatureClass, keyAlgorithm, fingerprint))
+ {
+ }
+
+ private static byte[] CreateData(
+ RevocationKeyTag signatureClass,
+ PublicKeyAlgorithmTag keyAlgorithm,
+ byte[] fingerprint)
+ {
+ byte[] data = new byte[2 + fingerprint.Length];
+ data[0] = (byte)signatureClass;
+ data[1] = (byte)keyAlgorithm;
+ Array.Copy(fingerprint, 0, data, 2, fingerprint.Length);
+ return data;
+ }
+
+ public virtual RevocationKeyTag SignatureClass
+ {
+ get { return (RevocationKeyTag)this.GetData()[0]; }
+ }
+
+ public virtual PublicKeyAlgorithmTag Algorithm
+ {
+ get { return (PublicKeyAlgorithmTag)this.GetData()[1]; }
+ }
+
+ public virtual byte[] GetFingerprint()
+ {
+ byte[] data = this.GetData();
+ byte[] fingerprint = new byte[data.Length - 2];
+ Array.Copy(data, 2, fingerprint, 0, fingerprint.Length);
+ return fingerprint;
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/RevocationKeyTags.cs b/Crypto/src/bcpg/sig/RevocationKeyTags.cs
new file mode 100644
index 000000000..d76d1dcf4
--- /dev/null
+++ b/Crypto/src/bcpg/sig/RevocationKeyTags.cs
@@ -0,0 +1,9 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ public enum RevocationKeyTag
+ : byte
+ {
+ ClassDefault = 0x80,
+ ClassSensitive = 0x40
+ }
+}
diff --git a/Crypto/src/bcpg/sig/RevocationReason.cs b/Crypto/src/bcpg/sig/RevocationReason.cs
new file mode 100644
index 000000000..f32c1f64a
--- /dev/null
+++ b/Crypto/src/bcpg/sig/RevocationReason.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <summary>
+ /// Represents revocation reason OpenPGP signature sub packet.
+ /// </summary>
+ public class RevocationReason
+ : SignatureSubpacket
+ {
+ public RevocationReason(bool isCritical, byte[] data)
+ : base(SignatureSubpacketTag.RevocationReason, isCritical, data)
+ {
+ }
+
+ public RevocationReason(
+ bool isCritical,
+ RevocationReasonTag reason,
+ string description)
+ : base(SignatureSubpacketTag.RevocationReason, isCritical, CreateData(reason, description))
+ {
+ }
+
+ private static byte[] CreateData(
+ RevocationReasonTag reason,
+ string description)
+ {
+ byte[] descriptionBytes = Strings.ToUtf8ByteArray(description);
+ byte[] data = new byte[1 + descriptionBytes.Length];
+
+ data[0] = (byte)reason;
+ Array.Copy(descriptionBytes, 0, data, 1, descriptionBytes.Length);
+
+ return data;
+ }
+
+ public virtual RevocationReasonTag GetRevocationReason()
+ {
+ return (RevocationReasonTag)GetData()[0];
+ }
+
+ public virtual string GetRevocationDescription()
+ {
+ byte[] data = GetData();
+ if (data.Length == 1)
+ {
+ return string.Empty;
+ }
+
+ byte[] description = new byte[data.Length - 1];
+ Array.Copy(data, 1, description, 0, description.Length);
+
+ return Strings.FromUtf8ByteArray(description);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/RevocationReasonTags.cs b/Crypto/src/bcpg/sig/RevocationReasonTags.cs
new file mode 100644
index 000000000..524a58c49
--- /dev/null
+++ b/Crypto/src/bcpg/sig/RevocationReasonTags.cs
@@ -0,0 +1,14 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ public enum RevocationReasonTag
+ : byte
+ {
+ NoReason = 0, // No reason specified (key revocations or cert revocations)
+ KeySuperseded = 1, // Key is superseded (key revocations)
+ KeyCompromised = 2, // Key material has been compromised (key revocations)
+ KeyRetired = 3, // Key is retired and no longer used (key revocations)
+ UserNoLongerValid = 32, // User ID information is no longer valid (cert revocations)
+
+ // 100-110 - Private Use
+ }
+}
diff --git a/Crypto/src/bcpg/sig/SignatureCreationTime.cs b/Crypto/src/bcpg/sig/SignatureCreationTime.cs
new file mode 100644
index 000000000..e6f241f11
--- /dev/null
+++ b/Crypto/src/bcpg/sig/SignatureCreationTime.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving signature creation time.
+ */
+ public class SignatureCreationTime
+ : SignatureSubpacket
+ {
+ protected static byte[] TimeToBytes(
+ DateTime time)
+ {
+ long t = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L;
+ byte[] data = new byte[4];
+ data[0] = (byte)(t >> 24);
+ data[1] = (byte)(t >> 16);
+ data[2] = (byte)(t >> 8);
+ data[3] = (byte)t;
+ return data;
+ }
+ public SignatureCreationTime(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.CreationTime, critical, data)
+ {
+ }
+ public SignatureCreationTime(
+ bool critical,
+ DateTime date)
+ : base(SignatureSubpacketTag.CreationTime, critical, TimeToBytes(date))
+ {
+ }
+ public DateTime GetTime()
+ {
+ long time = (long)(
+ ((uint)data[0] << 24)
+ | ((uint)data[1] << 16)
+ | ((uint)data[2] << 8)
+ | ((uint)data[3])
+ );
+ return DateTimeUtilities.UnixMsToDateTime(time * 1000L);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/SignatureExpirationTime.cs b/Crypto/src/bcpg/sig/SignatureExpirationTime.cs
new file mode 100644
index 000000000..7fddf5743
--- /dev/null
+++ b/Crypto/src/bcpg/sig/SignatureExpirationTime.cs
@@ -0,0 +1,54 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving signature expiration time.
+ */
+ public class SignatureExpirationTime
+ : SignatureSubpacket
+ {
+ protected static byte[] TimeToBytes(
+ long t)
+ {
+ byte[] data = new byte[4];
+
+ data[0] = (byte)(t >> 24);
+ data[1] = (byte)(t >> 16);
+ data[2] = (byte)(t >> 8);
+ data[3] = (byte)t;
+
+ return data;
+ }
+
+ public SignatureExpirationTime(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.ExpireTime, critical, data)
+ {
+ }
+
+ public SignatureExpirationTime(
+ bool critical,
+ long seconds)
+ : base(SignatureSubpacketTag.ExpireTime, critical, TimeToBytes(seconds))
+ {
+ }
+
+ /**
+ * return time in seconds before signature expires after creation time.
+ */
+ public long Time
+ {
+ get
+ {
+ long time = ((long)(data[0] & 0xff) << 24) | ((long)(data[1] & 0xff) << 16)
+ | ((long)(data[2] & 0xff) << 8) | ((long)data[3] & 0xff);
+
+ return time;
+ }
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/SignerUserId.cs b/Crypto/src/bcpg/sig/SignerUserId.cs
new file mode 100644
index 000000000..98cc808e7
--- /dev/null
+++ b/Crypto/src/bcpg/sig/SignerUserId.cs
@@ -0,0 +1,52 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving the User ID of the signer.
+ */
+ public class SignerUserId
+ : SignatureSubpacket
+ {
+ private static byte[] UserIdToBytes(
+ string id)
+ {
+ byte[] idData = new byte[id.Length];
+
+ for (int i = 0; i != id.Length; i++)
+ {
+ idData[i] = (byte)id[i];
+ }
+
+ return idData;
+ }
+
+ public SignerUserId(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.SignerUserId, critical, data)
+ {
+ }
+
+ public SignerUserId(
+ bool critical,
+ string userId)
+ : base(SignatureSubpacketTag.SignerUserId, critical, UserIdToBytes(userId))
+ {
+ }
+
+ public string GetId()
+ {
+ char[] chars = new char[data.Length];
+
+ for (int i = 0; i != chars.Length; i++)
+ {
+ chars[i] = (char)(data[i] & 0xff);
+ }
+
+ return new string(chars);
+ }
+ }
+}
diff --git a/Crypto/src/bcpg/sig/TrustSignature.cs b/Crypto/src/bcpg/sig/TrustSignature.cs
new file mode 100644
index 000000000..bbadd3067
--- /dev/null
+++ b/Crypto/src/bcpg/sig/TrustSignature.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving trust.
+ */
+ public class TrustSignature
+ : SignatureSubpacket
+ {
+ private static byte[] IntToByteArray(
+ int v1,
+ int v2)
+ {
+ return new byte[]{ (byte)v1, (byte)v2 };
+ }
+
+ public TrustSignature(
+ bool critical,
+ byte[] data)
+ : base(SignatureSubpacketTag.TrustSig, critical, data)
+ {
+ }
+
+ public TrustSignature(
+ bool critical,
+ int depth,
+ int trustAmount)
+ : base(SignatureSubpacketTag.TrustSig, critical, IntToByteArray(depth, trustAmount))
+ {
+ }
+
+ public int Depth
+ {
+ get { return data[0] & 0xff; }
+ }
+
+ public int TrustAmount
+ {
+ get { return data[1] & 0xff; }
+ }
+ }
+}
|