Asn1InputStream.cs 12 KB

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