asn1: decode BER integers with int.from_bytes - #5107
Conversation
AI-Assisted: yes (GPT-5.6-Cyber)
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5107 +/- ##
===========================================
- Coverage 80.59% 47.48% -33.12%
===========================================
Files 390 372 -18
Lines 96892 96539 -353
===========================================
- Hits 78094 45844 -32250
- Misses 18798 50695 +31897
🚀 New features to boost your workflow:
|
|
Correct me if I'm wrong, but while a negative value properly doesn't make sense, it's still whats in the spec: https://www.rfc-editor.org/info/rfc4120/#section-5.2.9. I'm not sure I understand the issue otherwise |
|
You're right that negative values are legal, and the patch keeps them — it never looks at the value. To be precise about what it is: this is a resource bound, not validation of The reason for it is cost.
If a loose bound in a Kerberos-specific field subclass isn't worth it, the reasonable alternatives are |
|
|
Move the fix from Kerberos into the BER codec, as reviewed. BERcodec_INTEGER.do_dec shifted a growing Python integer one octet at a time, so the cost of each octet rose with the number already accumulated and decoding was quadratic in the encoded width. A sender could multiply parsing cost by padding any INTEGER with leading sign octets, in any protocol that uses BER, not only in a Kerberos etype. Decoding 64,000 content octets took 304 ms; int.from_bytes does the same two's-complement conversion in one pass, in 0.03 ms, and agrees with the old loop on every input tested. This drops the Kerberos-specific Int32 width check the first version of this pull request added. Nothing is rejected now that was accepted before: the field is parsed, only more cheaply. The regression test counts that the conversion happens once rather than per octet, the way the BitLenField test added in secdev#5108 does. AI-Assisted: yes (GPT-5.6-Cyber)
|
You were right, and I have moved it. The cost is in So this version leaves On your earlier point about Two things worth saying plainly:
The new test counts that the conversion happens once rather than once per octet, in the same shape as the |
| # Convert the content octets in one go. Shifting a growing Python | ||
| # integer one octet at a time costs more with every octet already | ||
| # accumulated, so decoding was quadratic in the encoded width: a sender | ||
| # could multiply parsing cost by padding any INTEGER with leading sign | ||
| # octets, in any protocol that uses BER. int.from_bytes performs the | ||
| # same two's-complement conversion in a single pass. |
There was a problem hiding this comment.
We don't need the massive comment, otherwise OK.
There was a problem hiding this comment.
Trimmed to one line in 6d8635c.
|
One note on the red
The job fetches three commits deep, so it reaches Nothing for me to change here, but you may want to amend that commit or have the script skip commits already on the base branch. |
AI-Assisted: yes (GPT-5.6-Cyber)
BERcodec_INTEGER.do_decbuilds the value by shifting a Python integer left one octet at a time atscapy/asn1/ber.py:464-471.Each shift copies the whole accumulated integer, so the cost of an octet rises with the number of
octets already read and decoding is quadratic in the encoded width.
Nothing in BER limits that width, so any protocol using it can be made to spend the time. A sender
pads an INTEGER with leading sign octets, which do not change the value, and the parser does the
work anyway. Kerberos
EncryptedData.etypeis one way in; it is not the only one.Measured on one machine, decoding one INTEGER:
Quadratic before, linear after.
int.from_bytes(s, "big", signed=True)performs the same two's-complement conversion in one pass.The two agree on every input tested, including the empty string, single octets on both sides of the
sign boundary, and 20,000 random byte strings.
This replaces the first version of this pull request, which added an
Int32width check toKerberos. @gpotter2 was right that the problem is generic rather than Kerberos-specific, so
scapy/layers/kerberos.pyis untouched now. Nothing is rejected that was accepted before — wideintegers still parse, just cheaply — which also means certificates and other legitimately large
INTEGER values are unaffected.
The regression test counts that the conversion happens once rather than once per octet, following
the
BitLenFieldtest added in #5108. It fails on the unmodified revision.