Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

encoder.py 9.4KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #
  2. # This file is part of pyasn1 software.
  3. #
  4. # Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
  5. # License: https://pyasn1.readthedocs.io/en/latest/license.html
  6. #
  7. from pyasn1 import error
  8. from pyasn1.codec.ber import encoder
  9. from pyasn1.compat.octets import str2octs, null
  10. from pyasn1.type import univ
  11. from pyasn1.type import useful
  12. __all__ = ['Encoder', 'encode']
  13. class BooleanEncoder(encoder.IntegerEncoder):
  14. def encodeValue(self, value, asn1Spec, encodeFun, **options):
  15. if value == 0:
  16. substrate = (0,)
  17. else:
  18. substrate = (255,)
  19. return substrate, False, False
  20. class RealEncoder(encoder.RealEncoder):
  21. def _chooseEncBase(self, value):
  22. m, b, e = value
  23. return self._dropFloatingPoint(m, b, e)
  24. # specialized GeneralStringEncoder here
  25. class TimeEncoderMixIn(object):
  26. Z_CHAR = ord('Z')
  27. PLUS_CHAR = ord('+')
  28. MINUS_CHAR = ord('-')
  29. COMMA_CHAR = ord(',')
  30. DOT_CHAR = ord('.')
  31. ZERO_CHAR = ord('0')
  32. MIN_LENGTH = 12
  33. MAX_LENGTH = 19
  34. def encodeValue(self, value, asn1Spec, encodeFun, **options):
  35. # CER encoding constraints:
  36. # - minutes are mandatory, seconds are optional
  37. # - sub-seconds must NOT be zero / no meaningless zeros
  38. # - no hanging fraction dot
  39. # - time in UTC (Z)
  40. # - only dot is allowed for fractions
  41. if asn1Spec is not None:
  42. value = asn1Spec.clone(value)
  43. numbers = value.asNumbers()
  44. if self.PLUS_CHAR in numbers or self.MINUS_CHAR in numbers:
  45. raise error.PyAsn1Error('Must be UTC time: %r' % value)
  46. if numbers[-1] != self.Z_CHAR:
  47. raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % value)
  48. if self.COMMA_CHAR in numbers:
  49. raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
  50. if self.DOT_CHAR in numbers:
  51. isModified = False
  52. numbers = list(numbers)
  53. searchIndex = min(numbers.index(self.DOT_CHAR) + 4, len(numbers) - 1)
  54. while numbers[searchIndex] != self.DOT_CHAR:
  55. if numbers[searchIndex] == self.ZERO_CHAR:
  56. del numbers[searchIndex]
  57. isModified = True
  58. searchIndex -= 1
  59. searchIndex += 1
  60. if searchIndex < len(numbers):
  61. if numbers[searchIndex] == self.Z_CHAR:
  62. # drop hanging comma
  63. del numbers[searchIndex - 1]
  64. isModified = True
  65. if isModified:
  66. value = value.clone(numbers)
  67. if not self.MIN_LENGTH < len(numbers) < self.MAX_LENGTH:
  68. raise error.PyAsn1Error('Length constraint violated: %r' % value)
  69. options.update(maxChunkSize=1000)
  70. return encoder.OctetStringEncoder.encodeValue(
  71. self, value, asn1Spec, encodeFun, **options
  72. )
  73. class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
  74. MIN_LENGTH = 12
  75. MAX_LENGTH = 20
  76. class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
  77. MIN_LENGTH = 10
  78. MAX_LENGTH = 14
  79. class SetOfEncoder(encoder.SequenceOfEncoder):
  80. def encodeValue(self, value, asn1Spec, encodeFun, **options):
  81. chunks = self._encodeComponents(
  82. value, asn1Spec, encodeFun, **options)
  83. # sort by serialised and padded components
  84. if len(chunks) > 1:
  85. zero = str2octs('\x00')
  86. maxLen = max(map(len, chunks))
  87. paddedChunks = [
  88. (x.ljust(maxLen, zero), x) for x in chunks
  89. ]
  90. paddedChunks.sort(key=lambda x: x[0])
  91. chunks = [x[1] for x in paddedChunks]
  92. return null.join(chunks), True, True
  93. class SequenceOfEncoder(encoder.SequenceOfEncoder):
  94. def encodeValue(self, value, asn1Spec, encodeFun, **options):
  95. if options.get('ifNotEmpty', False) and not len(value):
  96. return null, True, True
  97. chunks = self._encodeComponents(
  98. value, asn1Spec, encodeFun, **options)
  99. return null.join(chunks), True, True
  100. class SetEncoder(encoder.SequenceEncoder):
  101. @staticmethod
  102. def _componentSortKey(componentAndType):
  103. """Sort SET components by tag
  104. Sort regardless of the Choice value (static sort)
  105. """
  106. component, asn1Spec = componentAndType
  107. if asn1Spec is None:
  108. asn1Spec = component
  109. if asn1Spec.typeId == univ.Choice.typeId and not asn1Spec.tagSet:
  110. if asn1Spec.tagSet:
  111. return asn1Spec.tagSet
  112. else:
  113. return asn1Spec.componentType.minTagSet
  114. else:
  115. return asn1Spec.tagSet
  116. def encodeValue(self, value, asn1Spec, encodeFun, **options):
  117. substrate = null
  118. comps = []
  119. compsMap = {}
  120. if asn1Spec is None:
  121. # instance of ASN.1 schema
  122. inconsistency = value.isInconsistent
  123. if inconsistency:
  124. raise inconsistency
  125. namedTypes = value.componentType
  126. for idx, component in enumerate(value.values()):
  127. if namedTypes:
  128. namedType = namedTypes[idx]
  129. if namedType.isOptional and not component.isValue:
  130. continue
  131. if namedType.isDefaulted and component == namedType.asn1Object:
  132. continue
  133. compsMap[id(component)] = namedType
  134. else:
  135. compsMap[id(component)] = None
  136. comps.append((component, asn1Spec))
  137. else:
  138. # bare Python value + ASN.1 schema
  139. for idx, namedType in enumerate(asn1Spec.componentType.namedTypes):
  140. try:
  141. component = value[namedType.name]
  142. except KeyError:
  143. raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value))
  144. if namedType.isOptional and namedType.name not in value:
  145. continue
  146. if namedType.isDefaulted and component == namedType.asn1Object:
  147. continue
  148. compsMap[id(component)] = namedType
  149. comps.append((component, asn1Spec[idx]))
  150. for comp, compType in sorted(comps, key=self._componentSortKey):
  151. namedType = compsMap[id(comp)]
  152. if namedType:
  153. options.update(ifNotEmpty=namedType.isOptional)
  154. chunk = encodeFun(comp, compType, **options)
  155. # wrap open type blob if needed
  156. if namedType and namedType.openType:
  157. wrapType = namedType.asn1Object
  158. if wrapType.tagSet and not wrapType.isSameTypeWith(comp):
  159. chunk = encodeFun(chunk, wrapType, **options)
  160. substrate += chunk
  161. return substrate, True, True
  162. class SequenceEncoder(encoder.SequenceEncoder):
  163. omitEmptyOptionals = True
  164. TAG_MAP = encoder.TAG_MAP.copy()
  165. TAG_MAP.update({
  166. univ.Boolean.tagSet: BooleanEncoder(),
  167. univ.Real.tagSet: RealEncoder(),
  168. useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
  169. useful.UTCTime.tagSet: UTCTimeEncoder(),
  170. # Sequence & Set have same tags as SequenceOf & SetOf
  171. univ.SetOf.tagSet: SetOfEncoder(),
  172. univ.Sequence.typeId: SequenceEncoder()
  173. })
  174. TYPE_MAP = encoder.TYPE_MAP.copy()
  175. TYPE_MAP.update({
  176. univ.Boolean.typeId: BooleanEncoder(),
  177. univ.Real.typeId: RealEncoder(),
  178. useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
  179. useful.UTCTime.typeId: UTCTimeEncoder(),
  180. # Sequence & Set have same tags as SequenceOf & SetOf
  181. univ.Set.typeId: SetEncoder(),
  182. univ.SetOf.typeId: SetOfEncoder(),
  183. univ.Sequence.typeId: SequenceEncoder(),
  184. univ.SequenceOf.typeId: SequenceOfEncoder()
  185. })
  186. # deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9
  187. tagMap = TAG_MAP
  188. typeMap = TYPE_MAP
  189. class SingleItemEncoder(encoder.SingleItemEncoder):
  190. fixedDefLengthMode = False
  191. fixedChunkSize = 1000
  192. TAG_MAP = TAG_MAP
  193. TYPE_MAP = TYPE_MAP
  194. class Encoder(encoder.Encoder):
  195. SINGLE_ITEM_ENCODER = SingleItemEncoder
  196. #: Turns ASN.1 object into CER octet stream.
  197. #:
  198. #: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
  199. #: walks all its components recursively and produces a CER octet stream.
  200. #:
  201. #: Parameters
  202. #: ----------
  203. #: value: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
  204. #: A Python or pyasn1 object to encode. If Python object is given, `asnSpec`
  205. #: parameter is required to guide the encoding process.
  206. #:
  207. #: Keyword Args
  208. #: ------------
  209. #: asn1Spec:
  210. #: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
  211. #:
  212. #: Returns
  213. #: -------
  214. #: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
  215. #: Given ASN.1 object encoded into BER octet-stream
  216. #:
  217. #: Raises
  218. #: ------
  219. #: ~pyasn1.error.PyAsn1Error
  220. #: On encoding errors
  221. #:
  222. #: Examples
  223. #: --------
  224. #: Encode Python value into CER with ASN.1 schema
  225. #:
  226. #: .. code-block:: pycon
  227. #:
  228. #: >>> seq = SequenceOf(componentType=Integer())
  229. #: >>> encode([1, 2, 3], asn1Spec=seq)
  230. #: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
  231. #:
  232. #: Encode ASN.1 value object into CER
  233. #:
  234. #: .. code-block:: pycon
  235. #:
  236. #: >>> seq = SequenceOf(componentType=Integer())
  237. #: >>> seq.extend([1, 2, 3])
  238. #: >>> encode(seq)
  239. #: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
  240. #:
  241. encode = Encoder()
  242. # EncoderFactory queries class instance and builds a map of tags -> encoders