From 27da95be36046410dbd9138ff893792bb7e02417 Mon Sep 17 00:00:00 2001 From: elie Date: Sun, 26 Apr 2015 12:17:48 +0000 Subject: more strict CER/DER encoders added for GeneralizedTime and UTCTime types --- CHANGES | 3 +- pyasn1/codec/cer/encoder.py | 44 +++++++++++++++++++++++++-- pyasn1/codec/der/encoder.py | 2 +- test/codec/cer/test_encoder.py | 69 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 112 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 7fbe937..c6ecfc6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -Revision 0.1.8rc2 +Revision 0.1.8rc3 ----------------- - ObjectIdentifier codec fixed to work properly with arc 0 and arc 2 values. @@ -49,6 +49,7 @@ Revision 0.1.8rc2 - String typed float initializer to REAL type now supported. - Float typed mantissa initializer to REAL type for base 2 added. - Encoding bases 8 and 16 support for REAL type binary encoder added. +- More strict CER/DER encoders added for GeneralizedTime and UTCTime types. - Fix to REAL type binary decoder to handle different bases and scale factor. - Fix to TagSet.repr() to include [obsolete] baseTag information. - Fix to broken REAL type decoding handling. diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py index b432b96..dad5955 100644 --- a/pyasn1/codec/cer/encoder.py +++ b/pyasn1/codec/cer/encoder.py @@ -1,7 +1,9 @@ # CER encoder from pyasn1.type import univ +from pyasn1.type import useful from pyasn1.codec.ber import encoder -from pyasn1.compat.octets import int2oct, null +from pyasn1.compat.octets import int2oct, str2octs, null +from pyasn1 import error class BooleanEncoder(encoder.IntegerEncoder): def encodeValue(self, encodeFun, client, defMode, maxChunkSize): @@ -29,8 +31,42 @@ class RealEncoder(encoder.RealEncoder): return self._dropFloatingPoint(m, b, e) # specialized GeneralStringEncoder here -# specialized GeneralizedTimeEncoder here -# specialized UTCTimeEncoder here + +class GeneralizedTimeEncoder(OctetStringEncoder): + zchar = str2octs('Z') + zero = str2octs('0') + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + octets = client.asOctets() + if '+' in octets: + raise error.PyAsn1Error('Must be UTC time') + if '.' not in octets: + raise error.PyAsn1Error('Format must include fraction of second') + if octets and octets[-1] != self.zchar: + raise error.PyAsn1Error('Missing timezone specifier') + if len(octets) < 16: + raise error.PyAsn1Error('Bad UTC time length') + if octets[-2] == self.zero or \ + octets[-3] != self.zero and octets[-2] == self.zero: + raise error.PyAsn1Error('Meningless zero in fraction of second') + return encoder.OctetStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class UTCTimeEncoder(encoder.OctetStringEncoder): + zchar = str2octs('Z') + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + octets = client.asOctets() + if '+' in octets: + raise error.PyAsn1Error('Must be UTC time') + if '.' in octets: + raise error.PyAsn1Error('Must be no fraction of second') + if octets and octets[-1] != self.zchar: + client = client.clone(octets + self.zchar) + if len(client) != 13: + raise error.PyAsn1Error('Bad UTC time length') + return encoder.OctetStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) class SetOfEncoder(encoder.SequenceOfEncoder): def encodeValue(self, encodeFun, client, defMode, maxChunkSize): @@ -74,6 +110,8 @@ tagMap.update({ univ.BitString.tagSet: BitStringEncoder(), univ.OctetString.tagSet: OctetStringEncoder(), univ.Real.tagSet: RealEncoder(), + useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(), + useful.UTCTime.tagSet: UTCTimeEncoder(), univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set }) diff --git a/pyasn1/codec/der/encoder.py b/pyasn1/codec/der/encoder.py index 0219ae7..2044df3 100644 --- a/pyasn1/codec/der/encoder.py +++ b/pyasn1/codec/der/encoder.py @@ -12,7 +12,7 @@ class SetOfEncoder(encoder.SetOfEncoder): tagMap = encoder.tagMap.copy() tagMap.update({ - # Overload CER encodrs with BER ones (a bit hackerish XXX) + # Overload CER encoders with BER ones (a bit hackerish XXX) univ.BitString.tagSet: encoder.encoder.BitStringEncoder(), univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(), # Set & SetOf have same tags diff --git a/test/codec/cer/test_encoder.py b/test/codec/cer/test_encoder.py index a4f80aa..5434996 100644 --- a/test/codec/cer/test_encoder.py +++ b/test/codec/cer/test_encoder.py @@ -1,4 +1,4 @@ -from pyasn1.type import namedtype, univ +from pyasn1.type import namedtype, univ, useful from pyasn1.codec.cer import encoder from pyasn1.compat.octets import ints2octs from pyasn1.error import PyAsn1Error @@ -104,4 +104,71 @@ class SetWithChoiceEncoderTestCase(unittest.TestCase): self.s.getComponentByName('status').setComponentByPosition(0, 1) assert encoder.encode(self.s) == ints2octs((49, 128, 1, 1, 255, 5, 0, 0, 0)) +class GeneralizedTimeEncoderTestCase(unittest.TestCase): + def testExtraZeroInSeconds(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112.10Z') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Meaningless trailing zero in fraction part tolerated' + + def testLocalTimezone(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112.1+0200') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Local timezone tolerated' + + def testMissingTimezone(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112.1') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Missing timezone tolerated' + + def testDecimalPoint(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112Z') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Missing decimal point tolerated' + +class UTCTimeEncoderTestCase(unittest.TestCase): + def testFractionOfSecond(self): + try: + assert encoder.encode( + useful.UTCTime('150501120112.10Z') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Decimal point tolerated' + + def testMissingTimezone(self): + assert encoder.encode( + useful.UTCTime('150501120112') + ) == ints2octs((23, 13, 49, 53, 48, 53, 48, 49, 49, 50, 48, 49, 49, 50, 90)), 'Missing timezone not added' + + def testLocalTimezone(self): + try: + assert encoder.encode( + useful.UTCTime('150501120112+0200') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Local timezone tolerated' + if __name__ == '__main__': unittest.main() -- cgit v1.2.1