SHARE
TWEET

Untitled

a guest Oct 21st, 2019 91 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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)
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top