aboutsummaryrefslogtreecommitdiff
path: root/pyasn1/codec/cer/encoder.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyasn1/codec/cer/encoder.py')
-rw-r--r--pyasn1/codec/cer/encoder.py149
1 files changed, 82 insertions, 67 deletions
diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py
index 788567f..6a9db97 100644
--- a/pyasn1/codec/cer/encoder.py
+++ b/pyasn1/codec/cer/encoder.py
@@ -31,17 +31,20 @@ class RealEncoder(encoder.RealEncoder):
# specialized GeneralStringEncoder here
class TimeEncoderMixIn(object):
- zchar, = str2octs('Z')
- pluschar, = str2octs('+')
- minuschar, = str2octs('-')
- commachar, = str2octs(',')
- minLength = 12
- maxLength = 19
+ Z_CHAR = ord('Z')
+ PLUS_CHAR = ord('+')
+ MINUS_CHAR = ord('-')
+ COMMA_CHAR = ord(',')
+ DOT_CHAR = ord('.')
+ ZERO_CHAR = ord('0')
+
+ MIN_LENGTH = 12
+ MAX_LENGTH = 19
def encodeValue(self, value, asn1Spec, encodeFun, **options):
- # Encoding constraints:
+ # CER encoding constraints:
# - minutes are mandatory, seconds are optional
- # - sub-seconds must NOT be zero
+ # - sub-seconds must NOT be zero / no meaningless zeros
# - no hanging fraction dot
# - time in UTC (Z)
# - only dot is allowed for fractions
@@ -49,20 +52,46 @@ class TimeEncoderMixIn(object):
if asn1Spec is not None:
value = asn1Spec.clone(value)
- octets = value.asOctets()
-
- if not self.minLength < len(octets) < self.maxLength:
- raise error.PyAsn1Error('Length constraint violated: %r' % value)
+ numbers = value.asNumbers()
- if self.pluschar in octets or self.minuschar in octets:
- raise error.PyAsn1Error('Must be UTC time: %r' % octets)
+ if self.PLUS_CHAR in numbers or self.MINUS_CHAR in numbers:
+ raise error.PyAsn1Error('Must be UTC time: %r' % value)
- if octets[-1] != self.zchar:
- raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % octets)
+ if numbers[-1] != self.Z_CHAR:
+ raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % value)
- if self.commachar in octets:
+ if self.COMMA_CHAR in numbers:
raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
+ if self.DOT_CHAR in numbers:
+
+ isModified = False
+
+ numbers = list(numbers)
+
+ searchIndex = min(numbers.index(self.DOT_CHAR) + 4, len(numbers) - 1)
+
+ while numbers[searchIndex] != self.DOT_CHAR:
+ if numbers[searchIndex] == self.ZERO_CHAR:
+ del numbers[searchIndex]
+ isModified = True
+
+ searchIndex -= 1
+
+ searchIndex += 1
+
+ if searchIndex < len(numbers):
+ if numbers[searchIndex] == self.Z_CHAR:
+ # drop hanging comma
+ del numbers[searchIndex - 1]
+ isModified = True
+
+ if isModified:
+ value = value.clone(numbers)
+
+ if not self.MIN_LENGTH < len(numbers) < self.MAX_LENGTH:
+ raise error.PyAsn1Error('Length constraint violated: %r' % value)
+
options.update(maxChunkSize=1000)
return encoder.OctetStringEncoder.encodeValue(
@@ -71,13 +100,44 @@ class TimeEncoderMixIn(object):
class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
- minLength = 12
- maxLength = 19
+ MIN_LENGTH = 12
+ MAX_LENGTH = 20
class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
- minLength = 10
- maxLength = 14
+ MIN_LENGTH = 10
+ MAX_LENGTH = 14
+
+
+class SetOfEncoder(encoder.SequenceOfEncoder):
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+ chunks = self._encodeComponents(
+ value, asn1Spec, encodeFun, **options)
+
+ # sort by serialised and padded components
+ if len(chunks) > 1:
+ zero = str2octs('\x00')
+ maxLen = max(map(len, chunks))
+ paddedChunks = [
+ (x.ljust(maxLen, zero), x) for x in chunks
+ ]
+ paddedChunks.sort(key=lambda x: x[0])
+
+ chunks = [x[1] for x in paddedChunks]
+
+ return null.join(chunks), True, True
+
+
+class SequenceOfEncoder(encoder.SequenceOfEncoder):
+ def encodeValue(self, value, asn1Spec, encodeFun, **options):
+
+ if options.get('ifNotEmpty', False) and not len(value):
+ return null, True, True
+
+ chunks = self._encodeComponents(
+ value, asn1Spec, encodeFun, **options)
+
+ return null.join(chunks), True, True
class SetEncoder(encoder.SequenceEncoder):
@@ -168,55 +228,10 @@ class SetEncoder(encoder.SequenceEncoder):
return substrate, True, True
-class SetOfEncoder(encoder.SequenceOfEncoder):
- def encodeValue(self, value, asn1Spec, encodeFun, **options):
- if asn1Spec is None:
- value.verifySizeSpec()
- else:
- asn1Spec = asn1Spec.componentType
-
- components = [encodeFun(x, asn1Spec, **options)
- for x in value]
-
- # sort by serialised and padded components
- if len(components) > 1:
- zero = str2octs('\x00')
- maxLen = max(map(len, components))
- paddedComponents = [
- (x.ljust(maxLen, zero), x) for x in components
- ]
- paddedComponents.sort(key=lambda x: x[0])
-
- components = [x[1] for x in paddedComponents]
-
- substrate = null.join(components)
-
- return substrate, True, True
-
-
class SequenceEncoder(encoder.SequenceEncoder):
omitEmptyOptionals = True
-class SequenceOfEncoder(encoder.SequenceOfEncoder):
- def encodeValue(self, value, asn1Spec, encodeFun, **options):
-
- if options.get('ifNotEmpty', False) and not len(value):
- return null, True, True
-
- if asn1Spec is None:
- value.verifySizeSpec()
- else:
- asn1Spec = asn1Spec.componentType
-
- substrate = null
-
- for idx, component in enumerate(value):
- substrate += encodeFun(value[idx], asn1Spec, **options)
-
- return substrate, True, True
-
-
tagMap = encoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(),
@@ -269,7 +284,7 @@ class Encoder(encoder.Encoder):
#:
#: Raises
#: ------
-#: :py:class:`~pyasn1.error.PyAsn1Error`
+#: ~pyasn1.error.PyAsn1Error
#: On encoding errors
#:
#: Examples