268 lines
5.5 KiB
C#
268 lines
5.5 KiB
C#
using System.Collections;
|
|
using System.IO;
|
|
using Org.BouncyCastle.Utilities;
|
|
using Org.BouncyCastle.Utilities.IO;
|
|
|
|
namespace Org.BouncyCastle.Bcpg;
|
|
|
|
public class ArmoredOutputStream : BaseOutputStream
|
|
{
|
|
public static readonly string HeaderVersion = "Version";
|
|
|
|
private static readonly byte[] encodingTable = new byte[64]
|
|
{
|
|
65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
|
|
75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
|
|
85, 86, 87, 88, 89, 90, 97, 98, 99, 100,
|
|
101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
|
|
111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
|
|
121, 122, 48, 49, 50, 51, 52, 53, 54, 55,
|
|
56, 57, 43, 47
|
|
};
|
|
|
|
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;
|
|
|
|
private static void Encode(Stream outStream, int[] data, int len)
|
|
{
|
|
byte[] array = new byte[4];
|
|
int num = data[0];
|
|
array[0] = encodingTable[(num >> 2) & 0x3F];
|
|
switch (len)
|
|
{
|
|
case 1:
|
|
array[1] = encodingTable[(num << 4) & 0x3F];
|
|
array[2] = 61;
|
|
array[3] = 61;
|
|
break;
|
|
case 2:
|
|
{
|
|
int num4 = data[1];
|
|
array[1] = encodingTable[((num << 4) | (num4 >> 4)) & 0x3F];
|
|
array[2] = encodingTable[(num4 << 2) & 0x3F];
|
|
array[3] = 61;
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
int num2 = data[1];
|
|
int num3 = data[2];
|
|
array[1] = encodingTable[((num << 4) | (num2 >> 4)) & 0x3F];
|
|
array[2] = encodingTable[((num2 << 2) | (num3 >> 6)) & 0x3F];
|
|
array[3] = encodingTable[num3 & 0x3F];
|
|
break;
|
|
}
|
|
}
|
|
outStream.Write(array, 0, array.Length);
|
|
}
|
|
|
|
public ArmoredOutputStream(Stream outStream)
|
|
{
|
|
this.outStream = outStream;
|
|
headers = Platform.CreateHashtable(1);
|
|
headers.Add(HeaderVersion, Version);
|
|
}
|
|
|
|
public ArmoredOutputStream(Stream outStream, IDictionary headers)
|
|
{
|
|
this.outStream = outStream;
|
|
this.headers = Platform.CreateHashtable(headers);
|
|
if (!this.headers.Contains(HeaderVersion))
|
|
{
|
|
this.headers.Add(HeaderVersion, Version);
|
|
}
|
|
}
|
|
|
|
public void SetHeader(string name, string v)
|
|
{
|
|
if (v == null)
|
|
{
|
|
headers.Remove(name);
|
|
}
|
|
else
|
|
{
|
|
headers[name] = v;
|
|
}
|
|
}
|
|
|
|
public void ResetHeaders()
|
|
{
|
|
string text = (string)headers[HeaderVersion];
|
|
headers.Clear();
|
|
if (text != null)
|
|
{
|
|
headers.Add(HeaderVersion, text);
|
|
}
|
|
}
|
|
|
|
public void BeginClearText(HashAlgorithmTag hashAlgorithm)
|
|
{
|
|
string text = hashAlgorithm switch
|
|
{
|
|
HashAlgorithmTag.Sha1 => "SHA1",
|
|
HashAlgorithmTag.Sha256 => "SHA256",
|
|
HashAlgorithmTag.Sha384 => "SHA384",
|
|
HashAlgorithmTag.Sha512 => "SHA512",
|
|
HashAlgorithmTag.MD2 => "MD2",
|
|
HashAlgorithmTag.MD5 => "MD5",
|
|
HashAlgorithmTag.RipeMD160 => "RIPEMD160",
|
|
_ => throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm),
|
|
};
|
|
DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + nl);
|
|
DoWrite("Hash: " + text + 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 != 10 || lastb != 13)
|
|
{
|
|
newLine = false;
|
|
}
|
|
if (b == 45)
|
|
{
|
|
outStream.WriteByte(32);
|
|
outStream.WriteByte(45);
|
|
}
|
|
}
|
|
if (b == 13 || (b == 10 && lastb != 13))
|
|
{
|
|
newLine = true;
|
|
}
|
|
lastb = b;
|
|
return;
|
|
}
|
|
if (start)
|
|
{
|
|
switch ((PacketTag)(((b & 0x40) == 0) ? ((b & 0x3F) >> 2) : (b & 0x3F)))
|
|
{
|
|
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);
|
|
if (headers.Contains(HeaderVersion))
|
|
{
|
|
WriteHeaderEntry(HeaderVersion, (string)headers[HeaderVersion]);
|
|
}
|
|
foreach (object header in headers)
|
|
{
|
|
DictionaryEntry dictionaryEntry = (DictionaryEntry)header;
|
|
string text = (string)dictionaryEntry.Key;
|
|
if (text != HeaderVersion)
|
|
{
|
|
string v = (string)dictionaryEntry.Value;
|
|
WriteHeaderEntry(text, 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;
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
if (type != null)
|
|
{
|
|
DoClose();
|
|
type = null;
|
|
start = true;
|
|
base.Close();
|
|
}
|
|
}
|
|
|
|
private void DoClose()
|
|
{
|
|
if (bufPtr > 0)
|
|
{
|
|
Encode(outStream, buf, bufPtr);
|
|
}
|
|
DoWrite(nl + '=');
|
|
int value = crc.Value;
|
|
buf[0] = (value >> 16) & 0xFF;
|
|
buf[1] = (value >> 8) & 0xFF;
|
|
buf[2] = value & 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[] array = Strings.ToAsciiByteArray(s);
|
|
outStream.Write(array, 0, array.Length);
|
|
}
|
|
}
|