diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-07-29 18:31:52 +0200 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2017-07-29 18:31:52 +0200 |
commit | 135ab65b34212b31c11db3d7c8b0cc02a112c89f (patch) | |
tree | 147d0c11a0a0ab3567937e88eb23919f09164b36 | |
parent | 7c5db3287f2a99e830066279b1973b61eb11453e (diff) | |
download | pyasn1-git-135ab65b34212b31c11db3d7c8b0cc02a112c89f.tar.gz |
fixed nested optional SET/SEQUENCE at CER/DER codec
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | pyasn1/__init__.py | 1 | ||||
-rw-r--r-- | pyasn1/codec/cer/encoder.py | 48 | ||||
-rw-r--r-- | pyasn1/type/univ.py | 4 | ||||
-rw-r--r-- | tests/codec/cer/test_encoder.py | 87 |
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__': |