ArmoredOutputStream.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Reflection;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  9. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
  10. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
  11. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Bcpg
  12. {
  13. /**
  14. * Basic output stream.
  15. */
  16. public class ArmoredOutputStream
  17. : BaseOutputStream
  18. {
  19. public static readonly string HeaderVersion = "Version";
  20. private static readonly byte[] encodingTable =
  21. {
  22. (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
  23. (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
  24. (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
  25. (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
  26. (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
  27. (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
  28. (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
  29. (byte)'v',
  30. (byte)'w', (byte)'x', (byte)'y', (byte)'z',
  31. (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
  32. (byte)'7', (byte)'8', (byte)'9',
  33. (byte)'+', (byte)'/'
  34. };
  35. /**
  36. * encode the input data producing a base 64 encoded byte array.
  37. */
  38. private static void Encode(
  39. Stream outStream,
  40. int[] data,
  41. int len)
  42. {
  43. Debug.Assert(len > 0);
  44. Debug.Assert(len < 4);
  45. byte[] bs = new byte[4];
  46. int d1 = data[0];
  47. bs[0] = encodingTable[(d1 >> 2) & 0x3f];
  48. switch (len)
  49. {
  50. case 1:
  51. {
  52. bs[1] = encodingTable[(d1 << 4) & 0x3f];
  53. bs[2] = (byte)'=';
  54. bs[3] = (byte)'=';
  55. break;
  56. }
  57. case 2:
  58. {
  59. int d2 = data[1];
  60. bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
  61. bs[2] = encodingTable[(d2 << 2) & 0x3f];
  62. bs[3] = (byte)'=';
  63. break;
  64. }
  65. case 3:
  66. {
  67. int d2 = data[1];
  68. int d3 = data[2];
  69. bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
  70. bs[2] = encodingTable[((d2 << 2) | (d3 >> 6)) & 0x3f];
  71. bs[3] = encodingTable[d3 & 0x3f];
  72. break;
  73. }
  74. }
  75. outStream.Write(bs, 0, bs.Length);
  76. }
  77. private readonly Stream outStream;
  78. private int[] buf = new int[3];
  79. private int bufPtr = 0;
  80. private Crc24 crc = new Crc24();
  81. private int chunkCount = 0;
  82. private int lastb;
  83. private bool start = true;
  84. private bool clearText = false;
  85. private bool newLine = false;
  86. private string type;
  87. private static readonly string NewLine = Environment.NewLine;
  88. private static readonly string headerStart = "-----BEGIN PGP ";
  89. private static readonly string headerTail = "-----";
  90. private static readonly string footerStart = "-----END PGP ";
  91. private static readonly string footerTail = "-----";
  92. private static string CreateVersion()
  93. {
  94. var assembly = Assembly.GetExecutingAssembly();
  95. var title = assembly.GetCustomAttribute<AssemblyTitleAttribute>().Title;
  96. var version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
  97. return title + " v" + version;
  98. }
  99. private static readonly string Version = CreateVersion();
  100. private readonly IDictionary<string, IList<string>> m_headers;
  101. public ArmoredOutputStream(Stream outStream)
  102. {
  103. this.outStream = outStream;
  104. this.m_headers = new Dictionary<string, IList<string>>(1);
  105. SetHeader(HeaderVersion, Version);
  106. }
  107. public ArmoredOutputStream(Stream outStream, IDictionary<string, string> headers)
  108. : this(outStream)
  109. {
  110. foreach (var header in headers)
  111. {
  112. var headerList = new List<string>(1);
  113. headerList.Add(header.Value);
  114. m_headers[header.Key] = headerList;
  115. }
  116. }
  117. /**
  118. * Set an additional header entry. Any current value(s) under the same name will be
  119. * replaced by the new one. A null value will clear the entry for name. *
  120. * @param name the name of the header entry.
  121. * @param v the value of the header entry.
  122. */
  123. public void SetHeader(string name, string val)
  124. {
  125. if (val == null)
  126. {
  127. this.m_headers.Remove(name);
  128. return;
  129. }
  130. if (m_headers.TryGetValue(name, out var valueList))
  131. {
  132. valueList.Clear();
  133. }
  134. else
  135. {
  136. valueList = new List<string>(1);
  137. m_headers[name] = valueList;
  138. }
  139. valueList.Add(val);
  140. }
  141. /**
  142. * Set an additional header entry. The current value(s) will continue to exist together
  143. * with the new one. Adding a null value has no effect.
  144. *
  145. * @param name the name of the header entry.
  146. * @param value the value of the header entry.
  147. */
  148. public void AddHeader(string name, string val)
  149. {
  150. if (val == null || name == null)
  151. return;
  152. if (!m_headers.TryGetValue(name, out var valueList))
  153. {
  154. valueList = new List<string>(1);
  155. m_headers[name] = valueList;
  156. }
  157. valueList.Add(val);
  158. }
  159. /**
  160. * Reset the headers to only contain a Version string (if one is present).
  161. */
  162. public void ResetHeaders()
  163. {
  164. var versions = CollectionUtilities.GetValueOrNull(m_headers, HeaderVersion);
  165. m_headers.Clear();
  166. if (versions != null)
  167. {
  168. m_headers[HeaderVersion] = versions;
  169. }
  170. }
  171. /**
  172. * Start a clear text signed message.
  173. * @param hashAlgorithm
  174. */
  175. public void BeginClearText(
  176. HashAlgorithmTag hashAlgorithm)
  177. {
  178. string hash;
  179. switch (hashAlgorithm)
  180. {
  181. case HashAlgorithmTag.Sha1:
  182. hash = "SHA1";
  183. break;
  184. case HashAlgorithmTag.Sha256:
  185. hash = "SHA256";
  186. break;
  187. case HashAlgorithmTag.Sha384:
  188. hash = "SHA384";
  189. break;
  190. case HashAlgorithmTag.Sha512:
  191. hash = "SHA512";
  192. break;
  193. case HashAlgorithmTag.MD2:
  194. hash = "MD2";
  195. break;
  196. case HashAlgorithmTag.MD5:
  197. hash = "MD5";
  198. break;
  199. case HashAlgorithmTag.RipeMD160:
  200. hash = "RIPEMD160";
  201. break;
  202. default:
  203. throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm);
  204. }
  205. DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + NewLine);
  206. DoWrite("Hash: " + hash + NewLine + NewLine);
  207. clearText = true;
  208. newLine = true;
  209. lastb = 0;
  210. }
  211. public void EndClearText()
  212. {
  213. clearText = false;
  214. }
  215. public override void WriteByte(byte value)
  216. {
  217. if (clearText)
  218. {
  219. outStream.WriteByte(value);
  220. if (newLine)
  221. {
  222. if (!(value == '\n' && lastb == '\r'))
  223. {
  224. newLine = false;
  225. }
  226. if (value == '-')
  227. {
  228. outStream.WriteByte((byte)' ');
  229. outStream.WriteByte((byte)'-'); // dash escape
  230. }
  231. }
  232. if (value == '\r' || (value == '\n' && lastb != '\r'))
  233. {
  234. newLine = true;
  235. }
  236. lastb = value;
  237. return;
  238. }
  239. if (start)
  240. {
  241. bool newPacket = (value & 0x40) != 0;
  242. int tag;
  243. if (newPacket)
  244. {
  245. tag = value & 0x3f;
  246. }
  247. else
  248. {
  249. tag = (value & 0x3f) >> 2;
  250. }
  251. switch ((PacketTag)tag)
  252. {
  253. case PacketTag.PublicKey:
  254. type = "PUBLIC KEY BLOCK";
  255. break;
  256. case PacketTag.SecretKey:
  257. type = "PRIVATE KEY BLOCK";
  258. break;
  259. case PacketTag.Signature:
  260. type = "SIGNATURE";
  261. break;
  262. default:
  263. type = "MESSAGE";
  264. break;
  265. }
  266. DoWrite(headerStart + type + headerTail + NewLine);
  267. if (m_headers.TryGetValue(HeaderVersion, out var versionHeaders))
  268. {
  269. WriteHeaderEntry(HeaderVersion, versionHeaders[0]);
  270. }
  271. foreach (var de in m_headers)
  272. {
  273. string k = de.Key;
  274. if (k != HeaderVersion)
  275. {
  276. foreach (string v in de.Value)
  277. {
  278. WriteHeaderEntry(k, v);
  279. }
  280. }
  281. }
  282. DoWrite(NewLine);
  283. start = false;
  284. }
  285. if (bufPtr == 3)
  286. {
  287. Encode(outStream, buf, bufPtr);
  288. bufPtr = 0;
  289. if ((++chunkCount & 0xf) == 0)
  290. {
  291. DoWrite(NewLine);
  292. }
  293. }
  294. crc.Update(value);
  295. buf[bufPtr++] = value & 0xff;
  296. }
  297. /**
  298. * <b>Note</b>: Close() does not close the underlying stream. So it is possible to write
  299. * multiple objects using armoring to a single stream.
  300. */
  301. protected override void Dispose(bool disposing)
  302. {
  303. if (disposing)
  304. {
  305. if (type != null)
  306. {
  307. DoClose();
  308. type = null;
  309. start = true;
  310. }
  311. }
  312. base.Dispose(disposing);
  313. }
  314. private void DoClose()
  315. {
  316. if (bufPtr > 0)
  317. {
  318. Encode(outStream, buf, bufPtr);
  319. }
  320. DoWrite(NewLine + '=');
  321. int crcV = crc.Value;
  322. buf[0] = ((crcV >> 16) & 0xff);
  323. buf[1] = ((crcV >> 8) & 0xff);
  324. buf[2] = (crcV & 0xff);
  325. Encode(outStream, buf, 3);
  326. DoWrite(NewLine);
  327. DoWrite(footerStart);
  328. DoWrite(type);
  329. DoWrite(footerTail);
  330. DoWrite(NewLine);
  331. outStream.Flush();
  332. }
  333. private void WriteHeaderEntry(
  334. string name,
  335. string v)
  336. {
  337. DoWrite(name + ": " + v + NewLine);
  338. }
  339. private void DoWrite(
  340. string s)
  341. {
  342. byte[] bs = Strings.ToAsciiByteArray(s);
  343. outStream.Write(bs, 0, bs.Length);
  344. }
  345. }
  346. }
  347. #pragma warning restore
  348. #endif