From cbbd04cdc36aa4874115062297850c8b7bf4ff31 Mon Sep 17 00:00:00 2001
From: mtu <maxence.tury@ssi.gouv.fr>
Date: Thu, 4 Feb 2016 17:10:57 +0100
Subject: [PATCH] Make ASN1F_CHOICE more simple and readable

---
 scapy/asn1fields.py | 43 ++++++++-----------------------------------
 test/x509.uts       |  2 +-
 2 files changed, 9 insertions(+), 36 deletions(-)

diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py
index 296ce0a7..bff04578 100644
--- a/scapy/asn1fields.py
+++ b/scapy/asn1fields.py
@@ -400,16 +400,8 @@ class ASN1F_optional(ASN1F_field):
 class ASN1F_CHOICE(ASN1F_field):
     """
     Multiple types are allowed: ASN1_Packet, ASN1F_field and ASN1F_PACKET(),
-    e.g. you could define ASN1F_CHOICE("qualifier", None,
-                                       X509_UserNotice,
-                                       ASN1F_X509_CPSuri)
-                       or ASN1F_CHOICE("qualifier", None,
-                                       ASN1F_PACKET("index", dflt, X509_Pkt,
-                                                    implicit_tag=0x82),
-                                       explicit_tag=0xa1)
+    See layers/x509.py for examples.
     Other ASN1F_field instances than ASN1F_PACKET instances must not be used.
-    ASN1F_PACKET instances must not be mixed with other types.
-    self.instantiated_choices signals whether only such instances are present.
     """
     holds_packets = 1
     ASN1_tag = ASN1_Class_UNIVERSAL.ANY
@@ -428,10 +420,9 @@ class ASN1F_CHOICE(ASN1F_field):
         self.default = default
         self.current_choice = None
         self.choices = {}
-        self.instantiated_choices = True
+        self.pktchoices = {}
         for p in args:
             if hasattr(p, "ASN1_root"):     # should be ASN1_Packet
-                self.instantiated_choices = False
                 if hasattr(p.ASN1_root, "choices"):
                     for k,v in p.ASN1_root.choices.items():
                         self.choices[k] = v         # ASN1F_CHOICE recursion
@@ -439,18 +430,16 @@ class ASN1F_CHOICE(ASN1F_field):
                     self.choices[p.ASN1_root.network_tag] = p
             elif hasattr(p, "ASN1_tag"):
                 if type(p) is type:         # should be ASN1F_field class
-                    self.instantiated_choices = False
                     self.choices[p.ASN1_tag] = p
                 else:                       # should be ASN1F_PACKET instance
                     self.choices[p.network_tag] = p
+                    self.pktchoices[hash(p.cls)] = (p.implicit_tag, p.explicit_tag)
             else:
                 raise ASN1_Error("ASN1F_CHOICE: no tag found for one field")
     def m2i(self, pkt, s):
         """
         First we have to retrieve the appropriate choice.
         Then we extract the field/packet, according to this choice.
-        In case the choice depends on implicit or explicit tags,
-        ASN1F_PACKET instance choices can be defined with the right tags.
         """
         if len(s) == 0:
             raise ASN1_Error("ASN1F_CHOICE: got empty string")
@@ -472,32 +461,16 @@ class ASN1F_CHOICE(ASN1F_field):
                 return choice(self.name, "").m2i(pkt, s)
             else:
                 # choice must be an ASN1F_PACKET instance here
-                c,s = choice.m2i(pkt, s)
-                # dirty hack here: when c is an ASN1_Packet, its __dict__
-                # is controlled so we hide tags inside overload_fields
-                hcls = hash(choice.cls)
-                c.overload_fields[hcls] = {}
-                if hasattr(choice, "implicit_tag"):
-                    c.overload_fields[hcls]["imp"] = choice.implicit_tag
-                else:
-                    c.overload_fields[hcls]["imp"] = None
-                if hasattr(choice, "explicit_tag"):
-                    c.overload_fields[hcls]["exp"] = choice.explicit_tag
-                else:
-                    c.overload_fields[hcls]["exp"] = None
-                return c,s
+                return choice.m2i(pkt, s)
     def i2m(self, pkt, x):
         if x is None:
             s = ""
         else:
             s = str(x)
-        if self.instantiated_choices:
-            cls = type(x)
-            if hash(cls) not in x.overload_fields:
-                raise ASN1_Error("ASN1F_CHOICE: could not encode object")
-            tags = x.overload_fields[hash(cls)]
-            s = BER_tagging_enc(s, implicit_tag=tags["imp"],
-                                explicit_tag=tags["exp"])
+        if hash(type(x)) in self.pktchoices.keys():
+            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(*map(lambda x:fuzz(x()), self.choice.values()))
diff --git a/test/x509.uts b/test/x509.uts
index 3c0cb430..3505c10d 100644
--- a/test/x509.uts
+++ b/test/x509.uts
@@ -1,4 +1,4 @@
-# X.509 unit tests
+% Tests for X.509 objects
 # 
 # Launch me with:
 # sudo bash test/run_tests -t test/x509.uts -F
-- 
GitLab