Skip to content

PER: a FROM constraint on UTF8String (or another non-known-multiplier string) raises TypeError on encode and decode #104

Closed

Description

@gomaja

Versions: pycrate 0.7.11 from PyPI, and master at 82772f76c3feb22fd0257d4d0575f56dedd104b5 (0.8.1+). Python 3.14.4.

ITU-T X.691 (02/2021) §10.3.7, §30.1 and §30.6: constraints on character string types that are not known-multiplier types (UTF8String, GeneralString, GraphicString, TeletexString/T61String, VideotexString) are never PER-visible. Such a value is encoded as an unconstrained length in octets followed by the octets, exactly as if the constraint were absent. A permitted-alphabet constraint therefore has no effect on the PER encoding of these types.

pycrate applies the FROM alphabet anyway. _get_char_dyn() returns the alphabet's bit width (self._const_alpha.rdyn) even though _clen is None for these types. __to_per_val() then evaluates cdyn < self._clen and raises TypeError (the decoder does the same in _from_per). A valid value can neither be encoded nor decoded, in UPER or APER.

from pycrate_asn1c.asnproc import compile_text, generate_modules, PycrateGenerator
import importlib.util, os, tempfile

compile_text('''Repro DEFINITIONS AUTOMATIC TAGS ::= BEGIN
U   ::= UTF8String (FROM ("013"))
Gen ::= GeneralString (FROM ("013"))
T61 ::= TeletexString (FROM ("013"))
Ref ::= UTF8String
END''')
d = tempfile.mkdtemp()
p = os.path.join(d, 'repro_mod.py')
generate_modules(PycrateGenerator, p)
spec = importlib.util.spec_from_file_location('repro_mod', p)
m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m)
R = m.Repro

R.Ref.set_val('13'); print(R.Ref.to_uper().hex())   # 023133, as expected
R.U.set_val('13');   R.U.to_uper()                  # TypeError
R.U.from_uper(bytes.fromhex('023133'))              # TypeError

Observed, identical for U, Gen and T61, and for both UPER and APER:

TypeError: '<' not supported between instances of 'int' and 'NoneType'
  encode: pycrate_asn1rt/asnobj_str.py:2444 (0.7.11) / :2421 (master), in __to_per_val: if cdyn < self._clen:
  decode: pycrate_asn1rt/asnobj_str.py:2334 (0.7.11) / :2311 (master), in _from_per:   elif cdyn < self._clen:

Expected: 023133 for the value "13" from every one of them, in both UPER and APER (one length octet 2, then the octets 31 33), and decoding 023133 gives back "13". This is the same result as for the unconstrained Ref.

The cause is _get_char_dyn() (0.7.11 asnobj_str.py:1974-1991, master :1951-1968). Ignoring the alphabet constraint when _clen is None fixes all of the cases above:

     def _get_char_dyn(self):
         # returns the number of bits required to encode a character
-        if self._const_alpha:
+        if self._const_alpha and self._clen is not None:

With this change applied to master, the reproducer gives 023133 for every type and codec, and decoding round-trips. test_rt_base, test_rrc3g, test_lteran, test_nrran, test_tcap_map, test_tcap_cap and test_X509 from test/test_asn1rt.py still pass.

Related: #42 fixed the SIZE half of the same X.691 §30.6 rule for UTF8String; the maintainer noted there that alphabet constraints might not be covered. This is that remaining case.

Activity

  1. Yuren0312 commented on Oct 10, 2026

    @Yuren0312
    Contributor

    Thanks for the detailed report and the precise root-cause analysis. I've opened PR #107 which applies the fix you identified: _get_char_dyn() now ignores the permitted-alphabet constraint when _clen is None (i.e. for non-known-multiplier character string types), so the value is encoded as an unconstrained length in octets, per X.691 30.6.

    Added regression types for UTF8String / GeneralString / TeletexString with FROM ("013") plus encode/decode assertions; the full test_asn1rt.py suite runs green (test_rt_base, test_rrc3g, test_lteran, test_nrran, test_tcap_map, test_tcap_cap, test_x509).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions