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.
420 lines
12 KiB
420 lines
12 KiB
8 months ago
|
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||
|
#pragma warning disable
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Diagnostics;
|
||
|
using System.IO;
|
||
|
using System.Reflection;
|
||
|
using System.Text;
|
||
|
|
||
|
#if PORTABLE || NETFX_CORE
|
||
|
using System.Linq;
|
||
|
#endif
|
||
|
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
||
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
|
||
|
|
||
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Bcpg
|
||
|
{
|
||
|
/**
|
||
|
* Basic output stream.
|
||
|
*/
|
||
|
public class ArmoredOutputStream
|
||
|
: BaseOutputStream
|
||
|
{
|
||
|
public static readonly string HeaderVersion = "Version";
|
||
|
|
||
|
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 = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.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" + HTTPManager.UserAgent;
|
||
|
|
||
|
private readonly IDictionary headers;
|
||
|
|
||
|
public ArmoredOutputStream(Stream outStream)
|
||
|
{
|
||
|
this.outStream = outStream;
|
||
|
this.headers = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateHashtable(1);
|
||
|
SetHeader(HeaderVersion, Version);
|
||
|
}
|
||
|
|
||
|
public ArmoredOutputStream(Stream outStream, IDictionary headers)
|
||
|
: this(outStream)
|
||
|
{
|
||
|
foreach (string header in headers.Keys)
|
||
|
{
|
||
|
IList headerList = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(1);
|
||
|
headerList.Add(headers[header]);
|
||
|
|
||
|
this.headers[header] = headerList;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set an additional header entry. Any current value(s) under the same name will be
|
||
|
* replaced by the new one. A null value will clear the entry for name. *
|
||
|
* @param name the name of the header entry.
|
||
|
* @param v the value of the header entry.
|
||
|
*/
|
||
|
public void SetHeader(string name, string val)
|
||
|
{
|
||
|
if (val == null)
|
||
|
{
|
||
|
this.headers.Remove(name);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IList valueList = (IList)headers[name];
|
||
|
if (valueList == null)
|
||
|
{
|
||
|
valueList = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(1);
|
||
|
this.headers[name] = valueList;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
valueList.Clear();
|
||
|
}
|
||
|
valueList.Add(val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set an additional header entry. The current value(s) will continue to exist together
|
||
|
* with the new one. Adding a null value has no effect.
|
||
|
*
|
||
|
* @param name the name of the header entry.
|
||
|
* @param value the value of the header entry.
|
||
|
*/
|
||
|
public void AddHeader(string name, string val)
|
||
|
{
|
||
|
if (val == null || name == null)
|
||
|
return;
|
||
|
|
||
|
IList valueList = (IList)headers[name];
|
||
|
if (valueList == null)
|
||
|
{
|
||
|
valueList = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(1);
|
||
|
this.headers[name] = valueList;
|
||
|
}
|
||
|
valueList.Add(val);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reset the headers to only contain a Version string (if one is present).
|
||
|
*/
|
||
|
public void ResetHeaders()
|
||
|
{
|
||
|
IList versions = (IList)headers[HeaderVersion];
|
||
|
|
||
|
headers.Clear();
|
||
|
|
||
|
if (versions != null)
|
||
|
{
|
||
|
headers[HeaderVersion] = versions;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
|
||
|
{
|
||
|
IList versionHeaders = (IList)headers[HeaderVersion];
|
||
|
if (versionHeaders != null)
|
||
|
{
|
||
|
WriteHeaderEntry(HeaderVersion, versionHeaders[0].ToString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (DictionaryEntry de in headers)
|
||
|
{
|
||
|
string k = (string)de.Key;
|
||
|
if (k != HeaderVersion)
|
||
|
{
|
||
|
IList values = (IList)de.Value;
|
||
|
foreach (string v in values)
|
||
|
{
|
||
|
WriteHeaderEntry(k, v);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DoWrite(nl);
|
||
|
|
||
|
start = false;
|
||
|
}
|
||
|
|
||
|
if (bufPtr == 3)
|
||
|
{
|
||
|
Encode(outStream, buf, bufPtr);
|
||
|
bufPtr = 0;
|
||
|
if ((++chunkCount & 0xf) == 0)
|
||
|
{
|
||
|
DoWrite(nl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
crc.Update(b);
|
||
|
buf[bufPtr++] = b & 0xff;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* <b>Note</b>: Close() does not close the underlying stream. So it is possible to write
|
||
|
* multiple objects using armoring to a single stream.
|
||
|
*/
|
||
|
#if PORTABLE || NETFX_CORE
|
||
|
protected override void Dispose(bool disposing)
|
||
|
{
|
||
|
if (disposing)
|
||
|
{
|
||
|
if (type == null)
|
||
|
return;
|
||
|
|
||
|
DoClose();
|
||
|
|
||
|
type = null;
|
||
|
start = true;
|
||
|
}
|
||
|
base.Dispose(disposing);
|
||
|
}
|
||
|
#else
|
||
|
public override void Close()
|
||
|
{
|
||
|
if (type == null)
|
||
|
return;
|
||
|
|
||
|
DoClose();
|
||
|
|
||
|
type = null;
|
||
|
start = true;
|
||
|
|
||
|
base.Close();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
private void DoClose()
|
||
|
{
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#pragma warning restore
|
||
|
#endif
|