ASN.1 enhancements

This commit is contained in:
mtu 2016-10-28 17:33:02 +02:00
parent 870770bc0f
commit 36bbb8ff44
3 changed files with 92 additions and 56 deletions

View File

@ -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 = "<invalid val_readable>"
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 = "<invalid val>"
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

View File

@ -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):

View File

@ -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()))