培训考核三期,新版培训,网页版培训登录器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

545 lines
16 KiB

#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Collections;
using System.IO;
using System.Text;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
namespace BestHTTP.SecureProtocol.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
* is detected and fails.
* <p>
* By default a missing CRC will not cause an exception. To force CRC detection use:
* <pre>
* ArmoredInputStream aIn = ...
*
* aIn.setDetectMissingCRC(true);
* </pre>
* </p>
*/
public class ArmoredInputStream
: BaseInputStream
{
/*
* set up the decoding table.
*/
private readonly static byte[] decodingTable;
static ArmoredInputStream()
{
decodingTable = new byte[128];
Arrays.Fill(decodingTable, 0xff);
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 static 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];
b2 = decodingTable[in1];
if ((b1 | b2) >= 128)
throw new IOException("invalid armor");
result[2] = ((b1 << 2) | (b2 >> 4)) & 0xff;
return 2;
}
else if (in3 == '=')
{
b1 = decodingTable[in0];
b2 = decodingTable[in1];
b3 = decodingTable[in2];
if ((b1 | b2 | b3) >= 128)
throw new IOException("invalid armor");
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];
if ((b1 | b2 | b3 | b4) >= 128)
throw new IOException("invalid armor");
result[0] = ((b1 << 2) | (b2 >> 4)) & 0xff;
result[1] = ((b2 << 4) | (b3 >> 2)) & 0xff;
result[2] = ((b3 << 6) | b4) & 0xff;
return 0;
}
}
/*
* Ignore missing CRC checksums.
* https://tests.sequoia-pgp.org/#ASCII_Armor suggests that missing CRC sums do not invalidate the message.
*/
private bool detectMissingChecksum = false;
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 = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.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 = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.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 buf = new StringBuilder("-");
bool eolReached = false;
bool crLf = false;
if (restart) // we've had to look ahead two '-'
{
buf.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 = buf.ToString();
if (line.Trim().Length < 1)
break;
if (headerList.Count > 0 && line.IndexOf(':') < 0)
throw new IOException("invalid armor header");
headerList.Add(line);
buf.Length = 0;
}
if (c != '\n' && c != '\r')
{
buf.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' || c == '\f' || c == '\u000B') ; // \u000B ~ \v
if (c >= 128)
throw new IOException("invalid armor");
return c;
}
public override int ReadByte()
{
if (start)
{
if (hasHeaders)
{
ParseHeaders();
}
crc.Reset();
start = false;
}
int c;
if (clearText)
{
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;
}
if (bufPtr > 2 || crcFound)
{
c = ReadIgnoreSpace();
if (c == '\r' || c == '\n')
{
c = ReadIgnoreSpace();
while (c == '\n' || c == '\r')
{
c = ReadIgnoreSpace();
}
if (c < 0) // EOF
{
isEndOfStream = true;
return -1;
}
if (c == '=') // crc reached
{
bufPtr = Decode(ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
if (bufPtr == 0)
{
int i = ((outBuf[0] & 0xff) << 16)
| ((outBuf[1] & 0xff) << 8)
| (outBuf[2] & 0xff);
crcFound = true;
if (i != crc.Value)
{
throw new IOException("crc check failed in armored message.");
}
return ReadByte();
}
else
{
if (detectMissingChecksum)
{
throw new IOException("no crc found in armored message");
}
}
}
else if (c == '-') // end of record reached
{
while ((c = input.ReadByte()) >= 0)
{
if (c == '\n' || c == '\r')
{
break;
}
}
if (!crcFound && detectMissingChecksum)
{
throw new IOException("crc check not found");
}
crcFound = false;
start = true;
bufPtr = 3;
if (c < 0)
{
isEndOfStream = true;
}
return -1;
}
else // data
{
bufPtr = Decode(c, ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
}
}
else
{
if (c >= 0)
{
bufPtr = Decode(c, ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
}
else
{
isEndOfStream = true;
return -1;
}
}
}
c = outBuf[bufPtr++];
crc.Update(c);
return c;
}
/**
* Reads up to <code>len</code> bytes of data from the input stream into
* an array of bytes. An attempt is made to read as many as
* <code>len</code> bytes, but a smaller number may be read.
* The number of bytes actually read is returned as an integer.
*
* The first byte read is stored into element <code>b[off]</code>, the
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
* is, at most, equal to <code>len</code>.
*
* NOTE: We need to override the custom behavior of Java's {@link InputStream#read(byte[], int, int)},
* as the upstream method silently swallows {@link IOException IOExceptions}.
* This would cause CRC checksum errors to go unnoticed.
*
* @see <a href="https://github.com/bcgit/bc-java/issues/998">Related BC bug report</a>
* @param b byte array
* @param off offset at which we start writing data to the array
* @param len number of bytes we write into the array
* @return total number of bytes read into the buffer
*
* @throws IOException if an exception happens AT ANY POINT
*/
public override int Read(byte[] b, int off, int len)
{
CheckIndexSize(b.Length, off, len);
int pos = 0;
while (pos < len)
{
int c = ReadByte();
if (c < 0)
break;
b[off + pos++] = (byte)c;
}
return pos;
}
private void CheckIndexSize(int size, int off, int len)
{
if (off < 0 || len < 0)
throw new IndexOutOfRangeException("Offset and length cannot be negative.");
if (off > size - len)
throw new IndexOutOfRangeException("Invalid offset and length.");
}
#if PORTABLE || NETFX_CORE
protected override void Dispose(bool disposing)
{
if (disposing)
{
BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.Dispose(input);
}
base.Dispose(disposing);
}
#else
public override void Close()
{
BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.Dispose(input);
base.Close();
}
#endif
/**
* Change how the stream should react if it encounters missing CRC checksum.
* The default value is false (ignore missing CRC checksums). If the behavior is set to true,
* an {@link IOException} will be thrown if a missing CRC checksum is encountered.
*
* @param detectMissing ignore missing CRC sums
*/
public virtual void SetDetectMissingCrc(bool detectMissing)
{
this.detectMissingChecksum = detectMissing;
}
}
}
#pragma warning restore
#endif