Asn1InputStream.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
  7. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1
  8. {
  9. /**
  10. * a general purpose ASN.1 decoder - note: this class differs from the
  11. * others in that it returns null after it has read the last object in
  12. * the stream. If an ASN.1 Null is encountered a Der/BER Null object is
  13. * returned.
  14. */
  15. public class Asn1InputStream
  16. : FilterStream
  17. {
  18. private readonly int limit;
  19. private readonly byte[][] tmpBuffers;
  20. internal static int FindLimit(Stream input)
  21. {
  22. if (input is LimitedInputStream)
  23. {
  24. return ((LimitedInputStream)input).GetRemaining();
  25. }
  26. else if (input is MemoryStream)
  27. {
  28. MemoryStream mem = (MemoryStream)input;
  29. return (int)(mem.Length - mem.Position);
  30. }
  31. return int.MaxValue;
  32. }
  33. public Asn1InputStream(
  34. Stream inputStream)
  35. : this(inputStream, FindLimit(inputStream))
  36. {
  37. }
  38. /**
  39. * Create an ASN1InputStream where no DER object will be longer than limit.
  40. *
  41. * @param input stream containing ASN.1 encoded data.
  42. * @param limit maximum size of a DER encoded object.
  43. */
  44. public Asn1InputStream(
  45. Stream inputStream,
  46. int limit)
  47. : base(inputStream)
  48. {
  49. this.limit = limit;
  50. this.tmpBuffers = new byte[16][];
  51. }
  52. /**
  53. * Create an ASN1InputStream based on the input byte array. The length of DER objects in
  54. * the stream is automatically limited to the length of the input array.
  55. *
  56. * @param input array containing ASN.1 encoded data.
  57. */
  58. public Asn1InputStream(
  59. byte[] input)
  60. : this(new MemoryStream(input, false), input.Length)
  61. {
  62. }
  63. /**
  64. * build an object given its tag and the number of bytes to construct it from.
  65. */
  66. private Asn1Object BuildObject(
  67. int tag,
  68. int tagNo,
  69. int length)
  70. {
  71. bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
  72. DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this.s, length);
  73. if ((tag & Asn1Tags.Application) != 0)
  74. {
  75. return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray());
  76. }
  77. if ((tag & Asn1Tags.Tagged) != 0)
  78. {
  79. return new Asn1StreamParser(defIn).ReadTaggedObject(isConstructed, tagNo);
  80. }
  81. if (isConstructed)
  82. {
  83. // TODO There are other tags that may be constructed (e.g. BitString)
  84. switch (tagNo)
  85. {
  86. case Asn1Tags.OctetString:
  87. //
  88. // yes, people actually do this...
  89. //
  90. return new BerOctetString(BuildDerEncodableVector(defIn));
  91. case Asn1Tags.Sequence:
  92. return CreateDerSequence(defIn);
  93. case Asn1Tags.Set:
  94. return CreateDerSet(defIn);
  95. case Asn1Tags.External:
  96. return new DerExternal(BuildDerEncodableVector(defIn));
  97. default:
  98. throw new IOException("unknown tag " + tagNo + " encountered");
  99. }
  100. }
  101. return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers);
  102. }
  103. internal Asn1EncodableVector BuildEncodableVector()
  104. {
  105. Asn1EncodableVector v = new Asn1EncodableVector();
  106. Asn1Object o;
  107. while ((o = ReadObject()) != null)
  108. {
  109. v.Add(o);
  110. }
  111. return v;
  112. }
  113. internal virtual Asn1EncodableVector BuildDerEncodableVector(
  114. DefiniteLengthInputStream dIn)
  115. {
  116. return new Asn1InputStream(dIn).BuildEncodableVector();
  117. }
  118. internal virtual DerSequence CreateDerSequence(
  119. DefiniteLengthInputStream dIn)
  120. {
  121. return DerSequence.FromVector(BuildDerEncodableVector(dIn));
  122. }
  123. internal virtual DerSet CreateDerSet(
  124. DefiniteLengthInputStream dIn)
  125. {
  126. return DerSet.FromVector(BuildDerEncodableVector(dIn), false);
  127. }
  128. public Asn1Object ReadObject()
  129. {
  130. int tag = ReadByte();
  131. if (tag <= 0)
  132. {
  133. if (tag == 0)
  134. throw new IOException("unexpected end-of-contents marker");
  135. return null;
  136. }
  137. //
  138. // calculate tag number
  139. //
  140. int tagNo = ReadTagNumber(this.s, tag);
  141. bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
  142. //
  143. // calculate length
  144. //
  145. int length = ReadLength(this.s, limit);
  146. if (length < 0) // indefinite length method
  147. {
  148. if (!isConstructed)
  149. throw new IOException("indefinite length primitive encoding encountered");
  150. IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this.s, limit);
  151. Asn1StreamParser sp = new Asn1StreamParser(indIn, limit);
  152. if ((tag & Asn1Tags.Application) != 0)
  153. {
  154. return new BerApplicationSpecificParser(tagNo, sp).ToAsn1Object();
  155. }
  156. if ((tag & Asn1Tags.Tagged) != 0)
  157. {
  158. return new BerTaggedObjectParser(true, tagNo, sp).ToAsn1Object();
  159. }
  160. // TODO There are other tags that may be constructed (e.g. BitString)
  161. switch (tagNo)
  162. {
  163. case Asn1Tags.OctetString:
  164. return new BerOctetStringParser(sp).ToAsn1Object();
  165. case Asn1Tags.Sequence:
  166. return new BerSequenceParser(sp).ToAsn1Object();
  167. case Asn1Tags.Set:
  168. return new BerSetParser(sp).ToAsn1Object();
  169. case Asn1Tags.External:
  170. return new DerExternalParser(sp).ToAsn1Object();
  171. default:
  172. throw new IOException("unknown BER object encountered");
  173. }
  174. }
  175. else
  176. {
  177. try
  178. {
  179. return BuildObject(tag, tagNo, length);
  180. }
  181. catch (ArgumentException e)
  182. {
  183. throw new Asn1Exception("corrupted stream detected", e);
  184. }
  185. }
  186. }
  187. internal static int ReadTagNumber(
  188. Stream s,
  189. int tag)
  190. {
  191. int tagNo = tag & 0x1f;
  192. //
  193. // with tagged object tag number is bottom 5 bits, or stored at the start of the content
  194. //
  195. if (tagNo == 0x1f)
  196. {
  197. tagNo = 0;
  198. int b = s.ReadByte();
  199. // X.690-0207 8.1.2.4.2
  200. // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
  201. if ((b & 0x7f) == 0) // Note: -1 will pass
  202. {
  203. throw new IOException("Corrupted stream - invalid high tag number found");
  204. }
  205. while ((b >= 0) && ((b & 0x80) != 0))
  206. {
  207. tagNo |= (b & 0x7f);
  208. tagNo <<= 7;
  209. b = s.ReadByte();
  210. }
  211. if (b < 0)
  212. throw new EndOfStreamException("EOF found inside tag value.");
  213. tagNo |= (b & 0x7f);
  214. }
  215. return tagNo;
  216. }
  217. internal static int ReadLength(
  218. Stream s,
  219. int limit)
  220. {
  221. int length = s.ReadByte();
  222. if (length < 0)
  223. throw new EndOfStreamException("EOF found when length expected");
  224. if (length == 0x80)
  225. return -1; // indefinite-length encoding
  226. if (length > 127)
  227. {
  228. int size = length & 0x7f;
  229. // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
  230. if (size > 4)
  231. throw new IOException("DER length more than 4 bytes: " + size);
  232. length = 0;
  233. for (int i = 0; i < size; i++)
  234. {
  235. int next = s.ReadByte();
  236. if (next < 0)
  237. throw new EndOfStreamException("EOF found reading length");
  238. length = (length << 8) + next;
  239. }
  240. if (length < 0)
  241. throw new IOException("Corrupted stream - negative length found");
  242. if (length >= limit) // after all we must have read at least 1 byte
  243. throw new IOException("Corrupted stream - out of bounds length found");
  244. }
  245. return length;
  246. }
  247. internal static byte[] GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
  248. {
  249. int len = defIn.GetRemaining();
  250. if (len >= tmpBuffers.Length)
  251. {
  252. return defIn.ToArray();
  253. }
  254. byte[] buf = tmpBuffers[len];
  255. if (buf == null)
  256. {
  257. buf = tmpBuffers[len] = new byte[len];
  258. }
  259. defIn.ReadAllIntoByteArray(buf);
  260. return buf;
  261. }
  262. internal static Asn1Object CreatePrimitiveDerObject(
  263. int tagNo,
  264. DefiniteLengthInputStream defIn,
  265. byte[][] tmpBuffers)
  266. {
  267. switch (tagNo)
  268. {
  269. case Asn1Tags.Boolean:
  270. return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
  271. case Asn1Tags.Enumerated:
  272. return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers));
  273. case Asn1Tags.ObjectIdentifier:
  274. return DerObjectIdentifier.FromOctetString(GetBuffer(defIn, tmpBuffers));
  275. }
  276. byte[] bytes = defIn.ToArray();
  277. switch (tagNo)
  278. {
  279. case Asn1Tags.BitString:
  280. return DerBitString.FromAsn1Octets(bytes);
  281. case Asn1Tags.BmpString:
  282. return new DerBmpString(bytes);
  283. case Asn1Tags.GeneralizedTime:
  284. return new DerGeneralizedTime(bytes);
  285. case Asn1Tags.GeneralString:
  286. return new DerGeneralString(bytes);
  287. case Asn1Tags.GraphicString:
  288. return new DerGraphicString(bytes);
  289. case Asn1Tags.IA5String:
  290. return new DerIA5String(bytes);
  291. case Asn1Tags.Integer:
  292. return new DerInteger(bytes);
  293. case Asn1Tags.Null:
  294. return DerNull.Instance; // actual content is ignored (enforce 0 length?)
  295. case Asn1Tags.NumericString:
  296. return new DerNumericString(bytes);
  297. case Asn1Tags.OctetString:
  298. return new DerOctetString(bytes);
  299. case Asn1Tags.PrintableString:
  300. return new DerPrintableString(bytes);
  301. case Asn1Tags.T61String:
  302. return new DerT61String(bytes);
  303. case Asn1Tags.UniversalString:
  304. return new DerUniversalString(bytes);
  305. case Asn1Tags.UtcTime:
  306. return new DerUtcTime(bytes);
  307. case Asn1Tags.Utf8String:
  308. return new DerUtf8String(bytes);
  309. case Asn1Tags.VideotexString:
  310. return new DerVideotexString(bytes);
  311. case Asn1Tags.VisibleString:
  312. return new DerVisibleString(bytes);
  313. default:
  314. throw new IOException("unknown tag " + tagNo + " encountered");
  315. }
  316. }
  317. }
  318. }
  319. #pragma warning restore
  320. #endif