diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py index 841ed901e7f51c084e922481500f2e426ac584bd..26baaa4af9886f03bcc3f72f95472b2907f70a34 100644 --- a/scapy/asn1/asn1.py +++ b/scapy/asn1/asn1.py @@ -1,6 +1,7 @@ ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi <phil@secdev.org> +## Modified by Maxence Tury <maxence.tury@ssi.gouv.fr> ## This program is published under a GPLv2 license """ @@ -8,6 +9,7 @@ ASN.1 (Abstract Syntax Notation One) """ import random +from datetime import datetime from scapy.config import conf from scapy.error import Scapy_Exception,warning from scapy.volatile import RandField @@ -153,27 +155,25 @@ class ASN1_Class_UNIVERSAL(ASN1_Class): EMBEDDED_PDF = 11 UTF8_STRING = 12 RELATIVE_OID = 13 - SEQUENCE = 0x30#XXX 16 ?? - SET = 0x31 #XXX 17 ?? + SEQUENCE = 16|0x20 # constructed encoding + SET = 17|0x20 # constructed encoding NUMERIC_STRING = 18 PRINTABLE_STRING = 19 - T61_STRING = 20 + T61_STRING = 20 # aka TELETEX_STRING VIDEOTEX_STRING = 21 IA5_STRING = 22 UTC_TIME = 23 GENERALIZED_TIME = 24 GRAPHIC_STRING = 25 - ISO646_STRING = 26 + ISO646_STRING = 26 # aka VISIBLE_STRING GENERAL_STRING = 27 UNIVERSAL_STRING = 28 CHAR_STRING = 29 BMP_STRING = 30 - IPADDRESS = 0x40 - COUNTER32 = 0x41 - GAUGE32 = 0x42 - TIME_TICKS = 0x43 - COUNTER64 = 0x46 - SEP = 0x80 + IPADDRESS = 0|0x40 # application-specific encoding + COUNTER32 = 1|0x40 # application-specific encoding + TIME_TICKS = 3|0x40 # application-specific encoding + class ASN1_Object_metaclass(type): def __new__(cls, name, bases, dct): @@ -184,7 +184,6 @@ class ASN1_Object_metaclass(type): warning("Error registering %r for %r" % (c.tag, c.codec)) return c - class ASN1_Object: __metaclass__ = ASN1_Object_metaclass tag = ASN1_Class_UNIVERSAL.ANY @@ -205,6 +204,13 @@ class ASN1_Object: def __cmp__(self, other): return cmp(self.val, other) + +####################### +#### ASN1 objects #### +####################### + +# on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value + class ASN1_DECODING_ERROR(ASN1_Object): tag = ASN1_Class_UNIVERSAL.ERROR def __init__(self, val, exc=None): @@ -230,61 +236,124 @@ class ASN1_BADTAG(ASN1_force): class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER + def __repr__(self): + h = hex(self.val) + if h[-1] == "L": + h = h[:-1] + # cut at 22 because with leading '0x', x509 serials should be < 23 + if len(h) > 22: + h = h[:12] + "..." + h[-10:] + r = repr(self.val) + if len(r) > 20: + r = r[:10] + "..." + r[-10:] + return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r) + +class ASN1_BOOLEAN(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.BOOLEAN + # BER: 0 means False, anything else means True + def __repr__(self): + return str((not (self.val==0))) + " " + ASN1_Object.__repr__(self) + +class ASN1_BIT_STRING(ASN1_Object): + """ + /!\ ASN1_BIT_STRING values are bit strings like "011101". + /!\ A zero-bit padded readable string is provided nonetheless. + """ + tag = ASN1_Class_UNIVERSAL.BIT_STRING + def __init__(self, val, readable=False): + if readable: + self.val_readable = val + val = "".join(format(ord(x), 'b').zfill(8) for x in val) + self.unused_bits = 0 + else: + if len(val) % 8 == 0: + self.unused_bits = 0 + else: + self.unused_bits = 8 - len(val)%8 + padded_val = val + "0"*self.unused_bits + bytes_arr = zip(*[iter(padded_val)]*8) + self.val_readable = "".join(chr(int("".join(x),2)) for x in bytes_arr) + ASN1_Object.__init__(self, val) + def __repr__(self): + if len(self.val) <= 16: + return "<%s[%r] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), self.val, self.unused_bits, "s" if self.unused_bits>1 else "") + else: + s = self.val_readable + if len(s) > 20: + s = s[:10] + "..." + s[-10:] + return "<%s[%r] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), s, self.unused_bits, "s" if self.unused_bits>1 else "") class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING -class ASN1_BIT_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.BIT_STRING +class ASN1_NULL(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.NULL + def __repr__(self): + return ASN1_Object.__repr__(self) -class ASN1_PRINTABLE_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING +class ASN1_OID(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.OID + def __init__(self, val): + val = conf.mib._oid(val) + ASN1_Object.__init__(self, val) + self.oidname = conf.mib._oidname(val) + def __repr__(self): + return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname) -class ASN1_T61_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.T61_STRING +class ASN1_ENUMERATED(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.ENUMERATED -class ASN1_IA5_STRING(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.IA5_STRING +class ASN1_UTF8_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.UTF8_STRING class ASN1_NUMERIC_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING +class ASN1_PRINTABLE_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class ASN1_T61_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.T61_STRING + class ASN1_VIDEOTEX_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING -class ASN1_IPADDRESS(ASN1_STRING): - tag = ASN1_Class_UNIVERSAL.IPADDRESS +class ASN1_IA5_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.IA5_STRING class ASN1_UTC_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME + def __init__(self, val): + pretty_time = "" + if len(val) == 13 and val[-1] == "Z": + dt = datetime.strptime(val[:-1], "%y%m%d%H%M%S") + pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") + self.pretty_time = pretty_time + ASN1_STRING.__init__(self, val) + def __repr__(self): + return self.pretty_time + " " + ASN1_STRING.__repr__(self) class ASN1_GENERALIZED_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + def __init__(self, val): + pretty_time = "" + if len(val) == 15 and val[-1] == "Z": + dt = datetime.strptime(val[:-1], "%Y%m%d%H%M%S") + pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") + self.pretty_time = pretty_time + ASN1_STRING.__init__(self, val) + def __repr__(self): + return self.pretty_time + " " + ASN1_STRING.__repr__(self) -class ASN1_TIME_TICKS(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.TIME_TICKS +class ASN1_ISO646_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.ISO646_STRING -class ASN1_BOOLEAN(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.BOOLEAN +class ASN1_UNIVERSAL_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING -class ASN1_ENUMERATED(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.ENUMERATED - -class ASN1_NULL(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.NULL +class ASN1_BMP_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.BMP_STRING -class ASN1_SEP(ASN1_NULL): - tag = ASN1_Class_UNIVERSAL.SEP - -class ASN1_GAUGE32(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.GAUGE32 - -class ASN1_COUNTER32(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.COUNTER32 - -class ASN1_COUNTER64(ASN1_INTEGER): - tag = ASN1_Class_UNIVERSAL.COUNTER64 - class ASN1_SEQUENCE(ASN1_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE def strshow(self, lvl=0): @@ -295,17 +364,15 @@ class ASN1_SEQUENCE(ASN1_Object): class ASN1_SET(ASN1_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET - -class ASN1_OID(ASN1_Object): - tag = ASN1_Class_UNIVERSAL.OID - def __init__(self, val): - val = conf.mib._oid(val) - ASN1_Object.__init__(self, val) - def __repr__(self): - return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val)) - def __oidname__(self): - return '%s'%conf.mib._oidname(self.val) - +class ASN1_IPADDRESS(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.IPADDRESS + +class ASN1_COUNTER32(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.COUNTER32 + +class ASN1_TIME_TICKS(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.TIME_TICKS + conf.ASN1_default_codec = ASN1_Codecs.BER