Skip to content

Commit

Permalink
major codec re-design, Any ASN.1 type implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
elie committed Jan 26, 2011
1 parent b6c4f66 commit 4b3d569
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 109 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Revision 0.0.13a
----------------

- Major codec re-design.
- ASN.1 Any type is now supported.
- PKCS#7 example added
- changes towards performance improvement:
+ all dict.has_key() & dict.get() invocations replaced with modern syntax
(this breaks compatibility with Python 2.1 and older).
Expand Down
197 changes: 119 additions & 78 deletions pyasn1/codec/ber/decoder.py

Large diffs are not rendered by default.

51 changes: 38 additions & 13 deletions pyasn1/codec/ber/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):

return string.join(octets, ''), 0

class SequenceOfEncoder(AbstractItemEncoder):
class SequenceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.setDefaultComponents()
value.verifySizeSpec()
Expand All @@ -195,10 +195,20 @@ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
idx = idx - 1
if value[idx] is None: # Optional component
continue
if isinstance(value, univ.SequenceAndSetBase):
component = value.getDefaultComponentByPosition(idx)
if component is not None and component == value[idx]:
continue
component = value.getDefaultComponentByPosition(idx)
if component is not None and component == value[idx]:
continue
substrate = encodeFun(
value[idx], defMode, maxChunkSize
) + substrate
return substrate, 1

class SequenceOfEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.verifySizeSpec()
substrate = ''; idx = len(value)
while idx > 0:
idx = idx - 1
substrate = encodeFun(
value[idx], defMode, maxChunkSize
) + substrate
Expand All @@ -208,7 +218,9 @@ class ChoiceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return encodeFun(value.getComponent(), defMode, maxChunkSize), 1

codecMap = {
class AnyEncoder(OctetStringEncoder): pass

tagMap = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
univ.Boolean.tagSet: IntegerEncoder(),
univ.Integer.tagSet: IntegerEncoder(),
Expand Down Expand Up @@ -238,25 +250,38 @@ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
useful.UTCTime.tagSet: OctetStringEncoder()
}

# Type-to-codec map for ambiguous ASN.1 types
typeMap = {
univ.Set.typeId: SequenceEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(),
univ.Any.typeId: AnyEncoder()
}

class Encoder:
def __init__(self, _codecMap):
self.__codecMap = _codecMap
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap

def __call__(self, value, defMode=1, maxChunkSize=0):
tagSet = value.getTagSet()
if len(tagSet) > 1:
concreteEncoder = explicitlyTaggedItemEncoder
else:
if tagSet in self.__codecMap:
concreteEncoder = self.__codecMap[tagSet]
if value.typeId is not None and value.typeId in self.__typeMap:
concreteEncoder = self.__typeMap[value.typeId]
elif tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet]
else:
baseTagSet = value.baseTagSet
if baseTagSet in self.__codecMap:
concreteEncoder = self.__codecMap[baseTagSet]
if baseTagSet in self.__tagMap:
concreteEncoder = self.__tagMap[baseTagSet]
else:
raise Error('No encoder for %s' % value)
return concreteEncoder.encode(
self, value, defMode, maxChunkSize
)

