Advertisement
Guest User

Untitled

a guest
Oct 21st, 2019
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.18 KB | None | 0 0
  1. """ ASN1 DER/BER symmetric parser/encoder. """
  2. import enum
  3.  
  4. def decode_varint(bytearr):
  5. value = bytearr[0] & 0x7f
  6. while bytearr.pop(0) & 0x80:
  7. value = (value << 7) | (bytearr[0] & 0x7f)
  8. return value
  9.  
  10. def encode_varint(n):
  11. res = [n & 0x7f]
  12. n >>= 7
  13. while n:
  14. res.append((n & 0x7f) | 0x80)
  15. n >>= 7
  16.  
  17. return bytes(reversed(res))
  18.  
  19. class BitField:
  20. def __init__(self, value=0):
  21. self.value = value
  22.  
  23. def __int__(self):
  24. return self.value
  25. def __index__(self):
  26. return self.value
  27.  
  28. def __getitem__(self, sl):
  29. if isinstance(sl, slice):
  30. bits = bin(self.value)[2:][::-1]
  31. return int(bits[sl][::-1] or '0', 2)
  32. else:
  33. return (self.value >> sl.__index__()) & 1
  34.  
  35. def __setitem__(self, sl, value):
  36. if isinstance(sl, slice):
  37. size = max(sl.start or 0, sl.stop or 0, self.value.bit_length())
  38. bits = list(bin(self.value)[2:].zfill(size)[::-1])
  39. bits[sl] = list(bin(value)[2:].zfill(len(bits[sl]))[::-1])
  40. self.value = int(''.join(reversed(bits)), 2)
  41. else:
  42. self.value |= (bool(value) << sl.__index__())
  43.  
  44. def __repr__(self):
  45. return f'<BitField({self.value:#x})>'
  46.  
  47. DER_TAG_EXTENDED = 0x1f
  48.  
  49. class ASN1Tag(enum.IntEnum):
  50. Boolean = 1
  51. Integer = 2
  52. BitString = 3
  53. OctetString = 4
  54. Null = 5
  55. OID = 6
  56. UTF8String = 12
  57. Sequence = 16
  58. Set = 17
  59. PrintableString = 19
  60. T61String = 20
  61. IA5String = 22
  62. UTCTime = 23
  63. GeneralString = 27
  64. UniversalString = 28
  65.  
  66. class ASN1Class(enum.IntEnum):
  67. Universal = 0
  68. Application = 1
  69. Context = 2
  70. Private = 3
  71.  
  72. class DERObject:
  73. CONS = CLASS = TAG = None
  74. def __init__(self, value=None, data=None):
  75. if not ((value is None) ^ (data is None)):
  76. raise TypeError(f"{self.__class__.__name__}: must supply exactly one of `data`, `value`")
  77. if value is None:
  78. value = self._decode_value(data)
  79. elif data is None:
  80. data = self._encode_value(value)
  81. self._data = data
  82. self._value = value
  83.  
  84. def _encode_value(self, value):
  85. return bytes(value)
  86. def _decode_value(self, data):
  87. return data
  88.  
  89. @property
  90. def value(self):
  91. return self._value
  92.  
  93. @value.setter
  94. def setvalue(self, value):
  95. self._value = value
  96. self._data = self._encode_value(value)
  97.  
  98. @property
  99. def data(self):
  100. return self._data
  101.  
  102. @data.setter
  103. def data(self, data):
  104. self._value = self._decode_value(data)
  105.  
  106. def __bytes__(self):
  107. # hack to suppoprt BER shit
  108. length = len(self.data)
  109. if getattr(self, '_indefinite', False):
  110. length = None
  111. return self._encode_header(length) + self.data
  112.  
  113. @classmethod
  114. def _encode_header(cls, length):
  115. tag, asn1cls, cons = cls.TAG, cls.CLASS, cls.CONS
  116. if tag >= 0x1f: #extended
  117. tag = DER_TAG_EXTENDED
  118. head = BitField()
  119. head[6:8] = asn1cls
  120. head[5] = cons
  121. head[:5] = tag
  122. data = bytearray([head])
  123. if tag == DER_TAG_EXTENDED:
  124. data += encode_varint(cls.TAG)
  125. if length is None:
  126. data.append(0x80)
  127. elif length < 0x80:
  128. data.append(length)
  129. else:
  130. byte_count = (length.bit_length() + 7) // 8
  131. data.append(0x80 | byte_count)
  132. data += length.to_bytes(byte_count, 'big')
  133. return bytes(data)
  134.  
  135. @classmethod
  136. def _decode_header(cls, bytearr):
  137. head = BitField(bytearr.pop(0))
  138. asn_cls, cons, tag = head[6:8], head[5], head[:5]
  139. if tag == DER_TAG_EXTENDED:
  140. tag = decode_varint(bytearr)
  141. if len(bytearr) == 0:
  142. raise ValueError("truncated der header")
  143.  
  144. length = bytearr.pop(0)
  145. if length > 0x80:
  146. byte_count = length & 0x7f
  147. if byte_count > len(bytearr):
  148. raise ValueError("bad der length")
  149. length = int.from_bytes(bytearr[:byte_count], 'big')
  150. bytearr[:byte_count] = b''
  151.  
  152. elif length == 0x80:
  153. length = None
  154. return (tag, cons, asn_cls, length, )
  155.  
  156. @classmethod
  157. def from_bytes(cls, data):
  158. arr = bytearray(data)
  159. tag, cons, asn1cls, length = cls._decode_header(arr)
  160. asn1cls = ASN1Class(asn1cls)
  161. if length is not None:
  162. if len(arr) < length:
  163. raise ValueError("Truncated DER object")
  164. indefinite = False
  165. else:
  166. indefinite = True
  167. length = len(arr)
  168.  
  169. cls = cls.find_class(tag, cons, asn1cls)
  170. obj = cls(data=bytes(arr[:length]))
  171. obj.header_length = len(data) - len(arr)
  172. obj.length = obj.header_length + length
  173. # hack
  174. obj._indefinite = indefinite
  175. return obj
  176.  
  177. def __repr__(self):
  178. return f"{self.__class__.__name__}({self.value!r})"
  179.  
  180. @classmethod
  181. def find_class(cls, tag, cons, asn1cls):
  182. if cls.TAG == tag and cls.CONS == cons and cls.CLASS == asn1cls:
  183. return cls
  184.  
  185. if cls.TAG is not None and cls.TAG != tag:
  186. return None
  187. if cls.CONS is not None and cls.CONS != cons:
  188. return None
  189. if cls.CLASS is not None and cls.CLASS != asn1cls:
  190. return None
  191.  
  192. for subclass in cls.__subclasses__():
  193. found = subclass.find_class(tag, cons, asn1cls)
  194. if found is not None:
  195. return found
  196.  
  197. # class not found; create new
  198. return type(f"{ASN1Class(asn1cls).name}{'Cons' if cons else 'Prim'}[{tag:#x}]",
  199. (cls,), {'TAG': tag, 'CLASS': asn1cls, 'CONS': cons})
  200.  
  201. class Constructed(DERObject):
  202. CONS = True
  203.  
  204. def _encode_value(self, value):
  205. return b''.join(bytes(x) for x in value)
  206.  
  207. def _decode_value(self, data):
  208. value = []
  209. while data:
  210. decoded = DERObject.from_bytes(data)
  211. value.append(decoded)
  212. data = data[decoded.length:]
  213. return value
  214.  
  215. def __iter__(self):
  216. return iter(self.value)
  217.  
  218. def __getitem__(self, index):
  219. return self.value[index]
  220.  
  221. def __len__(self):
  222. return len(self.value)
  223.  
  224.  
  225. class Universal(DERObject):
  226. CLASS = ASN1Class.Universal
  227.  
  228. class UniversalConstructed(Universal, Constructed):
  229. pass
  230.  
  231. class Sequence(UniversalConstructed):
  232. TAG = ASN1Tag.Sequence
  233. pass
  234.  
  235. class Set(UniversalConstructed):
  236. TAG = ASN1Tag.Set
  237. CLASS = ASN1Class.Universal
  238. pass
  239.  
  240. class Primitive(DERObject):
  241. CONS = False
  242.  
  243. def _decode_value(self, data):
  244. return data
  245.  
  246. def _encode_value(self, value):
  247. return bytes(value)
  248.  
  249. @property
  250. def value(self):
  251. return self._decode_value(self.data)
  252. @value.setter
  253. def setvalue(self, value):
  254. self.data = self._encode_value(value)
  255.  
  256.  
  257. class UniversalPrimitive(Universal, Primitive):
  258. pass
  259.  
  260. class Null(UniversalPrimitive):
  261. TAG = ASN1Tag.Null
  262.  
  263. def _decode_value(self, data):
  264. assert len(data) == 0, "Null tag with data"
  265. return None
  266.  
  267. def _encode_value(self, value):
  268. assert value is None, "Null tag with non-None value"
  269. return b''
  270.  
  271. class OID(UniversalPrimitive):
  272. TAG = ASN1Tag.OID
  273.  
  274. def _decode_value(self, data):
  275. oid = []
  276. oid.append(data[0] // 40)
  277. oid.append(data[0] % 40)
  278. d = bytearray(data[1:])
  279. while d:
  280. oid.append(decode_varint(d))
  281. return '.'.join(str(x) for x in oid)
  282.  
  283. def _encode_value(self, value):
  284. if isinstance(value, str):
  285. value = [int(x) for x in value.split('.')]
  286. data = bytearray()
  287. data.append(value[0] * 40 + value[1])
  288. for n in value[2:]:
  289. data += encode_varint(n)
  290. return bytes(data)
  291.  
  292. def __str__(self):
  293. return '.'.join(str(x) for x in self.oid())
  294.  
  295. class Integer(UniversalPrimitive):
  296. TAG = ASN1Tag.Integer
  297.  
  298. def _decode_value(self, data):
  299. return int.from_bytes(data, 'big')
  300.  
  301. def _encode_value(self, value):
  302. byte_length = ((value.bit_length() + 7) // 8 )
  303. return value.to_bytes(byte_length, 'big')
  304.  
  305. def __int__(self):
  306. return self.value
  307.  
  308. class Boolean(UniversalPrimitive):
  309. TAG = ASN1Tag.Boolean
  310. def _decode_value(self, data):
  311. return bool(int.from_bytes(data, 'big'))
  312.  
  313. def _encode_value(self, value):
  314. byte_length = ((value.bit_length() + 7) // 8 )
  315. return value.to_bytes(byte_length, 'big')
  316.  
  317. def __int__(self):
  318. return int.from_bytes(self.data, 'big')
  319.  
  320.  
  321. class StringType:
  322. def _decode_value(self, data):
  323. return data.decode(self.ENCODING)
  324.  
  325. def _encode_value(self, value):
  326. return value.encode(self.ENCODING)
  327.  
  328. def __str__(self):
  329. return str(self.value)
  330.  
  331. class UTF8String(StringType, UniversalPrimitive):
  332. TAG = ASN1Tag.UTF8String
  333. ENCODING = 'utf-8'
  334.  
  335. class GeneralString(StringType, UniversalPrimitive):
  336. TAG = ASN1Tag.GeneralString
  337. ENCODING = 'utf-8'
  338.  
  339. class UniversalString(StringType, UniversalPrimitive):
  340. TAG = ASN1Tag.UniversalString
  341. ENCODING = 'utf-8'
  342.  
  343. class IA5String(StringType, UniversalPrimitive):
  344. TAG = ASN1Tag.IA5String
  345. ENCODING = 'ascii'
  346.  
  347. class PrintableString(StringType, UniversalPrimitive):
  348. TAG = ASN1Tag.PrintableString
  349. ENCODING = 'ascii'
  350.  
  351. class OctetString(UniversalPrimitive):
  352. TAG = ASN1Tag.OctetString
  353. pass
  354.  
  355. class BitString(UniversalPrimitive):
  356. TAG = ASN1Tag.BitString
  357. def __int__(self):
  358. return int.from_bytes(self.data, 'big')
  359.  
  360.  
  361. if __name__ == '__main__':
  362. import base64
  363. # Google CA
  364. test = base64.b64decode(
  365. """MIIEKDCCAxCgAwIBAgIQAQAhJYiw+lmnd+8Fe2Yn3zANBgkqhkiG9w0BAQsFADBCMQswCQYDVQQGEwJV
  366. UzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMB4XDTE3
  367. MDUyMjExMzIzN1oXDTE4MTIzMTIzNTk1OVowSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJ
  368. bmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwggEiMA0GCSqGSIb3DQEBAQUA
  369. A4IBDwAwggEKAoIBAQCcKgR3XNhQkToGo4Lg2FBIvIk/8RlwGohGfuCPxfGJziHuWv5hDbcyRImgdAtT
  370. T1WkzoJile7rWV/G4QWAEsRelD+8W0g49FP3JOb7kekVxM/0Uw30SvyfVN59vqBrb4fA0FAfKDADQNoI
  371. c1Fsf/86PKc3Bo69SxEE630k3ub5/DFx+5TVYPMuSq9C0svqxGoassxT3RVLix/IGWEfzZ2oPmMrhDVp
  372. ZYTIGcVGIvhTlb7jgEoQxirsupcgEcc5mRAEoPBhepUljE5SdeK27QjKFPzOImqzTs9GA5eXA37Asd57
  373. r0Uzz7o+cbfe9CUlwg01iZ2d+w4ReYkeN8WvjnJpAgMBAAGjggERMIIBDTAfBgNVHSMEGDAWgBTAepho
  374. jYn7qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYDVR0PAQH/BAQD
  375. AgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMBIGA1UdEwEB
  376. /wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9i
  377. YWwuY3JsMCEGA1UdIAQaMBgwDAYKKwYBBAHWeQIFATAIBgZngQwBAgIwHQYDVR0lBBYwFAYIKwYBBQUH
  378. AwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQDKSeWs12Rkd1u+cfrP9B4jx5ppY1Rf60zWGSgj
  379. ZGaOHMeHgGRfBIsmr5jfCnC8vBk97nszqX+99AXUcLsFJnnqmseYuQcZZTTMPOk/xQH6bwx+23pwXEz+
  380. LQDwyr4tjrSogPsBE4jLnD/lu3fKOmc2887VJwJyQ6C9bgLxRwVxPgFZ6RGeGvOED4Cmong1L7bHon8X
  381. fOGLVq7uZ4hRJzBgpWJSwzfVO+qFKgE4h6LPcK2kesnE58rF2rwjMvL+GMJ74N87L9TQEOaWTPtEtyFk
  382. DbkAlDASJodYmDkFOA/MgkgMCkdm7r+0X8T/cKjhf4t5K7hlMqO5tzHpCvX2HzLc""")
  383.  
  384. obj = DERObject.from_bytes(test)
  385. assert bytes(obj) == test
  386. #print(repr(obj))
  387. from rupy import pp
  388. pp(obj)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement