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.

289 lines
8.9 KiB

4 years ago
  1. # coding: utf-8
  2. """
  3. Functions for parsing and dumping using the ASN.1 DER encoding. Exports the
  4. following items:
  5. - emit()
  6. - parse()
  7. - peek()
  8. Other type classes are defined that help compose the types listed above.
  9. """
  10. from __future__ import unicode_literals, division, absolute_import, print_function
  11. import sys
  12. from ._types import byte_cls, chr_cls, type_name
  13. from .util import int_from_bytes, int_to_bytes
  14. _PY2 = sys.version_info <= (3,)
  15. _INSUFFICIENT_DATA_MESSAGE = 'Insufficient data - %s bytes requested but only %s available'
  16. def emit(class_, method, tag, contents):
  17. """
  18. Constructs a byte string of an ASN.1 DER-encoded value
  19. This is typically not useful. Instead, use one of the standard classes from
  20. asn1crypto.core, or construct a new class with specific fields, and call the
  21. .dump() method.
  22. :param class_:
  23. An integer ASN.1 class value: 0 (universal), 1 (application),
  24. 2 (context), 3 (private)
  25. :param method:
  26. An integer ASN.1 method value: 0 (primitive), 1 (constructed)
  27. :param tag:
  28. An integer ASN.1 tag value
  29. :param contents:
  30. A byte string of the encoded byte contents
  31. :return:
  32. A byte string of the ASN.1 DER value (header and contents)
  33. """
  34. if not isinstance(class_, int):
  35. raise TypeError('class_ must be an integer, not %s' % type_name(class_))
  36. if class_ < 0 or class_ > 3:
  37. raise ValueError('class_ must be one of 0, 1, 2 or 3, not %s' % class_)
  38. if not isinstance(method, int):
  39. raise TypeError('method must be an integer, not %s' % type_name(method))
  40. if method < 0 or method > 1:
  41. raise ValueError('method must be 0 or 1, not %s' % method)
  42. if not isinstance(tag, int):
  43. raise TypeError('tag must be an integer, not %s' % type_name(tag))
  44. if tag < 0:
  45. raise ValueError('tag must be greater than zero, not %s' % tag)
  46. if not isinstance(contents, byte_cls):
  47. raise TypeError('contents must be a byte string, not %s' % type_name(contents))
  48. return _dump_header(class_, method, tag, contents) + contents
  49. def parse(contents, strict=False):
  50. """
  51. Parses a byte string of ASN.1 BER/DER-encoded data.
  52. This is typically not useful. Instead, use one of the standard classes from
  53. asn1crypto.core, or construct a new class with specific fields, and call the
  54. .load() class method.
  55. :param contents:
  56. A byte string of BER/DER-encoded data
  57. :param strict:
  58. A boolean indicating if trailing data should be forbidden - if so, a
  59. ValueError will be raised when trailing data exists
  60. :raises:
  61. ValueError - when the contents do not contain an ASN.1 header or are truncated in some way
  62. TypeError - when contents is not a byte string
  63. :return:
  64. A 6-element tuple:
  65. - 0: integer class (0 to 3)
  66. - 1: integer method
  67. - 2: integer tag
  68. - 3: byte string header
  69. - 4: byte string content
  70. - 5: byte string trailer
  71. """
  72. if not isinstance(contents, byte_cls):
  73. raise TypeError('contents must be a byte string, not %s' % type_name(contents))
  74. contents_len = len(contents)
  75. info, consumed = _parse(contents, contents_len)
  76. if strict and consumed != contents_len:
  77. raise ValueError('Extra data - %d bytes of trailing data were provided' % (contents_len - consumed))
  78. return info
  79. def peek(contents):
  80. """
  81. Parses a byte string of ASN.1 BER/DER-encoded data to find the length
  82. This is typically used to look into an encoded value to see how long the
  83. next chunk of ASN.1-encoded data is. Primarily it is useful when a
  84. value is a concatenation of multiple values.
  85. :param contents:
  86. A byte string of BER/DER-encoded data
  87. :raises:
  88. ValueError - when the contents do not contain an ASN.1 header or are truncated in some way
  89. TypeError - when contents is not a byte string
  90. :return:
  91. An integer with the number of bytes occupied by the ASN.1 value
  92. """
  93. if not isinstance(contents, byte_cls):
  94. raise TypeError('contents must be a byte string, not %s' % type_name(contents))
  95. info, consumed = _parse(contents, len(contents))
  96. return consumed
  97. def _parse(encoded_data, data_len, pointer=0, lengths_only=False):
  98. """
  99. Parses a byte string into component parts
  100. :param encoded_data:
  101. A byte string that contains BER-encoded data
  102. :param data_len:
  103. The integer length of the encoded data
  104. :param pointer:
  105. The index in the byte string to parse from
  106. :param lengths_only:
  107. A boolean to cause the call to return a 2-element tuple of the integer
  108. number of bytes in the header and the integer number of bytes in the
  109. contents. Internal use only.
  110. :return:
  111. A 2-element tuple:
  112. - 0: A tuple of (class_, method, tag, header, content, trailer)
  113. - 1: An integer indicating how many bytes were consumed
  114. """
  115. if data_len < pointer + 2:
  116. raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (2, data_len - pointer))
  117. start = pointer
  118. first_octet = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer]
  119. pointer += 1
  120. tag = first_octet & 31
  121. # Base 128 length using 8th bit as continuation indicator
  122. if tag == 31:
  123. tag = 0
  124. while True:
  125. num = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer]
  126. pointer += 1
  127. tag *= 128
  128. tag += num & 127
  129. if num >> 7 == 0:
  130. break
  131. length_octet = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer]
  132. pointer += 1
  133. if length_octet >> 7 == 0:
  134. if lengths_only:
  135. return (pointer, pointer + (length_octet & 127))
  136. contents_end = pointer + (length_octet & 127)
  137. else:
  138. length_octets = length_octet & 127
  139. if length_octets:
  140. pointer += length_octets
  141. contents_end = pointer + int_from_bytes(encoded_data[pointer - length_octets:pointer], signed=False)
  142. if lengths_only:
  143. return (pointer, contents_end)
  144. else:
  145. # To properly parse indefinite length values, we need to scan forward
  146. # parsing headers until we find a value with a length of zero. If we
  147. # just scanned looking for \x00\x00, nested indefinite length values
  148. # would not work.
  149. contents_end = pointer
  150. # Unfortunately we need to understand the contents of the data to
  151. # properly scan forward, which bleeds some representation info into
  152. # the parser. This condition handles the unused bits byte in
  153. # constructed bit strings.
  154. if tag == 3:
  155. contents_end += 1
  156. while contents_end < data_len:
  157. sub_header_end, contents_end = _parse(encoded_data, data_len, contents_end, lengths_only=True)
  158. if contents_end == sub_header_end and encoded_data[contents_end - 2:contents_end] == b'\x00\x00':
  159. break
  160. if lengths_only:
  161. return (pointer, contents_end)
  162. if contents_end > data_len:
  163. raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (contents_end, data_len))
  164. return (
  165. (
  166. first_octet >> 6,
  167. (first_octet >> 5) & 1,
  168. tag,
  169. encoded_data[start:pointer],
  170. encoded_data[pointer:contents_end - 2],
  171. b'\x00\x00'
  172. ),
  173. contents_end
  174. )
  175. if contents_end > data_len:
  176. raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (contents_end, data_len))
  177. return (
  178. (
  179. first_octet >> 6,
  180. (first_octet >> 5) & 1,
  181. tag,
  182. encoded_data[start:pointer],
  183. encoded_data[pointer:contents_end],
  184. b''
  185. ),
  186. contents_end
  187. )
  188. def _dump_header(class_, method, tag, contents):
  189. """
  190. Constructs the header bytes for an ASN.1 object
  191. :param class_:
  192. An integer ASN.1 class value: 0 (universal), 1 (application),
  193. 2 (context), 3 (private)
  194. :param method:
  195. An integer ASN.1 method value: 0 (primitive), 1 (constructed)
  196. :param tag:
  197. An integer ASN.1 tag value
  198. :param contents:
  199. A byte string of the encoded byte contents
  200. :return:
  201. A byte string of the ASN.1 DER header
  202. """
  203. header = b''
  204. id_num = 0
  205. id_num |= class_ << 6
  206. id_num |= method << 5
  207. if tag >= 31:
  208. header += chr_cls(id_num | 31)
  209. while tag > 0:
  210. continuation_bit = 0x80 if tag > 0x7F else 0
  211. header += chr_cls(continuation_bit | (tag & 0x7F))
  212. tag = tag >> 7
  213. else:
  214. header += chr_cls(id_num | tag)
  215. length = len(contents)
  216. if length <= 127:
  217. header += chr_cls(length)
  218. else:
  219. length_bytes = int_to_bytes(length)
  220. header += chr_cls(0x80 | len(length_bytes))
  221. header += length_bytes
  222. return header