55"""
66OER interoperability helpers.
77
8- Cross-check Scapy's OER codec against asn1tools when available.
9- asn1tools is an optional test dependency (pip install asn1tools).
8+ Cross-check Scapy's OER codec against reference encodings (from asn1tools).
109Reference vectors are taken from asn1tools/tests/test_oer.py.
1110"""
1211
13- from typing import Any , Callable , List , Optional , Tuple
14-
15- try :
16- import asn1tools
17- HAS_ASN1TOOLS = True
18- except ImportError :
19- asn1tools = None # type: ignore
20- HAS_ASN1TOOLS = False
21-
2212from scapy .asn1 .oer import (
2313 OERcodec_BIT_STRING ,
2414 OERcodec_BOOLEAN ,
2717 OERcodec_NULL ,
2818 OERcodec_OID ,
2919 OERcodec_STRING ,
30- OER_len_dec ,
31- OER_len_enc ,
32- OER_signed_integer_dec ,
3320 OER_signed_integer_enc ,
34- OER_unsigned_integer_dec ,
3521 OER_unsigned_integer_enc ,
3622)
3723
38-
39- ASN1TOOLS_INTEGER_SPEC = (
40- "Foo DEFINITIONS AUTOMATIC TAGS ::= "
41- "BEGIN "
42- "A ::= INTEGER "
43- "B ::= INTEGER (-128..127) "
44- "C ::= INTEGER (-32768..32767) "
45- "D ::= INTEGER (-2147483648..2147483647) "
46- "E ::= INTEGER (-9223372036854775808..9223372036854775807) "
47- "F ::= INTEGER (0..255) "
48- "G ::= INTEGER (0..65535) "
49- "H ::= INTEGER (0..4294967295) "
50- "I ::= INTEGER (0..18446744073709551615) "
51- "J ::= INTEGER (0..18446744073709551616) "
52- "K ::= INTEGER (1..MAX) "
53- "L ::= INTEGER (MIN..0) "
54- "END"
55- )
56-
57- # (asn1tools type, value, scapy encoder callable)
24+ # (type name, value, scapy encoder callable, reference encoding)
5825INTEGER_VECTORS = [
59- ("A" , 0 , lambda v : OERcodec_INTEGER .enc (v )),
60- ("A" , 128 , lambda v : OERcodec_INTEGER .enc (v )),
61- ("A" , 100000 , lambda v : OERcodec_INTEGER .enc (v )),
62- ("A" , - 255 , lambda v : OERcodec_INTEGER .enc (v )),
63- ("A" , - 1234567 , lambda v : OERcodec_INTEGER .enc (v )),
64- ("B" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 1 )),
65- ("C" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 2 )),
66- ("D" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 4 )),
67- ("E" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 8 )),
68- ("F" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 1 )),
69- ("G" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 2 )),
70- ("G" , 1000 , lambda v : OERcodec_INTEGER .enc (v , size_len = 2 )),
71- ("H" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 4 )),
72- ("I" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 8 )),
73- ("B" , 1 , lambda v : OERcodec_INTEGER .enc (v , size_len = 1 )),
74- ("K" , 1 , lambda v : OER_unsigned_integer_enc (v )),
75- ("K" , 128 , lambda v : OER_unsigned_integer_enc (v )),
76- ("L" , - 128 , lambda v : OER_signed_integer_enc (v )),
26+ ("A" , 0 , lambda v : OERcodec_INTEGER .enc (v ), b"\x01 \x00 " ),
27+ ("A" , 128 , lambda v : OERcodec_INTEGER .enc (v ), b"\x02 \x00 \x80 " ),
28+ ("A" , 100000 , lambda v : OERcodec_INTEGER .enc (v ), b"\x03 \x01 \x86 \xa0 " ),
29+ ("A" , - 255 , lambda v : OERcodec_INTEGER .enc (v ), b"\x02 \xff \x01 " ),
30+ ("A" , - 1234567 , lambda v : OERcodec_INTEGER .enc (v ), b"\x03 \xed )y" ),
31+ ("B" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 1 ), b"\xfe " ),
32+ ("C" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 2 ), b"\xff \xfe " ),
33+ ("D" , - 2 , lambda v : OERcodec_INTEGER .enc (v , size_len = 4 ), b"\xff \xff \xff \xfe " ),
34+ (
35+ "E" ,
36+ - 2 ,
37+ lambda v : OERcodec_INTEGER .enc (v , size_len = 8 ),
38+ b"\xff \xff \xff \xff \xff \xff \xff \xfe " ,
39+ ),
40+ ("F" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 1 ), b"\x80 " ),
41+ ("G" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 2 ), b"\x00 \x80 " ),
42+ ("G" , 1000 , lambda v : OERcodec_INTEGER .enc (v , size_len = 2 ), b"\x03 \xe8 " ),
43+ ("H" , 128 , lambda v : OERcodec_INTEGER .enc (v , size_len = 4 ), b"\x00 \x00 \x00 \x80 " ),
44+ (
45+ "I" ,
46+ 128 ,
47+ lambda v : OERcodec_INTEGER .enc (v , size_len = 8 ),
48+ b"\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x80 " ,
49+ ),
50+ ("B" , 1 , lambda v : OERcodec_INTEGER .enc (v , size_len = 1 ), b"\x01 " ),
51+ ("K" , 1 , lambda v : OER_unsigned_integer_enc (v ), b"\x01 \x01 " ),
52+ ("K" , 128 , lambda v : OER_unsigned_integer_enc (v ), b"\x01 \x80 " ),
53+ ("L" , - 128 , lambda v : OER_signed_integer_enc (v ), b"\x01 \x80 " ),
7754]
7855
7956BOOLEAN_VECTORS = [
80- (True , lambda v : OERcodec_BOOLEAN .enc (1 if v else 0 )),
81- (False , lambda v : OERcodec_BOOLEAN .enc (1 if v else 0 )),
57+ (True , lambda v : OERcodec_BOOLEAN .enc (1 if v else 0 ), b" \xff " ),
58+ (False , lambda v : OERcodec_BOOLEAN .enc (1 if v else 0 ), b" \x00 " ),
8259]
8360
8461ENUMERATED_VECTORS = [
85- ("A ::= ENUMERATED { a(1) } " , "A" , " a" , 1 ),
86- ("B ::= ENUMERATED { a(128) } " , "B" , " a" , 128 ),
87- ("C ::= ENUMERATED { a(0), b(127) } " , "C" , " a" , 0 ),
88- ("C ::= ENUMERATED { a(0), b(127) } " , "C" , "b" , 127 ),
89- ("E ::= ENUMERATED { a(-1), b(1234) } " , "E" , " a" , - 1 ),
62+ ("A" , "a" , 1 , b" \x01 " ),
63+ ("B" , "a" , 128 , b" \x82 \x00 \x80 " ),
64+ ("C" , "a" , 0 , b" \x00 " ),
65+ ("C" , "b " , 127 , b" \x7f " ),
66+ ("E" , "a" , - 1 , b" \x81 \xff " ),
9067]
9168
9269OID_VECTORS = [
93- ("1.2" , lambda v : OERcodec_OID .enc (v )),
94- ("1.2.3321" , lambda v : OERcodec_OID .enc (v )),
70+ ("1.2" , lambda v : OERcodec_OID .enc (v ), b" \x01 *" ),
71+ ("1.2.3321" , lambda v : OERcodec_OID .enc (v ), b" \x03 * \x99 y" ),
9572]
9673
9774OCTET_STRING_VECTORS = [
98- (b"\x12 \x34 " , 0 ),
99- (b"\x12 \x34 \x56 " , 3 ),
75+ (b"\x12 \x34 " , 0 , b" \x02 \x12 4" ),
76+ (b"\x12 \x34 \x56 " , 3 , b" \x12 4V" ),
10077]
10178
10279BIT_STRING_VECTORS = [
103- # (asn1 value, scapy bit string)
104- ((b"\x40 " , 4 ), "0100" ),
105- ((b"\x41 " , 8 ), "01000001" ),
80+ ("0100" , b"\x02 \x04 @" ),
81+ ("01000001" , b"\x02 \x00 A" ),
10682]
10783
108-
109- def require_asn1tools ():
110- # type: () -> bool
111- """Return True if asn1tools is available for interoperability tests."""
112- return HAS_ASN1TOOLS
113-
114-
115- def _compile (spec_body ):
116- # type: (str) -> Any
117- assert asn1tools is not None
118- spec = "Foo DEFINITIONS AUTOMATIC TAGS ::= BEGIN %s END" % spec_body
119- return asn1tools .compile_string (spec , "oer" )
120-
121-
122- def _assert_encode_match (spec_body , type_name , value , scapy_enc ):
123- # type: (str, str, Any, Callable[..., bytes]) -> None
124- compiled = _compile (spec_body )
125- expected = compiled .encode (type_name , value )
126- got = scapy_enc (value )
127- assert got == expected , (
128- "OER encode mismatch for %s=%r: asn1tools=%r scapy=%r" %
129- (type_name , value , expected , got )
130- )
131-
132-
133- def _assert_decode_match (type_name , encoded , scapy_dec , expected_value ):
134- # type: (str, bytes, Callable[[bytes], Tuple[Any, bytes]], Any) -> None
135- obj , remain = scapy_dec (encoded )
136- assert remain == b"" , "unexpected remainder after decode of %s" % type_name
137- assert obj .val == expected_value , (
138- "OER decode mismatch for %s: got %r expected %r" %
139- (type_name , obj .val , expected_value )
140- )
84+ # (type name, value, reference encoding)
85+ SCAPY_DECODE_VECTORS = [
86+ ("A" , 42 , b"\x01 *" ),
87+ ("F" , 200 , b"\xc8 " ),
88+ ("B" , - 99 , b"\x9d " ),
89+ ]
14190
14291
14392def check_primitive_interop ():
14493 # type: () -> bool
145- """Compare Scapy OER primitives against asn1tools. Returns True on success."""
146- if not HAS_ASN1TOOLS :
147- return True
148-
149- compiled = asn1tools .compile_string (ASN1TOOLS_INTEGER_SPEC , "oer" )
150- for type_name , value , enc in INTEGER_VECTORS :
151- expected = compiled .encode (type_name , value )
94+ """Compare Scapy OER primitives against reference encodings."""
95+ for type_name , value , enc , expected in INTEGER_VECTORS :
15296 got = enc (value )
15397 assert got == expected , (
154- "integer %s=%r: asn1tools =%r scapy=%r" %
98+ "integer %s=%r: reference =%r scapy=%r" %
15599 (type_name , value , expected , got )
156100 )
157101 if type_name == "A" :
158102 dec , remain = OERcodec_INTEGER .do_dec (got )
159103 assert remain == b"" and dec .val == value
160104
161- bool_spec = _compile ("A ::= BOOLEAN" )
162- for value , enc in BOOLEAN_VECTORS :
163- expected = bool_spec .encode ("A" , value )
105+ for value , enc , expected in BOOLEAN_VECTORS :
164106 got = enc (value )
165107 assert got == expected
166108 dec , remain = OERcodec_BOOLEAN .do_dec (got )
167109 assert remain == b"" and dec .val == (1 if value else 0 )
168110
169- _assert_encode_match ("A ::= NULL" , "A" , None , lambda _ : OERcodec_NULL .enc (None ))
111+ got = OERcodec_NULL .enc (None )
112+ assert got == b""
170113
171- for spec_body , type_name , enum_name , enum_val in ENUMERATED_VECTORS :
172- compiled = _compile (spec_body )
173- expected = compiled .encode (type_name , enum_name )
114+ for type_name , _enum_name , enum_val , expected in ENUMERATED_VECTORS :
174115 got = OERcodec_ENUMERATED .enc (enum_val )
175116 assert got == expected
176117 dec , remain = OERcodec_ENUMERATED .do_dec (got )
177118 assert remain == b"" and dec .val == enum_val
178119
179- oid_spec = _compile ("A ::= OBJECT IDENTIFIER" )
180- for oid , enc in OID_VECTORS :
181- expected = oid_spec .encode ("A" , oid )
120+ for oid , enc , expected in OID_VECTORS :
182121 got = enc (oid )
183122 assert got == expected
184123 dec , remain = OERcodec_OID .do_dec (got )
185124 assert remain == b"" and dec .val == oid
186125
187- octet_spec = _compile (
188- "A ::= OCTET STRING\n B ::= OCTET STRING (SIZE (3))"
189- )
190- for data , fixed_size in OCTET_STRING_VECTORS :
191- type_name = "B" if fixed_size else "A"
192- expected = octet_spec .encode (type_name , data )
126+ for data , fixed_size , expected in OCTET_STRING_VECTORS :
193127 got = OERcodec_STRING .enc (data , size_len = fixed_size or 0 )
194128 assert got == expected
195129 dec , remain = OERcodec_STRING .do_dec (got , size_len = fixed_size or 0 )
196130 assert remain == b"" and dec .val == data
197131
198- bit_spec = _compile ("A ::= BIT STRING" )
199- for (data , nbits ), bitstr in BIT_STRING_VECTORS :
200- expected = bit_spec .encode ("A" , (data , nbits ))
132+ for bitstr , expected in BIT_STRING_VECTORS :
201133 got = OERcodec_BIT_STRING .enc (bitstr )
202134 assert got == expected
203135 dec , remain = OERcodec_BIT_STRING .do_dec (got )
@@ -206,26 +138,23 @@ def check_primitive_interop():
206138 return True
207139
208140
209- def check_asn1tools_decode_scapy_encode ():
141+ def check_scapy_encode_reference_decode ():
210142 # type: () -> bool
211- """Encode with Scapy, decode with asn1tools."""
212- if not HAS_ASN1TOOLS :
213- return True
214-
215- compiled = asn1tools .compile_string (ASN1TOOLS_INTEGER_SPEC , "oer" )
216- samples = [("A" , 42 ), ("F" , 200 ), ("B" , - 99 )]
217- for type_name , value in samples :
218- encoded = {
219- "A" : OERcodec_INTEGER .enc ,
220- "F" : lambda v : OERcodec_INTEGER .enc (v , size_len = 1 ),
221- "B" : lambda v : OERcodec_INTEGER .enc (v , size_len = 1 ),
222- }[type_name ](value )
223- decoded = compiled .decode (type_name , encoded )
224- assert decoded == value
225-
226- bool_spec = _compile ("A ::= BOOLEAN" )
143+ """Decode reference encodings with Scapy."""
144+ for type_name , value , encoded in SCAPY_DECODE_VECTORS :
145+ if type_name == "A" :
146+ dec , remain = OERcodec_INTEGER .do_dec (encoded )
147+ elif type_name == "F" :
148+ dec , remain = OERcodec_INTEGER .do_dec (
149+ encoded , size_len = 1 , oer_unsigned = True ,
150+ )
151+ else :
152+ dec , remain = OERcodec_INTEGER .do_dec (encoded , size_len = 1 )
153+ assert remain == b"" and dec .val == value
154+
227155 for val in [0 , 1 ]:
228156 encoded = OERcodec_BOOLEAN .enc (val )
229- assert bool_spec .decode ("A" , encoded ) == bool (val )
157+ dec , remain = OERcodec_BOOLEAN .do_dec (encoded )
158+ assert remain == b"" and dec .val == val
230159
231160 return True
0 commit comments