diff options
Diffstat (limited to 'crypto/src/bcpg/ArmoredInputStream.cs')
-rw-r--r-- | crypto/src/bcpg/ArmoredInputStream.cs | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs new file mode 100644 index 000000000..3109dd4fd --- /dev/null +++ b/crypto/src/bcpg/ArmoredInputStream.cs @@ -0,0 +1,513 @@ +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; + } + + public override void Close() + { + input.Close(); + base.Close(); + } + } +} |