summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2017-07-29 18:31:52 +0200
committerIlya Etingof <etingof@gmail.com>2017-07-29 18:31:52 +0200
commit135ab65b34212b31c11db3d7c8b0cc02a112c89f (patch)
tree147d0c11a0a0ab3567937e88eb23919f09164b36
parent7c5db3287f2a99e830066279b1973b61eb11453e (diff)
downloadpyasn1-git-135ab65b34212b31c11db3d7c8b0cc02a112c89f.tar.gz
fixed nested optional SET/SEQUENCE at CER/DER codec
-rw-r--r--CHANGES.rst4
-rw-r--r--pyasn1/__init__.py1
-rw-r--r--pyasn1/codec/cer/encoder.py48
-rw-r--r--pyasn1/type/univ.py4
-rw-r--r--tests/codec/cer/test_encoder.py87
5 files changed, 132 insertions, 12 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index b937d6d..d2c43b6 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,8 +1,10 @@
-Revision 0.3.1, released XX-07-2017
+Revision 0.3.2, released XX-07-2017
-----------------------------------
- Fixed GeneralizedTime/UTCTime CER/DER codecs to actually get invoked
+- Fixed DER/CER encoders handling optional SEQUENCE/SET fields containing
+ nested SEQUENCE/SET with optional fields.
Revision 0.3.1, released 26-07-2017
-----------------------------------
diff --git a/pyasn1/__init__.py b/pyasn1/__init__.py
index b52ce35..583827b 100644
--- a/pyasn1/__init__.py
+++ b/pyasn1/__init__.py
@@ -5,4 +5,3 @@ __version__ = '0.3.2'
if sys.version_info[:2] < (2, 4):
raise RuntimeError('PyASN1 requires Python 2.4 or later')
-
diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py
index 021fd22..59a64d5 100644
--- a/pyasn1/codec/cer/encoder.py
+++ b/pyasn1/codec/cer/encoder.py
@@ -89,7 +89,29 @@ class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
maxLength = 14
-class SetOfEncoder(encoder.SequenceOfEncoder):
+class SetAndSequenceEncoderMixIn(object):
+ def componentsToEncode(self, value):
+ namedTypes = value.componentType
+ componentsToEncode = []
+ idx = len(value)
+ while idx > 0:
+ idx -= 1
+ if namedTypes:
+ if namedTypes[idx].isOptional:
+ if isinstance(value[idx], (univ.Sequence, univ.Set)):
+ if not self.componentsToEncode(value[idx]):
+ continue
+ elif not value[idx].isValue:
+ continue
+ if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
+ continue
+
+ componentsToEncode.append(idx)
+
+ return componentsToEncode
+
+
+class SetOfEncoder(encoder.SequenceOfEncoder, SetAndSequenceEncoderMixIn):
@staticmethod
def _sortComponents(components):
# sort by tags regardless of the Choice value (static sort)
@@ -105,12 +127,7 @@ class SetOfEncoder(encoder.SequenceOfEncoder):
# Set
namedTypes = client.componentType
comps = []
- while idx > 0:
- idx -= 1
- if namedTypes[idx].isOptional and not client[idx].isValue:
- continue
- if namedTypes[idx].isDefaulted and client[idx] == namedTypes[idx].asn1Object:
- continue
+ for idx in self.componentsToEncode(client):
comps.append(client[idx])
for comp in self._sortComponents(comps):
substrate += encodeFun(comp, defMode, maxChunkSize)
@@ -129,6 +146,15 @@ class SetOfEncoder(encoder.SequenceOfEncoder):
return substrate, True, True
+class SequenceEncoder(encoder.SequenceEncoder, SetAndSequenceEncoderMixIn):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ value.verifySizeSpec()
+ substrate = null
+ for idx in self.componentsToEncode(value):
+ substrate = encodeFun(value[idx], defMode, maxChunkSize) + substrate
+
+ return substrate, True, True
+
tagMap = encoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(),
@@ -137,7 +163,9 @@ tagMap.update({
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(),
- univ.SetOf.tagSet: SetOfEncoder() # conflcts with Set
+ # Sequence & Set have same tags as SequenceOf & SetOf
+ univ.SetOf.tagSet: SetOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder()
})
typeMap = encoder.typeMap.copy()
@@ -148,8 +176,10 @@ typeMap.update({
univ.Real.typeId: RealEncoder(),
useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
useful.UTCTime.typeId: UTCTimeEncoder(),
+ # Sequence & Set have same tags as SequenceOf & SetOf
univ.Set.typeId: SetOfEncoder(),
- univ.SetOf.typeId: SetOfEncoder()
+ univ.SetOf.typeId: SetOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder()
})
diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py
index 59644c1..5767dd8 100644
--- a/pyasn1/type/univ.py
+++ b/pyasn1/type/univ.py
@@ -2209,7 +2209,9 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
for idx, subComponentType in enumerate(componentType.namedTypes):
if subComponentType.isDefaulted or subComponentType.isOptional:
continue
- if not self._componentValues or self._componentValues[idx] is None or not self._componentValues[idx].isValue:
+ if (not self._componentValues or
+ self._componentValues[idx] is None or
+ not self._componentValues[idx].isValue):
return False
else:
diff --git a/tests/codec/cer/test_encoder.py b/tests/codec/cer/test_encoder.py
index 02b28c6..fbf2b97 100644
--- a/tests/codec/cer/test_encoder.py
+++ b/tests/codec/cer/test_encoder.py
@@ -214,6 +214,93 @@ class UTCTimeEncoderTestCase(unittest.TestCase):
) == ints2octs((23, 11, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 90))
+class NestedOptionalSequenceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ inner = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('first-name', univ.OctetString()),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ )
+ )
+
+ outerWithOptional = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.OptionalNamedType('inner', inner),
+ )
+ )
+
+ outerWithDefault = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.DefaultedNamedType('inner', inner),
+ )
+ )
+
+ self.s1 = outerWithOptional
+ self.s2 = outerWithDefault
+
+ def __initOptionalWithDefaultAndOptional(self):
+ self.s1.clear()
+ self.s1[0][0] = 'test'
+ self.s1[0][1] = 123
+ return self.s1
+
+ def __initOptionalWithDefault(self):
+ self.s1.clear()
+ self.s1[0][1] = 123
+ return self.s1
+
+ def __initOptionalWithOptional(self):
+ self.s1.clear()
+ self.s1[0][0] = 'test'
+ return self.s1
+
+ def __initOptional(self):
+ self.s1.clear()
+ return self.s1
+
+ def __initDefaultWithDefaultAndOptional(self):
+ self.s2.clear()
+ self.s2[0][0] = 'test'
+ self.s2[0][1] = 123
+ return self.s2
+
+ def __initDefaultWithDefault(self):
+ self.s2.clear()
+ self.s2[0][0] = 'test'
+ return self.s2
+
+ def __initDefaultWithOptional(self):
+ self.s2.clear()
+ self.s2[0][1] = 123
+ return self.s2
+
+ def testDefModeOptionalWithDefaultAndOptional(self):
+ s = self.__initOptionalWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+ def testDefModeOptionalWithDefault(self):
+ s = self.__initOptionalWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
+ def testDefModeOptionalWithOptional(self):
+ s = self.__initOptionalWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+ def testDefModeOptional(self):
+ s = self.__initOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 0, 0))
+
+ def testDefModeDefaultWithDefaultAndOptional(self):
+ s = self.__initDefaultWithDefaultAndOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0))
+
+ def testDefModeDefaultWithDefault(self):
+ s = self.__initDefaultWithDefault()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0))
+
+ def testDefModeDefaultWithOptional(self):
+ s = self.__initDefaultWithOptional()
+ assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0))
+
suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
if __name__ == '__main__':