diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py index 55d7ab6b0..6217d75fd 100644 --- a/scapy/asn1/asn1.py +++ b/scapy/asn1/asn1.py @@ -262,19 +262,42 @@ class ASN1_BIT_STRING(ASN1_Object): """ tag = ASN1_Class_UNIVERSAL.BIT_STRING def __init__(self, val, readable=False): - if readable: - self.val_readable = val - val = "".join(binrepr(ord(x)).zfill(8) for x in val) - self.unused_bits = 0 + if not readable: + self.val = val else: - if len(val) % 8 == 0: - self.unused_bits = 0 + self.val_readable = val + def __setattr__(self, name, value): + if name == "val_readable": + if isinstance(value, str): + val = "".join(binrepr(ord(x)).zfill(8) for x in value) 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) + val = "" + super(ASN1_Object, self).__setattr__("val", val) + super(ASN1_Object, self).__setattr__(name, value) + super(ASN1_Object, self).__setattr__("unused_bits", 0) + elif name == "val": + if isinstance(value, str): + if len([c for c in value if c not in ["0", "1"]]) > 0: + print "Invalid operation: 'val' is not a valid bit string." + return + else: + if len(value) % 8 == 0: + unused_bits = 0 + else: + unused_bits = 8 - (len(value) % 8) + padded_value = value + ("0" * unused_bits) + bytes_arr = zip(*[iter(padded_value)]*8) + val_readable = "".join(chr(int("".join(x),2)) for x in bytes_arr) + else: + val_readable = "" + unused_bits = 0 + super(ASN1_Object, self).__setattr__("val_readable", val_readable) + super(ASN1_Object, self).__setattr__(name, value) + super(ASN1_Object, self).__setattr__("unused_bits", unused_bits) + elif name == "unused_bits": + print "Invalid operation: unused_bits rewriting is not supported." + else: + super(ASN1_Object, self).__setattr__(name, value) 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 "") @@ -327,26 +350,46 @@ class ASN1_IA5_STRING(ASN1_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) + super(ASN1_UTC_TIME, self).__init__(val) + def __setattr__(self, name, value): + if name == "val": + pretty_time = None + if (isinstance(value, str) and + len(value) == 13 and value[-1] == "Z"): + dt = datetime.strptime(value[:-1], "%y%m%d%H%M%S") + pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") + else: + pretty_time = "%s [invalid utc_time]" % value + super(ASN1_UTC_TIME, self).__setattr__("pretty_time", pretty_time) + super(ASN1_UTC_TIME, self).__setattr__(name, value) + elif name == "pretty_time": + print "Invalid operation: pretty_time rewriting is not supported." + else: + super(ASN1_UTC_TIME, self).__setattr__(name, value) def __repr__(self): - return self.pretty_time + " " + ASN1_STRING.__repr__(self) + return "%s %s" % (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) + super(ASN1_GENERALIZED_TIME, self).__init__(val) + def __setattr__(self, name, value): + if name == "val": + pretty_time = None + if (isinstance(value, str) and + len(value) == 15 and value[-1] == "Z"): + dt = datetime.strptime(value[:-1], "%Y%m%d%H%M%S") + pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT") + else: + pretty_time = "%s [invalid generalized_time]" % value + super(ASN1_GENERALIZED_TIME, self).__setattr__("pretty_time", pretty_time) + super(ASN1_GENERALIZED_TIME, self).__setattr__(name, value) + elif name == "pretty_time": + print "Invalid operation: pretty_time rewriting is not supported." + else: + super(ASN1_GENERALIZED_TIME, self).__setattr__(name, value) def __repr__(self): - return self.pretty_time + " " + ASN1_STRING.__repr__(self) + return "%s %s" % (self.pretty_time, ASN1_STRING.__repr__(self)) class ASN1_ISO646_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.ISO646_STRING diff --git a/scapy/asn1/ber.py b/scapy/asn1/ber.py index bfe74726e..005f91266 100644 --- a/scapy/asn1/ber.py +++ b/scapy/asn1/ber.py @@ -105,13 +105,15 @@ def BER_id_dec(s): # This returns the tag ALONG WITH THE PADDED CLASS+CONSTRUCTIVE INFO. # Let's recall that bits 8-7 from the first byte of the tag encode # the class information, while bit 6 means primitive or constructive. + # # For instance, with low-tag-number '\x81', class would be 0b10 # ('context-specific') and tag 0x01, but we return 0x81 as a whole. - # For '\xff\x02', class would be 0b11 ('private'), constructed, then - # padding, then tag 0x02, but we return (0xff>>5)*128^1 + 0x02*128^0. + # For '\xff\x22', class would be 0b11 ('private'), constructed, then + # padding, then tag 0x22, but we return (0xff>>5)*128^1 + 0x22*128^0. # Why the 5-bit-shifting? Because it provides an unequivocal encoding # on base 128 (note that 0xff would equal 1*128^1 + 127*128^0...), # as we know that bits 5 to 1 are fixed to 1 anyway. + # # As long as there is no class differentiation, we have to keep this info # encoded in scapy's tag in order to reuse it for packet building. # Note that tags thus may have to be hard-coded with their extended @@ -394,7 +396,7 @@ class BERcodec_ISO646_STRING(BERcodec_STRING): class BERcodec_UNIVERSAL_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING -class BERcodec_BMP_STRING (BERcodec_STRING): +class BERcodec_BMP_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.BMP_STRING class BERcodec_SEQUENCE(BERcodec_Object): diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py index 7c37d14f9..d368f4c0b 100644 --- a/scapy/asn1fields.py +++ b/scapy/asn1fields.py @@ -179,26 +179,16 @@ class ASN1F_enum_INTEGER(ASN1F_INTEGER): for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k - def any2i_one(self, pkt, x): - if type(x) is str: - x = self.s2i[x] - return x - def any2i(self, pkt, x): - if type(x) is list: - return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x) - else: - return self.any2i_one(pkt, x) - def i2repr_one(self, pkt, x): - if x is not None: - r = self.i2s.get(x) - if r: - return r + " " + repr(x) - return repr(x) + def i2m(self, pkt, s): + if isinstance(s, str): + s = self.s2i.get(s) + return super(ASN1F_INTEGER, self).i2m(pkt, s) def i2repr(self, pkt, x): - if type(x) is list: - return map(lambda z,pkt=pkt:self.i2repr_one(pkt, z), x) - else: - return self.i2repr_one(pkt, x) + if x is not None and isinstance(x, ASN1_INTEGER): + r = self.i2s.get(x.val) + if r: + return "'%s' %s" % (r, repr(x)) + return repr(x) class ASN1F_BIT_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING @@ -479,22 +469,23 @@ class ASN1F_CHOICE(ASN1F_field): else: choice = self.choices[tag] if hasattr(choice, "ASN1_root"): + # we don't want to import ASN1_Packet in this module... return self.extract_packet(choice, s) + elif type(choice) is type: + #XXX find a way not to instantiate the ASN1F_field + return choice(self.name, "").m2i(pkt, s) else: - if type(choice) is type: - return choice(self.name, "").m2i(pkt, s) - else: - # choice must be an ASN1F_PACKET instance here - return choice.m2i(pkt, s) + #XXX check properly if this is an ASN1F_PACKET + return choice.m2i(pkt, s) def i2m(self, pkt, x): if x is None: s = "" else: s = str(x) - if hash(type(x)) in self.pktchoices: - imp, exp = self.pktchoices[hash(type(x))] - s = BER_tagging_enc(s, implicit_tag=imp, - explicit_tag=exp) + if hash(type(x)) in self.pktchoices: + imp, exp = self.pktchoices[hash(type(x))] + s = BER_tagging_enc(s, implicit_tag=imp, + explicit_tag=exp) return BER_tagging_enc(s, explicit_tag=self.explicit_tag) def randval(self): return RandChoice(*(packet.fuzz(x()) for x in self.choices.itervalues()))