encoder.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. #
  2. # This file is part of pyasn1 software.
  3. #
  4. # Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
  5. # License: http://pyasn1.sf.net/license.html
  6. #
  7. from pyasn1.type import base, tag, univ, char, useful
  8. from pyasn1.codec.ber import eoo
  9. from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
  10. from pyasn1.compat.integer import to_bytes
  11. from pyasn1 import debug, error
  12. __all__ = ['encode']
  13. class AbstractItemEncoder(object):
  14. supportIndefLenMode = 1
  15. # noinspection PyMethodMayBeStatic
  16. def encodeTag(self, t, isConstructed):
  17. tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot
  18. v = tagClass | tagFormat
  19. if isConstructed:
  20. v |= tag.tagFormatConstructed
  21. if tagId < 31:
  22. return int2oct(v | tagId)
  23. else:
  24. s = int2oct(tagId & 0x7f)
  25. tagId >>= 7
  26. while tagId:
  27. s = int2oct(0x80 | (tagId & 0x7f)) + s
  28. tagId >>= 7
  29. return int2oct(v | 0x1F) + s
  30. def encodeLength(self, length, defMode):
  31. if not defMode and self.supportIndefLenMode:
  32. return int2oct(0x80)
  33. if length < 0x80:
  34. return int2oct(length)
  35. else:
  36. substrate = null
  37. while length:
  38. substrate = int2oct(length & 0xff) + substrate
  39. length >>= 8
  40. substrateLen = len(substrate)
  41. if substrateLen > 126:
  42. raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
  43. return int2oct(0x80 | substrateLen) + substrate
  44. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  45. raise error.PyAsn1Error('Not implemented')
  46. def _encodeEndOfOctets(self, encodeFun, defMode):
  47. if defMode or not self.supportIndefLenMode:
  48. return null
  49. else:
  50. return encodeFun(eoo.endOfOctets, defMode)
  51. def encode(self, encodeFun, value, defMode, maxChunkSize):
  52. substrate, isConstructed = self.encodeValue(
  53. encodeFun, value, defMode, maxChunkSize
  54. )
  55. tagSet = value.getTagSet()
  56. if tagSet:
  57. if not isConstructed: # primitive form implies definite mode
  58. defMode = True
  59. return self.encodeTag(
  60. tagSet[-1], isConstructed
  61. ) + self.encodeLength(
  62. len(substrate), defMode
  63. ) + substrate + self._encodeEndOfOctets(encodeFun, defMode)
  64. else:
  65. return substrate # untagged value
  66. class EndOfOctetsEncoder(AbstractItemEncoder):
  67. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  68. return null, 0
  69. class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
  70. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  71. if isinstance(value, base.AbstractConstructedAsn1Item):
  72. value = value.clone(tagSet=value.getTagSet()[:-1],
  73. cloneValueFlag=1)
  74. else:
  75. value = value.clone(tagSet=value.getTagSet()[:-1])
  76. return encodeFun(value, defMode, maxChunkSize), 1
  77. explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
  78. class BooleanEncoder(AbstractItemEncoder):
  79. supportIndefLenMode = 0
  80. _true = ints2octs((1,))
  81. _false = ints2octs((0,))
  82. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  83. return value and self._true or self._false, 0
  84. class IntegerEncoder(AbstractItemEncoder):
  85. supportIndefLenMode = 0
  86. supportCompactZero = False
  87. encodedZero = ints2octs((0,))
  88. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  89. if value == 0:
  90. # de-facto way to encode zero
  91. if self.supportCompactZero:
  92. return null, 0
  93. else:
  94. return self.encodedZero, 0
  95. return to_bytes(int(value), signed=True), 0
  96. class BitStringEncoder(AbstractItemEncoder):
  97. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  98. if len(value) % 8:
  99. alignedValue = value << (8 - len(value) % 8)
  100. else:
  101. alignedValue = value
  102. if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
  103. substrate = alignedValue.asOctets()
  104. return int2oct(len(substrate) * 8 - len(value)) + substrate, 0
  105. stop = 0
  106. substrate = null
  107. while stop < len(value):
  108. start = stop
  109. stop = min(start + maxChunkSize * 8, len(value))
  110. substrate += encodeFun(alignedValue[start:stop], defMode, maxChunkSize)
  111. return substrate, 1
  112. class OctetStringEncoder(AbstractItemEncoder):
  113. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  114. if not maxChunkSize or len(value) <= maxChunkSize:
  115. return value.asOctets(), 0
  116. else:
  117. pos = 0
  118. substrate = null
  119. while True:
  120. v = value.clone(value[pos:pos + maxChunkSize])
  121. if not v:
  122. break
  123. substrate = substrate + encodeFun(v, defMode, maxChunkSize)
  124. pos += maxChunkSize
  125. return substrate, 1
  126. class NullEncoder(AbstractItemEncoder):
  127. supportIndefLenMode = 0
  128. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  129. return null, 0
  130. class ObjectIdentifierEncoder(AbstractItemEncoder):
  131. supportIndefLenMode = 0
  132. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  133. oid = value.asTuple()
  134. if len(oid) < 2:
  135. raise error.PyAsn1Error('Short OID %s' % (value,))
  136. octets = ()
  137. # Build the first pair
  138. first = oid[0]
  139. second = oid[1]
  140. if 0 <= second <= 39:
  141. if first == 1:
  142. oid = (second + 40,) + oid[2:]
  143. elif first == 0:
  144. oid = (second,) + oid[2:]
  145. elif first == 2:
  146. oid = (second + 80,) + oid[2:]
  147. else:
  148. raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
  149. elif first == 2:
  150. oid = (second + 80,) + oid[2:]
  151. else:
  152. raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
  153. # Cycle through subIds
  154. for subOid in oid:
  155. if 0 <= subOid <= 127:
  156. # Optimize for the common case
  157. octets += (subOid,)
  158. elif subOid > 127:
  159. # Pack large Sub-Object IDs
  160. res = (subOid & 0x7f,)
  161. subOid >>= 7
  162. while subOid:
  163. res = (0x80 | (subOid & 0x7f),) + res
  164. subOid >>= 7
  165. # Add packed Sub-Object ID to resulted Object ID
  166. octets += res
  167. else:
  168. raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
  169. return ints2octs(octets), 0
  170. class RealEncoder(AbstractItemEncoder):
  171. supportIndefLenMode = 0
  172. binEncBase = 2 # set to None to choose encoding base automatically
  173. @staticmethod
  174. def _dropFloatingPoint(m, encbase, e):
  175. ms, es = 1, 1
  176. if m < 0:
  177. ms = -1 # mantissa sign
  178. if e < 0:
  179. es = -1 # exponenta sign
  180. m *= ms
  181. if encbase == 8:
  182. m *= 2 ** (abs(e) % 3 * es)
  183. e = abs(e) // 3 * es
  184. elif encbase == 16:
  185. m *= 2 ** (abs(e) % 4 * es)
  186. e = abs(e) // 4 * es
  187. while True:
  188. if int(m) != m:
  189. m *= encbase
  190. e -= 1
  191. continue
  192. break
  193. return ms, int(m), encbase, e
  194. def _chooseEncBase(self, value):
  195. m, b, e = value
  196. encBase = [2, 8, 16]
  197. if value.binEncBase in encBase:
  198. return self._dropFloatingPoint(m, value.binEncBase, e)
  199. elif self.binEncBase in encBase:
  200. return self._dropFloatingPoint(m, self.binEncBase, e)
  201. # auto choosing base 2/8/16
  202. mantissa = [m, m, m]
  203. exponenta = [e, e, e]
  204. sign = 1
  205. encbase = 2
  206. e = float('inf')
  207. for i in range(3):
  208. sign, mantissa[i], encBase[i], exponenta[i] = \
  209. self._dropFloatingPoint(mantissa[i], encBase[i], exponenta[i])
  210. if abs(exponenta[i]) < abs(e) or \
  211. (abs(exponenta[i]) == abs(e) and mantissa[i] < m):
  212. e = exponenta[i]
  213. m = int(mantissa[i])
  214. encbase = encBase[i]
  215. return sign, m, encbase, e
  216. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  217. if value.isPlusInfinity():
  218. return int2oct(0x40), 0
  219. if value.isMinusInfinity():
  220. return int2oct(0x41), 0
  221. m, b, e = value
  222. if not m:
  223. return null, 0
  224. if b == 10:
  225. return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0
  226. elif b == 2:
  227. fo = 0x80 # binary encoding
  228. ms, m, encbase, e = self._chooseEncBase(value)
  229. if ms < 0: # mantissa sign
  230. fo |= 0x40 # sign bit
  231. # exponenta & mantissa normalization
  232. if encbase == 2:
  233. while m & 0x1 == 0:
  234. m >>= 1
  235. e += 1
  236. elif encbase == 8:
  237. while m & 0x7 == 0:
  238. m >>= 3
  239. e += 1
  240. fo |= 0x10
  241. else: # encbase = 16
  242. while m & 0xf == 0:
  243. m >>= 4
  244. e += 1
  245. fo |= 0x20
  246. sf = 0 # scale factor
  247. while m & 0x1 == 0:
  248. m >>= 1
  249. sf += 1
  250. if sf > 3:
  251. raise error.PyAsn1Error('Scale factor overflow') # bug if raised
  252. fo |= sf << 2
  253. eo = null
  254. if e == 0 or e == -1:
  255. eo = int2oct(e & 0xff)
  256. else:
  257. while e not in (0, -1):
  258. eo = int2oct(e & 0xff) + eo
  259. e >>= 8
  260. if e == 0 and eo and oct2int(eo[0]) & 0x80:
  261. eo = int2oct(0) + eo
  262. if e == -1 and eo and not (oct2int(eo[0]) & 0x80):
  263. eo = int2oct(0xff) + eo
  264. n = len(eo)
  265. if n > 0xff:
  266. raise error.PyAsn1Error('Real exponent overflow')
  267. if n == 1:
  268. pass
  269. elif n == 2:
  270. fo |= 1
  271. elif n == 3:
  272. fo |= 2
  273. else:
  274. fo |= 3
  275. eo = int2oct(n & 0xff) + eo
  276. po = null
  277. while m:
  278. po = int2oct(m & 0xff) + po
  279. m >>= 8
  280. substrate = int2oct(fo) + eo + po
  281. return substrate, 0
  282. else:
  283. raise error.PyAsn1Error('Prohibited Real base %s' % b)
  284. class SequenceEncoder(AbstractItemEncoder):
  285. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  286. value.setDefaultComponents()
  287. value.verifySizeSpec()
  288. substrate = null
  289. idx = len(value)
  290. while idx > 0:
  291. idx -= 1
  292. if value[idx] is None: # Optional component
  293. continue
  294. component = value.getDefaultComponentByPosition(idx)
  295. if component is not None and component == value[idx]:
  296. continue
  297. substrate = encodeFun(
  298. value[idx], defMode, maxChunkSize
  299. ) + substrate
  300. return substrate, 1
  301. class SequenceOfEncoder(AbstractItemEncoder):
  302. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  303. value.verifySizeSpec()
  304. substrate = null
  305. idx = len(value)
  306. while idx > 0:
  307. idx -= 1
  308. substrate = encodeFun(
  309. value[idx], defMode, maxChunkSize
  310. ) + substrate
  311. return substrate, 1
  312. class ChoiceEncoder(AbstractItemEncoder):
  313. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  314. return encodeFun(value.getComponent(), defMode, maxChunkSize), 1
  315. class AnyEncoder(OctetStringEncoder):
  316. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  317. return value.asOctets(), defMode == 0
  318. tagMap = {
  319. eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
  320. univ.Boolean.tagSet: BooleanEncoder(),
  321. univ.Integer.tagSet: IntegerEncoder(),
  322. univ.BitString.tagSet: BitStringEncoder(),
  323. univ.OctetString.tagSet: OctetStringEncoder(),
  324. univ.Null.tagSet: NullEncoder(),
  325. univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
  326. univ.Enumerated.tagSet: IntegerEncoder(),
  327. univ.Real.tagSet: RealEncoder(),
  328. # Sequence & Set have same tags as SequenceOf & SetOf
  329. univ.SequenceOf.tagSet: SequenceOfEncoder(),
  330. univ.SetOf.tagSet: SequenceOfEncoder(),
  331. univ.Choice.tagSet: ChoiceEncoder(),
  332. # character string types
  333. char.UTF8String.tagSet: OctetStringEncoder(),
  334. char.NumericString.tagSet: OctetStringEncoder(),
  335. char.PrintableString.tagSet: OctetStringEncoder(),
  336. char.TeletexString.tagSet: OctetStringEncoder(),
  337. char.VideotexString.tagSet: OctetStringEncoder(),
  338. char.IA5String.tagSet: OctetStringEncoder(),
  339. char.GraphicString.tagSet: OctetStringEncoder(),
  340. char.VisibleString.tagSet: OctetStringEncoder(),
  341. char.GeneralString.tagSet: OctetStringEncoder(),
  342. char.UniversalString.tagSet: OctetStringEncoder(),
  343. char.BMPString.tagSet: OctetStringEncoder(),
  344. # useful types
  345. useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
  346. useful.GeneralizedTime.tagSet: OctetStringEncoder(),
  347. useful.UTCTime.tagSet: OctetStringEncoder()
  348. }
  349. # Type-to-codec map for ambiguous ASN.1 types
  350. typeMap = {
  351. univ.Set.typeId: SequenceEncoder(),
  352. univ.SetOf.typeId: SequenceOfEncoder(),
  353. univ.Sequence.typeId: SequenceEncoder(),
  354. univ.SequenceOf.typeId: SequenceOfEncoder(),
  355. univ.Choice.typeId: ChoiceEncoder(),
  356. univ.Any.typeId: AnyEncoder()
  357. }
  358. class Encoder(object):
  359. supportIndefLength = True
  360. # noinspection PyDefaultArgument
  361. def __init__(self, tagMap, typeMap={}):
  362. self.__tagMap = tagMap
  363. self.__typeMap = typeMap
  364. def __call__(self, value, defMode=True, maxChunkSize=0):
  365. if not isinstance(value, base.Asn1Item):
  366. raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
  367. if not defMode and not self.supportIndefLength:
  368. raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
  369. debug.logger & debug.flagEncoder and debug.logger(
  370. 'encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (
  371. not defMode and 'in' or '', maxChunkSize, value.prettyPrintType(), value.prettyPrint()))
  372. tagSet = value.getTagSet()
  373. if len(tagSet) > 1:
  374. concreteEncoder = explicitlyTaggedItemEncoder
  375. else:
  376. if value.typeId is not None and value.typeId in self.__typeMap:
  377. concreteEncoder = self.__typeMap[value.typeId]
  378. elif tagSet in self.__tagMap:
  379. concreteEncoder = self.__tagMap[tagSet]
  380. else:
  381. tagSet = value.baseTagSet
  382. if tagSet in self.__tagMap:
  383. concreteEncoder = self.__tagMap[tagSet]
  384. else:
  385. raise error.PyAsn1Error('No encoder for %s' % (value,))
  386. debug.logger & debug.flagEncoder and debug.logger(
  387. 'using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
  388. substrate = concreteEncoder.encode(
  389. self, value, defMode, maxChunkSize
  390. )
  391. debug.logger & debug.flagEncoder and debug.logger(
  392. 'built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate)))
  393. return substrate
  394. #: Turns ASN.1 object into BER octet stream.
  395. #:
  396. #: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
  397. #: walks all its components recursively and produces a BER octet stream.
  398. #:
  399. #: Parameters
  400. #: ----------
  401. # value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
  402. #: A pyasn1 object to encode
  403. #:
  404. #: defMode: :py:class:`bool`
  405. #: If `False`, produces indefinite length encoding
  406. #:
  407. #: maxChunkSize: :py:class:`int`
  408. #: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
  409. #:
  410. #: Returns
  411. #: -------
  412. #: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
  413. #: Given ASN.1 object encoded into BER octetstream
  414. #:
  415. #: Raises
  416. #: ------
  417. #: : :py:class:`pyasn1.error.PyAsn1Error`
  418. #: On encoding errors
  419. encode = Encoder(tagMap, typeMap)