diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-09-08 20:52:36 +0200 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2017-09-10 14:29:29 +0200 |
commit | d35f412398a54a93ac141d88c41dac411bd05453 (patch) | |
tree | c36ccc6dfe8e34909922f9cc6e999378a69956f5 | |
parent | c324055b318fa2c01249819c44836214efdfb90e (diff) | |
download | pyasn1-git-d35f412398a54a93ac141d88c41dac411bd05453.tar.gz |
WIP: hole types implementation
-rw-r--r-- | pyasn1/codec/ber/decoder.py | 84 | ||||
-rw-r--r-- | pyasn1/type/definedby.py | 54 | ||||
-rw-r--r-- | pyasn1/type/namedtype.py | 31 | ||||
-rw-r--r-- | tests/codec/ber/test_decoder.py | 57 |
4 files changed, 167 insertions, 59 deletions
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py index c080b96..f8f0587 100644 --- a/pyasn1/codec/ber/decoder.py +++ b/pyasn1/codec/ber/decoder.py @@ -514,6 +514,33 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder): if namedTypes: if not namedTypes.requiredComponents.issubset(seenIndices): raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__) + + + for idx, namedType in enumerate(namedTypes.namedTypes): + if not namedType.definedBy: + continue + + governingValue = asn1Object.getComponentByName( + namedType.definedBy.name + ) + + try: + asn1Spec = namedType.definedBy[governingValue] + + except KeyError: + continue + + component, rest = decodeFun( + asn1Object.getComponentByPosition(idx).asOctets(), + asn1Spec=asn1Spec + ) + + asn1Object.setComponentByPosition( + idx, component, + matchTags=False, + matchConstraints=False + ) + else: asn1Object.verifySizeSpec() @@ -529,21 +556,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder): ) idx += 1 - if namedTypes: - for holeName, governingName, typesMap in namedTypes.holes: - holeComponent = asn1Object[holeName] - if holeComponent.isValue: - governingComponent = asn1Object[governingName] - if governingComponent in typesMap: - component, rest = decodeFun( - holeComponent.asOctets(), - asn1Spec=typesMap[governingComponent] - ) - asn1Object.setComponentByName(holeName, component, matchTags=False, matchConstraints=False) - - else: - asn1Object.verifySizeSpec() - return asn1Object, tail def indefLenValueDecoder(self, substrate, asn1Spec, @@ -624,6 +636,33 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder): if namedTypes: if not namedTypes.requiredComponents.issubset(seenIndices): raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__) + + for idx, namedType in enumerate(namedTypes.namedTypes): + if not namedType.definedBy: + continue + + governingValue = asn1Object.getComponentByName( + namedType.definedBy.name + ) + + try: + asn1Spec = namedType.definedBy[governingValue] + + except KeyError: + continue + + component, rest = decodeFun( + asn1Object.getComponentByPosition(idx).asOctets(), + asn1Spec=asn1Spec, allowEoo=True + ) + + if component is not eoo.endOfOctets: + asn1Object.setComponentByPosition( + idx, component, + matchTags=False, + matchConstraints=False + ) + else: asn1Object.verifySizeSpec() @@ -645,21 +684,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder): 'No EOO seen before substrate ends' ) - if namedTypes: - for holeName, governingName, typesMap in namedTypes.holes: - holeComponent = asn1Object[holeName] - if holeComponent.isValue: - governingComponent = asn1Object[governingName] - if governingComponent in typesMap: - component, rest = decodeFun( - holeComponent.asOctets(), - asn1Spec=typesMap[governingComponent] - ) - asn1Object.setComponentByName(holeName, component, matchTags=False, matchConstraints=False) - - else: - asn1Object.verifySizeSpec() - return asn1Object, substrate class SequenceOrSequenceOfDecoder(UniversalConstructedTypeDecoder): diff --git a/pyasn1/type/definedby.py b/pyasn1/type/definedby.py new file mode 100644 index 0000000..e22d394 --- /dev/null +++ b/pyasn1/type/definedby.py @@ -0,0 +1,54 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com> +# License: http://pyasn1.sf.net/license.html +# + +__all__ = ['DefinedBy'] + + +class DefinedBy(object): + """Create ASN.1 type map indexed by a value + + The *DefinedBy* object models the ASN.1 *DEFINED BY* clause which maps + values to ASN.1 types in the context of the ASN.1 SEQUENCE/SET type. + + DefinedBy objects are immutable and duck-type Python :class:`dict` objects + + Parameters + ---------- + name: :py:class:`str` + Field name + + *choices: + Sequence of (*value*, *type*) tuples representing the mapping + """ + + def __init__(self, name, args, **kwargs): + self.__name = name + self.__choices = dict(args, **kwargs) + + @property + def name(self): + return self.__name + + # Python dict protocol + + def values(self): + return self.__choices.values() + + def keys(self): + return self.__choices + + def items(self): + return self.__choices.items() + + def __contains__(self, key): + return key in self.__choices + + def __getitem__(self, key): + return self.__choices[key] + + def __iter__(self): + return iter(self.__choices) diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py index 6bf82f4..69b4422 100644 --- a/pyasn1/type/namedtype.py +++ b/pyasn1/type/namedtype.py @@ -30,12 +30,11 @@ class NamedType(object): isOptional = False isDefaulted = False - def __init__(self, name, asn1Object): + def __init__(self, name, asn1Object, definedBy=None): self.__name = name self.__type = asn1Object self.__nameAndType = name, asn1Object - self.__governingName = None - self.__typesMap = None + self.__definedBy = definedBy def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.__name, self.__type) @@ -75,18 +74,9 @@ class NamedType(object): def asn1Object(self): return self.__type - def definedBy(self, governingName, typesMap): - self.__governingName = governingName - self.__typesMap = typesMap - return self - - @property - def governingName(self): - return self.__governingName - @property - def typesMap(self): - return self.__typesMap + def definedBy(self): + return self.__definedBy # Backward compatibility @@ -138,7 +128,6 @@ class NamedTypes(object): self.__keys = frozenset([namedType.name for namedType in self.__namedTypes]) self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes]) self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes]) - self.__holes = self.__computeTypeHoles() def __repr__(self): return '%s(%s)' % ( @@ -525,15 +514,3 @@ class NamedTypes(object): @property def requiredComponents(self): return self.__requiredComponents - - @property - def holes(self): - return self.__holes - - def __computeTypeHoles(self): - holes = [] - for namedType in self.__namedTypes: - if namedType.governingName: - holes.append((namedType.name, namedType.governingName, namedType.typesMap)) - - return holes diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py index d2f02ad..45cd530 100644 --- a/tests/codec/ber/test_decoder.py +++ b/tests/codec/ber/test_decoder.py @@ -10,7 +10,7 @@ try: except ImportError: import unittest -from pyasn1.type import tag, namedtype, univ, char +from pyasn1.type import tag, namedtype, univ, char, definedby from pyasn1.codec.ber import decoder, eoo from pyasn1.compat.octets import ints2octs, str2octs, null from pyasn1.error import PyAsn1Error @@ -720,7 +720,7 @@ class SequenceDecoderWithSchemaTestCase(unittest.TestCase): def testDefMode(self): self.__init() assert decoder.decode( - ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s + ints2octs((48, 2, 5, 0)), asn1Spec=self.s ) == (self.s, null) def testIndefMode(self): @@ -823,6 +823,59 @@ class SequenceDecoderWithSchemaTestCase(unittest.TestCase): ) == (self.s, null) +class SequenceDecoderWithIntegerHoleTypesTestCase(unittest.TestCase): + def setUp(self): + definedBy = definedby.DefinedBy( + 'id', + [(1, univ.Integer()), + (2, univ.OctetString())] + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), definedBy=definedBy) + ) + ) + + # def testChoiceOne(self): + # s, r = decoder.decode( + # ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), + # asn1Spec=self.s + # ) + # assert not r + # assert s[0] == 1 + # assert s[1] == ints2octs((2, 1, 12)) + # + # def testChoiceTwo(self): + # s, r = decoder.decode( + # ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), + # asn1Spec=self.s + # ) + # assert not r + # assert s[0] == 1 + # assert s[1] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testChoiceOneResolveHoles(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s +# TODO +# resolveHoleTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testChoiceTwoResolveHoles(self): + s, r = decoder.decode( + ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s +# TODO +# resolveHoleTypes = True + ) + assert not r + assert s[0] == 2 + assert s[1] == univ.OctetString('quick brown') + + class SetDecoderTestCase(unittest.TestCase): def setUp(self): self.s = univ.Set( |