encode = Encoder(codecMap)
encode = Encoder(tagMap, typeMap)
10 changes: 6 additions & 4 deletions pyasn1/codec/cer/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class BooleanDecoder(decoder.AbstractSimpleDecoder):
protoComponent = univ.Boolean(0)
def valueDecoder(self, substrate, asn1Spec, tagSet, length,
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun):
if not substrate:
raise error.PyAsn1Error('Empty substrate')
Expand All @@ -16,11 +16,13 @@ def valueDecoder(self, substrate, asn1Spec, tagSet, length,
value = 0
return self._createComponent(asn1Spec, tagSet, value), substrate[1:]

codecMap = decoder.codecMap.copy()
codecMap.update({
tagMap = decoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanDecoder(),
})

typeMap = decoder.typeMap

class Decoder(decoder.Decoder): pass

decode = Decoder(codecMap)
decode = Decoder(tagMap, decoder.typeMap)
19 changes: 12 additions & 7 deletions pyasn1/codec/cer/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,24 @@ def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
substrate = string.join(compSubs, '')
return substrate, 1

codecMap = encoder.codecMap.copy()
codecMap.update({
tagMap = encoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(),
univ.BitString.tagSet: BitStringEncoder(),
univ.OctetString.tagSet: OctetStringEncoder(),
# Set & SetOf have same tags
univ.SetOf().tagSet: SetOfEncoder()
univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set
})


typeMap = encoder.typeMap.copy()
typeMap.update({
univ.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder()
})

class Encoder(encoder.Encoder):
def __call__(self, client, defMode=0, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
encode = Encoder(codecMap)

encode = Encoder(tagMap, typeMap)

# EncoderFactory queries class instance and builds a map of tags -> encoders
2 changes: 1 addition & 1 deletion pyasn1/codec/der/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
from pyasn1.type import univ
from pyasn1.codec.cer import decoder

decode = decoder.Decoder(decoder.codecMap)
decode = decoder.Decoder(decoder.tagMap, decoder.typeMap)
8 changes: 5 additions & 3 deletions pyasn1/codec/der/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ def _cmpSetComponents(self, c1, c2):
c2.getEffectiveTagSet() or c2.getTagSet()
return cmp(tagSet1, tagSet2)

codecMap = encoder.codecMap.copy()
codecMap.update({
tagMap = encoder.tagMap.copy()
tagMap.update({
# Overload CER encodrs with BER ones (a bit hackerish XXX)
univ.BitString.tagSet: encoder.encoder.BitStringEncoder(),
univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(),
# Set & SetOf have same tags
univ.SetOf().tagSet: SetOfEncoder()
})

typeMap = encoder.typeMap

class Encoder(encoder.Encoder):
def __call__(self, client, defMode=1, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)

encode = Encoder(codecMap)
encode = Encoder(tagMap, typeMap)
4 changes: 4 additions & 0 deletions pyasn1/type/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class Asn1ItemBase(Asn1Item):
# A list of constraint.Constraint instances for checking values
subtypeSpec = constraint.ConstraintsIntersection()

# Used for ambiguous ASN.1 types identification
typeId = None

def __init__(self, tagSet=None, subtypeSpec=None):
if tagSet is None:
self._tagSet = self.tagSet
Expand All @@ -30,6 +33,7 @@ def _verifySubtypeSpec(self, value, idx=None):
def getSubtypeSpec(self): return self._subtypeSpec

def getTagSet(self): return self._tagSet
def getEffectiveTagSet(self): return self._tagSet # used by untagged types
def getTypeMap(self): return { self._tagSet: self }

def isSameTypeWith(self, other):
Expand Down
27 changes: 24 additions & 3 deletions pyasn1/type/univ.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ class SetOf(base.AbstractConstructedAsn1Item):
componentType = None
tagSet = baseTagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
)
)
typeId = 1

def _cloneComponentValues(self, myClone, cloneValueFlag):
idx = 0; l = len(self._componentValues)
Expand Down Expand Up @@ -405,6 +406,7 @@ class SequenceOf(SetOf):
tagSet = baseTagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
)
typeId = 2

class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
componentType = namedtype.NamedTypes()
Expand Down Expand Up @@ -515,17 +517,23 @@ class Sequence(SequenceAndSetBase):
tagSet = baseTagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
)
typeId = 3

def getComponentTypeMapNearPosition(self, idx):
return self._componentType.getTypeMapNearPosition(idx)
if self._componentType:
return self._componentType.getTypeMapNearPosition(idx)

def getComponentPositionNearType(self, tagSet, idx):
return self._componentType.getPositionNearType(tagSet, idx)
if self._componentType:
return self._componentType.getPositionNearType(tagSet, idx)
else:
return idx

class Set(SequenceAndSetBase):
tagSet = baseTagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
)
typeId = 4

def getComponent(self, innerFlag=0): return self

Expand Down Expand Up @@ -565,6 +573,7 @@ class Choice(Set):
sizeSpec = constraint.ConstraintsIntersection(
constraint.ValueSizeConstraint(1, 1)
)
typeId = 5
_currentIdx = None

def __cmp__(self, other):
Expand Down Expand Up @@ -666,5 +675,17 @@ def getName(self, innerFlag=0):

def setDefaultComponents(self): pass

class ContainsAnyTag:
def __contains__(self, key): return 1

containsAnyTag = ContainsAnyTag()

class Any(OctetString):
tagSet = baseTagSet = tag.TagSet() # untagged
typeId = 6

def getTypeMap(self):
return self._tagSet and { self._tagSet: self } or containsAnyTag

# XXX
# coercion rules?

0 comments on commit 4b3d569

Please sign in to comment.