Repository navigation
Description
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')) # TypeErrorObserved, 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
Yuren0312 commentedon Oct 10, 2026
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_clenisNone(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 fulltest_asn1rt.pysuite runs green (test_rt_base, test_rrc3g, test_lteran, test_nrran, test_tcap_map, test_tcap_cap, test_x509).