Skip to content
Snippets Groups Projects
Commit 09f669fe authored by mtu's avatar mtu
Browse files

Add implicit/explicit tagging support ; add high-tag number support ; add more...

Add implicit/explicit tagging support ; add high-tag number support ; add more proper BER bit string encoding/decoding
parent 2f0a559e
No related branches found
No related tags found
No related merge requests found
## This file is part of Scapy ## This file is part of Scapy
## See http://www.secdev.org/projects/scapy for more informations ## See http://www.secdev.org/projects/scapy for more informations
## Copyright (C) Philippe Biondi <phil@secdev.org> ## Copyright (C) Philippe Biondi <phil@secdev.org>
## Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
## Acknowledgment: Ralph Broenink
## This program is published under a GPLv2 license ## This program is published under a GPLv2 license
""" """
Basic Encoding Rules (BER) for ASN.1 Basic Encoding Rules (BER) for ASN1.1
""" """
from scapy.error import warning from scapy.error import warning
...@@ -87,8 +89,7 @@ def BER_num_enc(l, size=1): ...@@ -87,8 +89,7 @@ def BER_num_enc(l, size=1):
l >>= 7 l >>= 7
size -= 1 size -= 1
return "".join([chr(k) for k in x]) return "".join([chr(k) for k in x])
def BER_num_dec(s): def BER_num_dec(s, x=0):
x = 0
for i, c in enumerate(s): for i, c in enumerate(s):
c = ord(c) c = ord(c)
x <<= 7 x <<= 7
...@@ -99,6 +100,43 @@ def BER_num_dec(s): ...@@ -99,6 +100,43 @@ def BER_num_dec(s):
raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s)
return x, s[i+1:] return x, s[i+1:]
# The function below provides low-tag and high-tag identifier partial support.
# Class and primitive/constructed bit decoding is not supported yet.
# For now Scapy considers this information to always be part of the identifier
# e.g. we need BER_id_dec("\x30") to be 0x30 so that Scapy calls a SEQUENCE,
# even though the real id is 0x10 once bit 6 (constructed) has been removed.
def BER_id_dec(s):
x = ord(s[0])
if x & 0x1f != 0x1f:
return x,s[1:]
return BER_num_dec(s[1:], x&0xe0)
# The functions below provide implicit and explicit tagging support.
def BER_tagging_dec(s, hidden_tag=None, implicit_tag=None,
explicit_tag=None, safe=False):
if len(s) > 0:
err_msg = "BER_tagging_dec: observed tag does not match expected tag"
if implicit_tag is not None:
ber_id,s = BER_id_dec(s)
if ber_id != implicit_tag:
if not safe:
raise BER_Decoding_Error(err_msg, remaining=s)
s = chr(hidden_tag) + s
elif explicit_tag is not None:
ber_id,s = BER_id_dec(s)
if ber_id != explicit_tag:
if not safe:
raise BER_Decoding_Error(err_msg, remaining=s)
l,s = BER_len_dec(s)
return s
def BER_tagging_enc(s, implicit_tag=None, explicit_tag=None):
if len(s) > 0:
if implicit_tag is not None:
s = chr(implicit_tag) + s[1:]
elif explicit_tag is not None:
s = chr(explicit_tag) + BER_len_enc(len(s)) + s
return s
#####[ BER classes ]##### #####[ BER classes ]#####
class BERcodec_metaclass(type): class BERcodec_metaclass(type):
...@@ -128,10 +166,11 @@ class BERcodec_Object: ...@@ -128,10 +166,11 @@ class BERcodec_Object:
@classmethod @classmethod
def check_type(cls, s): def check_type(cls, s):
cls.check_string(s) cls.check_string(s)
if cls.tag != ord(s[0]): tag, remainder = BER_id_dec(s)
if cls.tag != tag:
raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" % raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" %
(cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s) (cls.__name__, tag, tag, cls.tag), remaining=s)
return s[1:] return remainder
@classmethod @classmethod
def check_type_get_len(cls, s): def check_type_get_len(cls, s):
s2 = cls.check_type(s) s2 = cls.check_type(s)
...@@ -152,7 +191,7 @@ class BERcodec_Object: ...@@ -152,7 +191,7 @@ class BERcodec_Object:
if context is None: if context is None:
context = cls.tag.context context = cls.tag.context
cls.check_string(s) cls.check_string(s)
p = ord(s[0]) p,_ = BER_id_dec(s)
if p not in context: if p not in context:
t = s t = s
if len(t) > 18: if len(t) > 18:
...@@ -187,11 +226,13 @@ class BERcodec_Object: ...@@ -187,11 +226,13 @@ class BERcodec_Object:
else: else:
return BERcodec_INTEGER.enc(int(s)) return BERcodec_INTEGER.enc(int(s))
ASN1_Codecs.BER.register_stem(BERcodec_Object) ASN1_Codecs.BER.register_stem(BERcodec_Object)
##########################
#### BERcodec objects ####
##########################
class BERcodec_INTEGER(BERcodec_Object): class BERcodec_INTEGER(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.INTEGER tag = ASN1_Class_UNIVERSAL.INTEGER
@classmethod @classmethod
...@@ -223,12 +264,46 @@ class BERcodec_INTEGER(BERcodec_Object): ...@@ -223,12 +264,46 @@ class BERcodec_INTEGER(BERcodec_Object):
x |= ord(c) x |= ord(c)
return cls.asn1_object(x),t return cls.asn1_object(x),t
class BERcodec_BOOLEAN(BERcodec_INTEGER): class BERcodec_BOOLEAN(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.BOOLEAN tag = ASN1_Class_UNIVERSAL.BOOLEAN
class BERcodec_ENUMERATED(BERcodec_INTEGER): class BERcodec_BIT_STRING(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.ENUMERATED tag = ASN1_Class_UNIVERSAL.BIT_STRING
@classmethod
def do_dec(cls, s, context=None, safe=False):
# /!\ the unused_bits information is lost after this decoding
l,s,t = cls.check_type_check_len(s)
if len(s) > 0:
unused_bits = ord(s[0])
if safe and unused_bits > 7:
raise BER_Decoding_Error("BERcodec_BIT_STRING: too many unused_bits advertised", remaining=s)
s = "".join(format(ord(x), 'b').zfill(8) for x in s[1:])
if unused_bits > 0:
s = s[:-unused_bits]
return cls.tag.asn1_object(s),t
else:
raise BER_Decoding_Error("BERcodec_BIT_STRING found no content (not even unused_bits byte)", remaining=s)
@classmethod
def enc(cls,s):
# /!\ this is DER encoding (bit strings are only zero-bit padded)
if len(s) % 8 == 0:
unused_bits = 0
else:
unused_bits = 8 - len(s)%8
s += "0"*unused_bits
s = "".join(chr(int("".join(x),2)) for x in zip(*[iter(s)]*8))
s = chr(unused_bits) + s
return chr(cls.tag)+BER_len_enc(len(s))+s
class BERcodec_STRING(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.STRING
@classmethod
def enc(cls,s):
return chr(cls.tag)+BER_len_enc(len(s))+s
@classmethod
def do_dec(cls, s, context=None, safe=False):
l,s,t = cls.check_type_check_len(s)
return cls.tag.asn1_object(s),t
class BERcodec_NULL(BERcodec_INTEGER): class BERcodec_NULL(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.NULL tag = ASN1_Class_UNIVERSAL.NULL
...@@ -237,23 +312,35 @@ class BERcodec_NULL(BERcodec_INTEGER): ...@@ -237,23 +312,35 @@ class BERcodec_NULL(BERcodec_INTEGER):
if i == 0: if i == 0:
return chr(cls.tag)+"\0" return chr(cls.tag)+"\0"
else: else:
return BERcodec_INTEGER.enc(i) return super(cls,cls).enc(i)
class BERcodec_SEP(BERcodec_NULL):
tag = ASN1_Class_UNIVERSAL.SEP
class BERcodec_STRING(BERcodec_Object): class BERcodec_OID(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.STRING tag = ASN1_Class_UNIVERSAL.OID
@classmethod @classmethod
def enc(cls,s): def enc(cls, oid):
lst = [int(x) for x in oid.strip(".").split(".")]
if len(lst) >= 2:
lst[1] += 40*lst[0]
del(lst[0])
s = "".join([BER_num_enc(k) for k in lst])
return chr(cls.tag)+BER_len_enc(len(s))+s return chr(cls.tag)+BER_len_enc(len(s))+s
@classmethod @classmethod
def do_dec(cls, s, context=None, safe=False): def do_dec(cls, s, context=None, safe=False):
l,s,t = cls.check_type_check_len(s) l,s,t = cls.check_type_check_len(s)
return cls.tag.asn1_object(s),t lst = []
while s:
l,s = BER_num_dec(s)
lst.append(l)
if (len(lst) > 0):
lst.insert(0,lst[0]/40)
lst[1] %= 40
return cls.asn1_object(".".join([str(k) for k in lst])), t
class BERcodec_BIT_STRING(BERcodec_STRING): class BERcodec_ENUMERATED(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.BIT_STRING tag = ASN1_Class_UNIVERSAL.ENUMERATED
class BERcodec_UTF8_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.UTF8_STRING
class BERcodec_PRINTABLE_STRING(BERcodec_STRING): class BERcodec_PRINTABLE_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
...@@ -264,49 +351,20 @@ class BERcodec_T61_STRING (BERcodec_STRING): ...@@ -264,49 +351,20 @@ class BERcodec_T61_STRING (BERcodec_STRING):
class BERcodec_IA5_STRING(BERcodec_STRING): class BERcodec_IA5_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.IA5_STRING tag = ASN1_Class_UNIVERSAL.IA5_STRING
class BERcodec_NUMERIC_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
class BERcodec_VIDEOTEX_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
class BERcodec_IPADDRESS(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.IPADDRESS
@classmethod
def enc(cls, ipaddr_ascii):
try:
s = inet_aton(ipaddr_ascii)
except Exception:
raise BER_Encoding_Error("IPv4 address could not be encoded")
return chr(cls.tag)+BER_len_enc(len(s))+s
@classmethod
def do_dec(cls, s, context=None, safe=False):
l,s,t = cls.check_type_check_len(s)
try:
ipaddr_ascii = inet_ntoa(s)
except Exception:
raise BER_Decoding_Error("IP address could not be decoded", decoded=obj)
return cls.asn1_object(ipaddr_ascii), t
class BERcodec_UTC_TIME(BERcodec_STRING): class BERcodec_UTC_TIME(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.UTC_TIME tag = ASN1_Class_UNIVERSAL.UTC_TIME
class BERcodec_GENERALIZED_TIME(BERcodec_STRING): class BERcodec_GENERALIZED_TIME(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
class BERcodec_TIME_TICKS(BERcodec_INTEGER): class BERcodec_ISO646_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.TIME_TICKS tag = ASN1_Class_UNIVERSAL.ISO646_STRING
class BERcodec_GAUGE32(BERcodec_INTEGER): class BERcodec_UNIVERSAL_STRING(BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.GAUGE32 tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
class BERcodec_COUNTER32(BERcodec_INTEGER): class BERcodec_BMP_STRING (BERcodec_STRING):
tag = ASN1_Class_UNIVERSAL.COUNTER32 tag = ASN1_Class_UNIVERSAL.BMP_STRING
class BERcodec_COUNTER64(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.COUNTER64
class BERcodec_SEQUENCE(BERcodec_Object): class BERcodec_SEQUENCE(BERcodec_Object):
tag = ASN1_Class_UNIVERSAL.SEQUENCE tag = ASN1_Class_UNIVERSAL.SEQUENCE
...@@ -339,28 +397,29 @@ class BERcodec_SEQUENCE(BERcodec_Object): ...@@ -339,28 +397,29 @@ class BERcodec_SEQUENCE(BERcodec_Object):
class BERcodec_SET(BERcodec_SEQUENCE): class BERcodec_SET(BERcodec_SEQUENCE):
tag = ASN1_Class_UNIVERSAL.SET tag = ASN1_Class_UNIVERSAL.SET
class BERcodec_IPADDRESS(BERcodec_STRING):
class BERcodec_OID(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.IPADDRESS
tag = ASN1_Class_UNIVERSAL.OID
@classmethod @classmethod
def enc(cls, oid): def enc(cls, ipaddr_ascii):
lst = [int(x) for x in oid.strip(".").split(".")] try:
if len(lst) >= 2: s = inet_aton(ipaddr_ascii)
lst[1] += 40*lst[0] except Exception:
del(lst[0]) raise BER_Encoding_Error("IPv4 address could not be encoded")
s = "".join([BER_num_enc(k) for k in lst])
return chr(cls.tag)+BER_len_enc(len(s))+s return chr(cls.tag)+BER_len_enc(len(s))+s
@classmethod @classmethod
def do_dec(cls, s, context=None, safe=False): def do_dec(cls, s, context=None, safe=False):
l,s,t = cls.check_type_check_len(s) l,s,t = cls.check_type_check_len(s)
lst = [] try:
while s: ipaddr_ascii = inet_ntoa(s)
l,s = BER_num_dec(s) except Exception:
lst.append(l) raise BER_Decoding_Error("IP address could not be decoded", decoded=obj)
if (len(lst) > 0): return cls.asn1_object(ipaddr_ascii), t
lst.insert(0,lst[0]/40)
lst[1] %= 40 class BERcodec_COUNTER32(BERcodec_INTEGER):
return cls.asn1_object(".".join([str(k) for k in lst])), t tag = ASN1_Class_UNIVERSAL.COUNTER32
class BERcodec_TIME_TICKS(BERcodec_INTEGER):
tag = ASN1_Class_UNIVERSAL.TIME_TICKS
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment