Skip to content

Commit 239f2e4

Browse files
author
Nils Weiss
committed
Remove asn1tools test dependency in favor of hardcoded reference vectors.
AI-Assisted: yes (Cursor AI)
1 parent b1bbcf6 commit 239f2e4

9 files changed

Lines changed: 217 additions & 448 deletions

File tree

.config/ci/install.ps1

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,3 @@ python -m pip install --upgrade pip setuptools wheel --ignore-installed
1919

2020
# Make sure tox is installed and up to date
2121
python -m pip install -U tox --ignore-installed
22-
23-
# Optional test dependency for ASN.1 OER interoperability tests
24-
python -m pip install asn1tools

.config/ci/install.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,5 @@ python -m pip install --upgrade pip setuptools wheel --ignore-installed
5353
# Make sure tox is installed and up to date
5454
python -m pip install -U tox --ignore-installed
5555

56-
# Optional test dependency for ASN.1 OER interoperability tests
57-
python -m pip install asn1tools
58-
5956
# Dump Environment (so that we can check PATH, UT_FLAGS, etc.)
6057
set

test/scapy/layers/asn1.uts

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,11 @@ ASN1_BOOLEAN(1).enc(ASN1_Codecs.OER) == b"\xff"
207207
= OER ASN1 null object
208208
ASN1_NULL(None).enc(ASN1_Codecs.OER) == b""
209209

210-
+ ASN.1 OER interoperability (asn1tools)
211-
= asn1tools availability probe
212-
from test.scapy.layers.oer_iop import require_asn1tools
213-
HAS_ASN1TOOLS_OER = require_asn1tools()
214-
= primitive encode interop with asn1tools
215-
(not HAS_ASN1TOOLS_OER) or __import__('test.scapy.layers.oer_iop', fromlist=['check_primitive_interop']).check_primitive_interop()
216-
= scapy encode asn1tools decode
217-
(not HAS_ASN1TOOLS_OER) or __import__('test.scapy.layers.oer_iop', fromlist=['check_asn1tools_decode_scapy_encode']).check_asn1tools_decode_scapy_encode()
210+
+ ASN.1 OER interoperability (reference vectors)
211+
= primitive encode interop
212+
__import__('test.scapy.layers.oer_iop', fromlist=['check_primitive_interop']).check_primitive_interop()
213+
= scapy encode reference decode
214+
__import__('test.scapy.layers.oer_iop', fromlist=['check_scapy_encode_reference_decode']).check_scapy_encode_reference_decode()
218215

219216
+ ASN.1 OER review fixes
220217
= OER fixed integer decode roundtrip
@@ -342,19 +339,16 @@ x.val == 1 and r == b""
342339
ASN1_INTEGER(42).enc(ASN1_Codecs.PER) == b"\x01*"
343340
= UPER codec registration
344341
ASN1_Class_UNIVERSAL.INTEGER.get_codec(ASN1_Codecs.PER) is UPERcodec_INTEGER
345-
= asn1tools availability probe for UPER
346-
from test.scapy.layers.uper_iop import require_asn1tools
347-
HAS_ASN1TOOLS_UPER = require_asn1tools()
348342

349343
+ ASN.1 UPER codec roundtrips
350344
= UPER codec primitive roundtrips
351345
__import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_roundtrips']).check_uper_codec_roundtrips()
352-
= UPER codec asn1tools decode interop
353-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_asn1tools_decode']).check_uper_codec_asn1tools_decode()
354-
= UPER codec scapy encode asn1tools decode
355-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_scapy_decode_asn1tools']).check_uper_codec_scapy_decode_asn1tools()
346+
= UPER codec reference decode interop
347+
__import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_reference_decode']).check_uper_codec_reference_decode()
348+
= UPER codec scapy encode reference interop
349+
__import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_encode_reference']).check_uper_codec_encode_reference()
356350
= UPER codec OID encode interop
357-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_oid_encode_interop']).check_uper_codec_oid_encode_interop()
351+
__import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_oid_encode_interop']).check_uper_codec_oid_encode_interop()
358352
= UPER codec OID roundtrip
359353
__import__('test.scapy.layers.uper_codec', fromlist=['check_uper_codec_oid_roundtrip']).check_uper_codec_oid_roundtrip()
360354

@@ -380,25 +374,23 @@ __import__('test.scapy.layers.uper_helpers', fromlist=['check_uper_join_encoding
380374
= UPER chained encode into
381375
__import__('test.scapy.layers.uper_helpers', fromlist=['check_uper_chained_encode_into']).check_uper_chained_encode_into()
382376

383-
+ ASN.1 UPER interoperability (asn1tools)
384-
= UPER primitive encode interop with asn1tools
385-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_iop', fromlist=['check_primitive_interop']).check_primitive_interop()
386-
= UPER composite encode interop with asn1tools
387-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_iop', fromlist=['check_composite_interop']).check_composite_interop()
388-
= UPER composite decode interop with asn1tools
389-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_iop', fromlist=['check_composite_decode_interop']).check_composite_decode_interop()
390-
= UPER packet asn1tools interop
391-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_iop', fromlist=['check_packet_asn1tools_interop']).check_packet_asn1tools_interop()
377+
+ ASN.1 UPER interoperability (reference vectors)
378+
= UPER primitive encode interop
379+
__import__('test.scapy.layers.uper_iop', fromlist=['check_primitive_interop']).check_primitive_interop()
380+
= UPER composite encode interop
381+
__import__('test.scapy.layers.uper_iop', fromlist=['check_composite_interop']).check_composite_interop()
382+
= UPER packet reference interop
383+
__import__('test.scapy.layers.uper_iop', fromlist=['check_packet_reference_interop']).check_packet_reference_interop()
392384
= UPER packet decode vectors
393385
__import__('test.scapy.layers.uper_iop', fromlist=['check_packet_decode_vectors']).check_packet_decode_vectors()
394386

395387
+ ASN.1 UPER asn1scc interoperability
396-
= asn1scc vector encode interop with asn1tools
397-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_asn1scc_iop', fromlist=['check_asn1scc_vectors']).check_asn1scc_vectors()
398-
= asn1scc README Message uPER reference (asn1tools)
399-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_asn1scc_iop', fromlist=['check_asn1scc_readme_message_reference']).check_asn1scc_readme_message_reference()
388+
= asn1scc vector encode interop
389+
__import__('test.scapy.layers.uper_asn1scc_iop', fromlist=['check_asn1scc_vectors']).check_asn1scc_vectors()
390+
= asn1scc README Message uPER reference
391+
__import__('test.scapy.layers.uper_asn1scc_iop', fromlist=['check_asn1scc_readme_message_reference']).check_asn1scc_readme_message_reference()
400392
= asn1scc README MessagePrefix Scapy interop
401-
(not HAS_ASN1TOOLS_UPER) or __import__('test.scapy.layers.uper_asn1scc_iop', fromlist=['check_asn1scc_readme_message_prefix']).check_asn1scc_readme_message_prefix()
393+
__import__('test.scapy.layers.uper_asn1scc_iop', fromlist=['check_asn1scc_readme_message_prefix']).check_asn1scc_readme_message_prefix()
402394

403395
+ ASN.1 UPER packets and fields
404396
= UPER field fixed size

test/scapy/layers/oer_iop.py

Lines changed: 74 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,10 @@
55
"""
66
OER 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).
109
Reference 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-
2212
from scapy.asn1.oer import (
2313
OERcodec_BIT_STRING,
2414
OERcodec_BOOLEAN,
@@ -27,177 +17,119 @@
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)
5825
INTEGER_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

7956
BOOLEAN_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

8461
ENUMERATED_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

9269
OID_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*\x99y"),
9572
]
9673

9774
OCTET_STRING_VECTORS = [
98-
(b"\x12\x34", 0),
99-
(b"\x12\x34\x56", 3),
75+
(b"\x12\x34", 0, b"\x02\x124"),
76+
(b"\x12\x34\x56", 3, b"\x124V"),
10077
]
10178

10279
BIT_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\x00A"),
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

14392
def 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\nB ::= 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

Comments
 (0)