upgrade pyasn1 to 0.3.7

This commit is contained in:
Jay Lee
2017-10-08 21:22:35 -04:00
parent 98438644c5
commit ce5eb39f37
29 changed files with 6957 additions and 2233 deletions

View File

@ -1,8 +1,7 @@
import sys import sys
# http://www.python.org/dev/peps/pep-0396/ # http://www.python.org/dev/peps/pep-0396/
__version__ = '0.1.9' __version__ = '0.3.7'
if sys.version_info[:2] < (2, 4): if sys.version_info[:2] < (2, 4):
raise RuntimeError('PyASN1 requires Python 2.4 or later') raise RuntimeError('PyASN1 requires Python 2.4 or later')

File diff suppressed because it is too large Load Diff

View File

@ -1,214 +1,251 @@
# BER encoder #
from pyasn1.type import base, tag, univ, char, useful # This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import tag, univ, char, useful
from pyasn1.codec.ber import eoo from pyasn1.codec.ber import eoo
from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
from pyasn1.compat.integer import to_bytes
from pyasn1 import debug, error from pyasn1 import debug, error
class Error(Exception): pass __all__ = ['encode']
class AbstractItemEncoder:
class AbstractItemEncoder(object):
supportIndefLenMode = 1 supportIndefLenMode = 1
def encodeTag(self, t, isConstructed):
tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot # An outcome of otherwise legit call `encodeFun(eoo.endOfOctets)`
v = tagClass | tagFormat eooIntegerSubstrate = (0, 0)
eooOctetsSubstrate = ints2octs(eooIntegerSubstrate)
# noinspection PyMethodMayBeStatic
def encodeTag(self, singleTag, isConstructed):
tagClass, tagFormat, tagId = singleTag
encodedTag = tagClass | tagFormat
if isConstructed: if isConstructed:
v = v|tag.tagFormatConstructed encodedTag |= tag.tagFormatConstructed
if tagId < 31: if tagId < 31:
return int2oct(v|tagId) return encodedTag | tagId,
else: else:
s = int2oct(tagId&0x7f) substrate = tagId & 0x7f,
tagId = tagId >> 7 tagId >>= 7
while tagId: while tagId:
s = int2oct(0x80|(tagId&0x7f)) + s substrate = (0x80 | (tagId & 0x7f),) + substrate
tagId = tagId >> 7 tagId >>= 7
return int2oct(v|0x1F) + s return (encodedTag | 0x1F,) + substrate
def encodeLength(self, length, defMode): def encodeLength(self, length, defMode):
if not defMode and self.supportIndefLenMode: if not defMode and self.supportIndefLenMode:
return int2oct(0x80) return (0x80,)
if length < 0x80: if length < 0x80:
return int2oct(length) return length,
else: else:
substrate = null substrate = ()
while length: while length:
substrate = int2oct(length&0xff) + substrate substrate = (length & 0xff,) + substrate
length = length >> 8 length >>= 8
substrateLen = len(substrate) substrateLen = len(substrate)
if substrateLen > 126: if substrateLen > 126:
raise Error('Length octets overflow (%d)' % substrateLen) raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
return int2oct(0x80 | substrateLen) + substrate return (0x80 | substrateLen,) + substrate
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
raise Error('Not implemented') raise error.PyAsn1Error('Not implemented')
def _encodeEndOfOctets(self, encodeFun, defMode): def encode(self, value, encodeFun, **options):
if defMode or not self.supportIndefLenMode:
return null
else:
return encodeFun(eoo.endOfOctets, defMode)
def encode(self, encodeFun, value, defMode, maxChunkSize): tagSet = value.tagSet
substrate, isConstructed = self.encodeValue(
encodeFun, value, defMode, maxChunkSize # untagged item?
if not tagSet:
substrate, isConstructed, isOctets = self.encodeValue(
value, encodeFun, **options
) )
tagSet = value.getTagSet() return substrate
if tagSet:
if not isConstructed: # primitive form implies definite mode defMode = options.get('defMode', True)
defMode = 1
return self.encodeTag( for idx, singleTag in enumerate(tagSet.superTags):
tagSet[-1], isConstructed
) + self.encodeLength( defModeOverride = defMode
len(substrate), defMode
) + substrate + self._encodeEndOfOctets(encodeFun, defMode) # base tag?
if not idx:
substrate, isConstructed, isOctets = self.encodeValue(
value, encodeFun, **options
)
if options.get('ifNotEmpty', False) and not substrate:
return substrate
# primitive form implies definite mode
if not isConstructed:
defModeOverride = True
header = self.encodeTag(singleTag, isConstructed)
header += self.encodeLength(len(substrate), defModeOverride)
if isOctets:
substrate = ints2octs(header) + substrate
if not defModeOverride:
substrate += self.eooOctetsSubstrate
else: else:
return substrate # untagged value substrate = header + substrate
if not defModeOverride:
substrate += self.eooIntegerSubstrate
if not isOctets:
substrate = ints2octs(substrate)
return substrate
class EndOfOctetsEncoder(AbstractItemEncoder): class EndOfOctetsEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
return null, 0 return null, False, True
class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if isinstance(value, base.AbstractConstructedAsn1Item):
value = value.clone(tagSet=value.getTagSet()[:-1],
cloneValueFlag=1)
else:
value = value.clone(tagSet=value.getTagSet()[:-1])
return encodeFun(value, defMode, maxChunkSize), 1
explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
class BooleanEncoder(AbstractItemEncoder): class BooleanEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
_true = ints2octs((1,))
_false = ints2octs((0,)) def encodeValue(self, value, encodeFun, **options):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): return value and (1,) or (0,), False, False
return value and self._true or self._false, 0
class IntegerEncoder(AbstractItemEncoder): class IntegerEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
supportCompactZero = False supportCompactZero = False
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if value == 0: # shortcut for zero value def encodeValue(self, value, encodeFun, **options):
if value == 0:
# de-facto way to encode zero
if self.supportCompactZero: if self.supportCompactZero:
# this seems to be a correct way for encoding zeros return (), False, False
return null, 0
else: else:
# this seems to be a widespread way for encoding zeros return (0,), False, False
return ints2octs((0,)), 0
octets = [] return to_bytes(int(value), signed=True), False, True
value = int(value) # to save on ops on asn1 type
while 1:
octets.insert(0, value & 0xff)
if value == 0 or value == -1:
break
value = value >> 8
if value == 0 and octets[0] & 0x80:
octets.insert(0, 0)
while len(octets) > 1 and \
(octets[0] == 0 and octets[1] & 0x80 == 0 or \
octets[0] == 0xff and octets[1] & 0x80 != 0):
del octets[0]
return ints2octs(octets), 0
class BitStringEncoder(AbstractItemEncoder): class BitStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
if not maxChunkSize or len(value) <= maxChunkSize*8: valueLength = len(value)
out_len = (len(value) + 7) // 8 if valueLength % 8:
out_list = out_len * [0] alignedValue = value << (8 - valueLength % 8)
j = 7
i = -1
for val in value:
j += 1
if j == 8:
i += 1
j = 0
out_list[i] = out_list[i] | val << (7-j)
return int2oct(7-j) + ints2octs(out_list), 0
else: else:
pos = 0; substrate = null alignedValue = value
while 1:
# count in octets maxChunkSize = options.get('maxChunkSize', 0)
v = value.clone(value[pos*8:pos*8+maxChunkSize*8]) if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
if not v: substrate = alignedValue.asOctets()
break return int2oct(len(substrate) * 8 - valueLength) + substrate, False, True
substrate = substrate + encodeFun(v, defMode, maxChunkSize)
pos = pos + maxChunkSize # strip off explicit tags
return substrate, 1 alignedValue = alignedValue.clone(
tagSet=tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
)
stop = 0
substrate = null
while stop < valueLength:
start = stop
stop = min(start + maxChunkSize * 8, valueLength)
substrate += encodeFun(alignedValue[start:stop], **options)
return substrate, True, True
class OctetStringEncoder(AbstractItemEncoder): class OctetStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
maxChunkSize = options.get('maxChunkSize', 0)
if not maxChunkSize or len(value) <= maxChunkSize: if not maxChunkSize or len(value) <= maxChunkSize:
return value.asOctets(), 0 return value.asOctets(), False, True
else: else:
pos = 0; substrate = null # will strip off explicit tags
while 1: baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
v = value.clone(value[pos:pos+maxChunkSize])
if not v: pos = 0
substrate = null
while True:
chunk = value.clone(value[pos:pos + maxChunkSize],
tagSet=baseTagSet)
if not chunk:
break break
substrate = substrate + encodeFun(v, defMode, maxChunkSize) substrate += encodeFun(chunk, **options)
pos = pos + maxChunkSize pos += maxChunkSize
return substrate, 1
return substrate, True, True
class NullEncoder(AbstractItemEncoder): class NullEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0 def encodeValue(self, value, encodeFun, **options):
return null, False, True
class ObjectIdentifierEncoder(AbstractItemEncoder): class ObjectIdentifierEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
precomputedValues = {
(1, 3, 6, 1, 2): (43, 6, 1, 2), def encodeValue(self, value, encodeFun, **options):
(1, 3, 6, 1, 4): (43, 6, 1, 4)
}
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
oid = value.asTuple() oid = value.asTuple()
if oid[:5] in self.precomputedValues:
octets = self.precomputedValues[oid[:5]] # Build the first pair
oid = oid[5:] try:
else: first = oid[0]
if len(oid) < 2: second = oid[1]
except IndexError:
raise error.PyAsn1Error('Short OID %s' % (value,)) raise error.PyAsn1Error('Short OID %s' % (value,))
if 0 <= second <= 39:
if first == 1:
oid = (second + 40,) + oid[2:]
elif first == 0:
oid = (second,) + oid[2:]
elif first == 2:
oid = (second + 80,) + oid[2:]
else:
raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
elif first == 2:
oid = (second + 80,) + oid[2:]
else:
raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
octets = () octets = ()
# Build the first twos
if oid[0] == 0 and 0 <= oid[1] <= 39:
oid = (oid[1],) + oid[2:]
elif oid[0] == 1 and 0 <= oid[1] <= 39:
oid = (oid[1] + 40,) + oid[2:]
elif oid[0] == 2:
oid = (oid[1] + 80,) + oid[2:]
else:
raise error.PyAsn1Error(
'Impossible initial arcs %s at %s' % (oid[:2], value)
)
# Cycle through subIds # Cycle through subIds
for subId in oid: for subOid in oid:
if subId > -1 and subId < 128: if 0 <= subOid <= 127:
# Optimize for the common case # Optimize for the common case
octets = octets + (subId & 0x7f,) octets += (subOid,)
elif subId < 0: elif subOid > 127:
raise error.PyAsn1Error(
'Negative OID arc %s at %s' % (subId, value)
)
else:
# Pack large Sub-Object IDs # Pack large Sub-Object IDs
res = (subId & 0x7f,) res = (subOid & 0x7f,)
subId = subId >> 7 subOid >>= 7
while subId > 0: while subOid:
res = (0x80 | (subId & 0x7f),) + res res = (0x80 | (subOid & 0x7f),) + res
subId = subId >> 7 subOid >>= 7
# Add packed Sub-Object ID to resulted Object ID # Add packed Sub-Object ID to resulted Object ID
octets += res octets += res
else:
raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
return octets, False, False
return ints2octs(octets), 0
class RealEncoder(AbstractItemEncoder): class RealEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = 0
binEncBase = 2 # set to None to choose encoding base automatically binEncBase = 2 # set to None to choose encoding base automatically
def _dropFloatingPoint(self, m, encbase, e):
@staticmethod
def _dropFloatingPoint(m, encbase, e):
ms, es = 1, 1 ms, es = 1, 1
if m < 0: if m < 0:
ms = -1 # mantissa sign ms = -1 # mantissa sign
@ -216,13 +253,13 @@ class RealEncoder(AbstractItemEncoder):
es = -1 # exponenta sign es = -1 # exponenta sign
m *= ms m *= ms
if encbase == 8: if encbase == 8:
m = m*2**(abs(e) % 3 * es) m *= 2 ** (abs(e) % 3 * es)
e = abs(e) // 3 * es e = abs(e) // 3 * es
elif encbase == 16: elif encbase == 16:
m = m*2**(abs(e) % 4 * es) m *= 2 ** (abs(e) % 4 * es)
e = abs(e) // 4 * es e = abs(e) // 4 * es
while 1: while True:
if int(m) != m: if int(m) != m:
m *= encbase m *= encbase
e -= 1 e -= 1
@ -232,41 +269,43 @@ class RealEncoder(AbstractItemEncoder):
def _chooseEncBase(self, value): def _chooseEncBase(self, value):
m, b, e = value m, b, e = value
base = [2, 8, 16] encBase = [2, 8, 16]
if value.binEncBase in base: if value.binEncBase in encBase:
return self._dropFloatingPoint(m, value.binEncBase, e) return self._dropFloatingPoint(m, value.binEncBase, e)
elif self.binEncBase in base: elif self.binEncBase in encBase:
return self._dropFloatingPoint(m, self.binEncBase, e) return self._dropFloatingPoint(m, self.binEncBase, e)
# auto choosing base 2/8/16 # auto choosing base 2/8/16
mantissa = [m, m, m] mantissa = [m, m, m]
exponenta = [e, e, e] exponenta = [e, e, e]
sign = 1
encbase = 2 encbase = 2
e = float('inf') e = float('inf')
for i in range(3): for i in range(3):
sign, mantissa[i], base[i], exponenta[i] = \ (sign,
self._dropFloatingPoint(mantissa[i], base[i], exponenta[i]) mantissa[i],
if abs(exponenta[i]) < abs(e) or \ encBase[i],
(abs(exponenta[i]) == abs(e) and mantissa[i] < m): exponenta[i]) = self._dropFloatingPoint(mantissa[i], encBase[i], exponenta[i])
if abs(exponenta[i]) < abs(e) or (abs(exponenta[i]) == abs(e) and mantissa[i] < m):
e = exponenta[i] e = exponenta[i]
m = int(mantissa[i]) m = int(mantissa[i])
encbase = base[i] encbase = encBase[i]
return sign, m, encbase, e return sign, m, encbase, e
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
if value.isPlusInfinity(): if value.isPlusInf:
return int2oct(0x40), 0 return (0x40,), False, False
if value.isMinusInfinity(): if value.isMinusInf:
return int2oct(0x41), 0 return (0x41,), False, False
m, b, e = value m, b, e = value
if not m: if not m:
return null, 0 return null, False, True
if b == 10: if b == 10:
return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0 return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), False, True
elif b == 2: elif b == 2:
fo = 0x80 # binary encoding fo = 0x80 # binary encoding
ms, m, encbase, e = self._chooseEncBase(value) ms, m, encbase, e = self._chooseEncBase(value)
if ms < 0: # mantissa sign if ms < 0: # mantissa sign
fo = fo | 0x40 # sign bit fo |= 0x40 # sign bit
# exponenta & mantissa normalization # exponenta & mantissa normalization
if encbase == 2: if encbase == 2:
while m & 0x1 == 0: while m & 0x1 == 0:
@ -291,10 +330,10 @@ class RealEncoder(AbstractItemEncoder):
fo |= sf << 2 fo |= sf << 2
eo = null eo = null
if e == 0 or e == -1: if e == 0 or e == -1:
eo = int2oct(e&0xff) eo = int2oct(e & 0xff)
else: else:
while e not in (0, -1): while e not in (0, -1):
eo = int2oct(e&0xff) + eo eo = int2oct(e & 0xff) + eo
e >>= 8 e >>= 8
if e == 0 and eo and oct2int(eo[0]) & 0x80: if e == 0 and eo and oct2int(eo[0]) & 0x80:
eo = int2oct(0) + eo eo = int2oct(0) + eo
@ -311,51 +350,57 @@ class RealEncoder(AbstractItemEncoder):
fo |= 2 fo |= 2
else: else:
fo |= 3 fo |= 3
eo = int2oct(n&0xff) + eo eo = int2oct(n & 0xff) + eo
po = null po = null
while m: while m:
po = int2oct(m&0xff) + po po = int2oct(m & 0xff) + po
m >>= 8 m >>= 8
substrate = int2oct(fo) + eo + po substrate = int2oct(fo) + eo + po
return substrate, 0 return substrate, False, True
else: else:
raise error.PyAsn1Error('Prohibited Real base %s' % b) raise error.PyAsn1Error('Prohibited Real base %s' % b)
class SequenceEncoder(AbstractItemEncoder): class SequenceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
value.setDefaultComponents()
value.verifySizeSpec() value.verifySizeSpec()
substrate = null; idx = len(value)
namedTypes = value.componentType
substrate = null
idx = len(value)
while idx > 0: while idx > 0:
idx = idx - 1 idx -= 1
if value[idx] is None: # Optional component if namedTypes:
if namedTypes[idx].isOptional and not value[idx].isValue:
continue continue
component = value.getDefaultComponentByPosition(idx) if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
if component is not None and component == value[idx]:
continue continue
substrate = encodeFun( substrate = encodeFun(value[idx], **options) + substrate
value[idx], defMode, maxChunkSize
) + substrate return substrate, True, True
return substrate, 1
class SequenceOfEncoder(AbstractItemEncoder): class SequenceOfEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
value.verifySizeSpec() value.verifySizeSpec()
substrate = null; idx = len(value) substrate = null
idx = len(value)
while idx > 0: while idx > 0:
idx = idx - 1 idx -= 1
substrate = encodeFun( substrate = encodeFun(value[idx], **options) + substrate
value[idx], defMode, maxChunkSize return substrate, True, True
) + substrate
return substrate, 1
class ChoiceEncoder(AbstractItemEncoder): class ChoiceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
return encodeFun(value.getComponent(), defMode, maxChunkSize), 1 return encodeFun(value.getComponent(), **options), True, True
class AnyEncoder(OctetStringEncoder): class AnyEncoder(OctetStringEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
return value.asOctets(), defMode == 0 return value.asOctets(), not options.get('defMode', True), True
tagMap = { tagMap = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(), eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
@ -387,47 +432,117 @@ tagMap = {
useful.ObjectDescriptor.tagSet: OctetStringEncoder(), useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.tagSet: OctetStringEncoder(), useful.GeneralizedTime.tagSet: OctetStringEncoder(),
useful.UTCTime.tagSet: OctetStringEncoder() useful.UTCTime.tagSet: OctetStringEncoder()
} }
# Type-to-codec map for ambiguous ASN.1 types # Put in ambiguous & non-ambiguous types for faster codec lookup
typeMap = { typeMap = {
univ.Boolean.typeId: BooleanEncoder(),
univ.Integer.typeId: IntegerEncoder(),
univ.BitString.typeId: BitStringEncoder(),
univ.OctetString.typeId: OctetStringEncoder(),
univ.Null.typeId: NullEncoder(),
univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(),
univ.Enumerated.typeId: IntegerEncoder(),
univ.Real.typeId: RealEncoder(),
# Sequence & Set have same tags as SequenceOf & SetOf
univ.Set.typeId: SequenceEncoder(), univ.Set.typeId: SequenceEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(), univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(), univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(), univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(), univ.Choice.typeId: ChoiceEncoder(),
univ.Any.typeId: AnyEncoder() univ.Any.typeId: AnyEncoder(),
} # character string types
char.UTF8String.typeId: OctetStringEncoder(),
char.NumericString.typeId: OctetStringEncoder(),
char.PrintableString.typeId: OctetStringEncoder(),
char.TeletexString.typeId: OctetStringEncoder(),
char.VideotexString.typeId: OctetStringEncoder(),
char.IA5String.typeId: OctetStringEncoder(),
char.GraphicString.typeId: OctetStringEncoder(),
char.VisibleString.typeId: OctetStringEncoder(),
char.GeneralString.typeId: OctetStringEncoder(),
char.UniversalString.typeId: OctetStringEncoder(),
char.BMPString.typeId: OctetStringEncoder(),
# useful types
useful.ObjectDescriptor.typeId: OctetStringEncoder(),
useful.GeneralizedTime.typeId: OctetStringEncoder(),
useful.UTCTime.typeId: OctetStringEncoder()
}
class Encoder:
supportIndefLength = True class Encoder(object):
fixedDefLengthMode = None
fixedChunkSize = None
# noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap={}): def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap self.__tagMap = tagMap
self.__typeMap = typeMap self.__typeMap = typeMap
def __call__(self, value, defMode=True, maxChunkSize=0): def __call__(self, value, **options):
if not defMode and not self.supportIndefLength:
raise error.PyAsn1Error('Indefinite length encoding not supported by this codec') if debug.logger & debug.flagEncoder:
debug.logger & debug.flagEncoder and debug.logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not defMode and 'in' or '', maxChunkSize, value.prettyPrintType(), value.prettyPrint())) logger = debug.logger
tagSet = value.getTagSet()
if len(tagSet) > 1:
concreteEncoder = explicitlyTaggedItemEncoder
else: else:
if value.typeId is not None and value.typeId in self.__typeMap: logger = None
if logger:
logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not options.get('defMode', True) and 'in' or '', options.get('maxChunkSize', 0), value.prettyPrintType(), value.prettyPrint()))
if self.fixedDefLengthMode is not None:
options.update(defMode=self.fixedDefLengthMode)
if self.fixedChunkSize is not None:
options.update(maxChunkSize=self.fixedChunkSize)
tagSet = value.tagSet
try:
concreteEncoder = self.__typeMap[value.typeId] concreteEncoder = self.__typeMap[value.typeId]
elif tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet] except KeyError:
else: # use base type for codec lookup to recover untagged types
tagSet = value.baseTagSet baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
if tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet] try:
else: concreteEncoder = self.__tagMap[baseTagSet]
raise Error('No encoder for %s' % (value,))
debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet)) except KeyError:
substrate = concreteEncoder.encode( raise error.PyAsn1Error('No encoder for %s' % (value,))
self, value, defMode, maxChunkSize
) if logger:
debug.logger & debug.flagEncoder and debug.logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate))) logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
substrate = concreteEncoder.encode(value, self, **options)
if logger:
logger('codec %s built %s octets of substrate: %s\nencoder completed' % (concreteEncoder, len(substrate), debug.hexdump(substrate)))
return substrate return substrate
#: Turns ASN.1 object into BER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a BER octet stream.
#:
#: Parameters
#: ----------
# value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: A pyasn1 object to encode
#:
#: defMode: :py:class:`bool`
#: If `False`, produces indefinite length encoding
#:
#: maxChunkSize: :py:class:`int`
#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
#:
#: Returns
#: -------
#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: Given ASN.1 object encoded into BER octetstream
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap) encode = Encoder(tagMap, typeMap)

View File

@ -1,8 +1,25 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import base, tag from pyasn1.type import base, tag
class EndOfOctets(base.AbstractSimpleAsn1Item): class EndOfOctets(base.AbstractSimpleAsn1Item):
defaultValue = 0 defaultValue = 0
tagSet = tag.initTagSet( tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
) )
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
endOfOctets = EndOfOctets() endOfOctets = EndOfOctets()

View File

@ -1,13 +1,24 @@
# CER decoder #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ from pyasn1.type import univ
from pyasn1.codec.ber import decoder from pyasn1.codec.ber import decoder
from pyasn1.compat.octets import oct2int from pyasn1.compat.octets import oct2int
from pyasn1 import error from pyasn1 import error
__all__ = ['decode']
class BooleanDecoder(decoder.AbstractSimpleDecoder): class BooleanDecoder(decoder.AbstractSimpleDecoder):
protoComponent = univ.Boolean(0) protoComponent = univ.Boolean(0)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun): def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
head, tail = substrate[:length], substrate[length:] head, tail = substrate[:length], substrate[length:]
if not head or length != 1: if not head or length != 1:
raise error.PyAsn1Error('Not single-octet Boolean payload') raise error.PyAsn1Error('Not single-octet Boolean payload')
@ -23,13 +34,57 @@ class BooleanDecoder(decoder.AbstractSimpleDecoder):
raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte) raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
return self._createComponent(asn1Spec, tagSet, value), tail return self._createComponent(asn1Spec, tagSet, value), tail
# TODO: prohibit non-canonical encoding
BitStringDecoder = decoder.BitStringDecoder
OctetStringDecoder = decoder.OctetStringDecoder
RealDecoder = decoder.RealDecoder
tagMap = decoder.tagMap.copy() tagMap = decoder.tagMap.copy()
tagMap.update({ tagMap.update(
univ.Boolean.tagSet: BooleanDecoder() {univ.Boolean.tagSet: BooleanDecoder(),
}) univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: OctetStringDecoder(),
univ.Real.tagSet: RealDecoder()}
)
typeMap = decoder.typeMap typeMap = decoder.typeMap.copy()
class Decoder(decoder.Decoder): pass # Put in non-ambiguous types for faster codec lookup
for typeDecoder in tagMap.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
if typeId is not None and typeId not in typeMap:
typeMap[typeId] = typeDecoder
class Decoder(decoder.Decoder):
pass
#: Turns CER octet stream into an ASN.1 object.
#:
#: Takes CER octetstream and decode it into an ASN.1 object
#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
#: may be a scalar or an arbitrary nested structure.
#:
#: Parameters
#: ----------
#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: CER octetstream
#:
#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
#: being decoded, *asn1Spec* may or may not be required. Most common reason for
#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
#:
#: Returns
#: -------
#: : :py:class:`tuple`
#: A tuple of pyasn1 object recovered from CER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: and the unprocessed trailing portion of the *substrate* (may be empty)
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On decoding errors
decode = Decoder(tagMap, decoder.typeMap) decode = Decoder(tagMap, decoder.typeMap)

View File

@ -1,130 +1,221 @@
# CER encoder #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ from pyasn1.type import univ
from pyasn1.type import useful from pyasn1.type import useful
from pyasn1.codec.ber import encoder from pyasn1.codec.ber import encoder
from pyasn1.compat.octets import int2oct, str2octs, null from pyasn1.compat.octets import str2octs, null
from pyasn1 import error from pyasn1 import error
__all__ = ['encode']
class BooleanEncoder(encoder.IntegerEncoder): class BooleanEncoder(encoder.IntegerEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): def encodeValue(self, value, encodeFun, **options):
if client == 0: if value == 0:
substrate = int2oct(0) substrate = (0,)
else: else:
substrate = int2oct(255) substrate = (255,)
return substrate, 0 return substrate, False, False
class BitStringEncoder(encoder.BitStringEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
return encoder.BitStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class OctetStringEncoder(encoder.OctetStringEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class RealEncoder(encoder.RealEncoder): class RealEncoder(encoder.RealEncoder):
def _chooseEncBase(self, value): def _chooseEncBase(self, value):
m, b, e = value m, b, e = value
return self._dropFloatingPoint(m, b, e) return self._dropFloatingPoint(m, b, e)
# specialized GeneralStringEncoder here # specialized GeneralStringEncoder here
class GeneralizedTimeEncoder(OctetStringEncoder): class TimeEncoderMixIn(object):
zchar = str2octs('Z') zchar, = str2octs('Z')
pluschar = str2octs('+') pluschar, = str2octs('+')
minuschar = str2octs('-') minuschar, = str2octs('-')
zero = str2octs('0') commachar, = str2octs(',')
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): minLength = 12
octets = client.asOctets() maxLength = 19
# This breaks too many existing data items
# if '.' not in octets: def encodeValue(self, value, encodeFun, **options):
# raise error.PyAsn1Error('Format must include fraction of second: %r' % octets) # Encoding constraints:
if len(octets) < 15: # - minutes are mandatory, seconds are optional
raise error.PyAsn1Error('Bad UTC time length: %r' % octets) # - subseconds must NOT be zero
# - no hanging fraction dot
# - time in UTC (Z)
# - only dot is allowed for fractions
octets = value.asOctets()
if not self.minLength < len(octets) < self.maxLength:
raise error.PyAsn1Error('Length constraint violated: %r' % value)
if self.pluschar in octets or self.minuschar in octets: if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets) raise error.PyAsn1Error('Must be UTC time: %r' % octets)
if octets[-1] != self.zchar[0]:
raise error.PyAsn1Error('Missing timezone specifier: %r' % octets) if octets[-1] != self.zchar:
raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % octets)
if self.commachar in octets:
raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
options.update(maxChunkSize=1000)
return encoder.OctetStringEncoder.encodeValue( return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000 self, value, encodeFun, **options
) )
class UTCTimeEncoder(encoder.OctetStringEncoder):
zchar = str2octs('Z') class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
pluschar = str2octs('+') minLength = 12
minuschar = str2octs('-') maxLength = 19
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
octets = client.asOctets()
if self.pluschar in octets or self.minuschar in octets: class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
raise error.PyAsn1Error('Must be UTC time: %r' % octets) minLength = 10
if octets and octets[-1] != self.zchar[0]: maxLength = 14
client = client.clone(octets + self.zchar)
if len(client) != 13:
raise error.PyAsn1Error('Bad UTC time length: %r' % client)
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class SetOfEncoder(encoder.SequenceOfEncoder): class SetOfEncoder(encoder.SequenceOfEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): @staticmethod
if isinstance(client, univ.SequenceAndSetBase): def _sortComponents(components):
client.setDefaultComponents() # sort by tags regardless of the Choice value (static sort)
client.verifySizeSpec() return sorted(components, key=lambda x: isinstance(x, univ.Choice) and x.minTagSet or x.tagSet)
substrate = null; idx = len(client)
# This is certainly a hack but how else do I distinguish SetOf def encodeValue(self, value, encodeFun, **options):
# from Set if they have the same tags&constraints? value.verifySizeSpec()
if isinstance(client, univ.SequenceAndSetBase):
# Set
comps = []
while idx > 0:
idx = idx - 1
if client[idx] is None: # Optional component
continue
if client.getDefaultComponentByPosition(idx) == client[idx]:
continue
comps.append(client[idx])
comps.sort(key=lambda x: isinstance(x, univ.Choice) and \
x.getMinTagSet() or x.getTagSet())
for c in comps:
substrate += encodeFun(c, defMode, maxChunkSize)
else:
# SetOf
compSubs = []
while idx > 0:
idx = idx - 1
compSubs.append(
encodeFun(client[idx], defMode, maxChunkSize)
)
compSubs.sort() # perhaps padding's not needed
substrate = null substrate = null
for compSub in compSubs: idx = len(value)
substrate += compSub if value.typeId == univ.Set.typeId:
return substrate, 1 namedTypes = value.componentType
comps = []
compsMap = {}
while idx > 0:
idx -= 1
if namedTypes:
if namedTypes[idx].isOptional and not value[idx].isValue:
continue
if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
continue
comps.append(value[idx])
compsMap[id(value[idx])] = namedTypes and namedTypes[idx].isOptional
for comp in self._sortComponents(comps):
options.update(ifNotEmpty=compsMap[id(comp)])
substrate += encodeFun(comp, **options)
else:
components = [encodeFun(x, **options) for x in value]
# sort by serialized 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):
def encodeValue(self, value, encodeFun, **options):
value.verifySizeSpec()
namedTypes = value.componentType
substrate = null
idx = len(value)
while idx > 0:
idx -= 1
if namedTypes:
if namedTypes[idx].isOptional and not value[idx].isValue:
continue
if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
continue
options.update(ifNotEmpty=namedTypes and namedTypes[idx].isOptional)
substrate = encodeFun(value[idx], **options) + substrate
return substrate, True, True
class SequenceOfEncoder(encoder.SequenceOfEncoder):
def encodeValue(self, value, encodeFun, **options):
substrate = null
idx = len(value)
if options.get('ifNotEmpty', False) and not idx:
return substrate, True, True
value.verifySizeSpec()
while idx > 0:
idx -= 1
substrate = encodeFun(value[idx], **options) + substrate
return substrate, True, True
tagMap = encoder.tagMap.copy() tagMap = encoder.tagMap.copy()
tagMap.update({ tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(), univ.Boolean.tagSet: BooleanEncoder(),
univ.BitString.tagSet: BitStringEncoder(),
univ.OctetString.tagSet: OctetStringEncoder(),
univ.Real.tagSet: RealEncoder(), univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(), useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(), 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() typeMap = encoder.typeMap.copy()
typeMap.update({ typeMap.update({
univ.Boolean.typeId: BooleanEncoder(),
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.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder() univ.SetOf.typeId: SetOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder()
}) })
class Encoder(encoder.Encoder):
def __call__(self, client, defMode=False, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
class Encoder(encoder.Encoder):
fixedDefLengthMode = False
fixedChunkSize = 1000
#: Turns ASN.1 object into CER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a CER octet stream.
#:
#: Parameters
#: ----------
# value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: A pyasn1 object to encode
#:
#: defMode: :py:class:`bool`
#: If `False`, produces indefinite length encoding
#:
#: maxChunkSize: :py:class:`int`
#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
#:
#: Returns
#: -------
#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: Given ASN.1 object encoded into BER octetstream
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap) encode = Encoder(tagMap, typeMap)
# EncoderFactory queries class instance and builds a map of tags -> encoders # EncoderFactory queries class instance and builds a map of tags -> encoders

View File

@ -1,9 +1,70 @@
# DER decoder #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ
from pyasn1.codec.cer import decoder from pyasn1.codec.cer import decoder
tagMap = decoder.tagMap __all__ = ['decode']
typeMap = decoder.typeMap
class BitStringDecoder(decoder.BitStringDecoder):
supportConstructedForm = False
class OctetStringDecoder(decoder.OctetStringDecoder):
supportConstructedForm = False
# TODO: prohibit non-canonical encoding
RealDecoder = decoder.RealDecoder
tagMap = decoder.tagMap.copy()
tagMap.update(
{univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: OctetStringDecoder(),
univ.Real.tagSet: RealDecoder()}
)
typeMap = decoder.typeMap.copy()
# Put in non-ambiguous types for faster codec lookup
for typeDecoder in tagMap.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
if typeId is not None and typeId not in typeMap:
typeMap[typeId] = typeDecoder
class Decoder(decoder.Decoder): class Decoder(decoder.Decoder):
supportIndefLength = False supportIndefLength = False
#: Turns DER octet stream into an ASN.1 object.
#:
#: Takes DER octetstream and decode it into an ASN.1 object
#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
#: may be a scalar or an arbitrary nested structure.
#:
#: Parameters
#: ----------
#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: DER octetstream
#:
#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
#: being decoded, *asn1Spec* may or may not be required. Most common reason for
#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
#:
#: Returns
#: -------
#: : :py:class:`tuple`
#: A tuple of pyasn1 object recovered from DER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: and the unprocessed trailing portion of the *substrate* (may be empty)
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On decoding errors
decode = Decoder(tagMap, typeMap) decode = Decoder(tagMap, typeMap)

View File

@ -1,32 +1,62 @@
# DER encoder #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ from pyasn1.type import univ
from pyasn1.codec.cer import encoder from pyasn1.codec.cer import encoder
from pyasn1 import error
__all__ = ['encode']
class SetOfEncoder(encoder.SetOfEncoder): class SetOfEncoder(encoder.SetOfEncoder):
def _cmpSetComponents(self, c1, c2): @staticmethod
tagSet1 = isinstance(c1, univ.Choice) and \ def _sortComponents(components):
c1.getEffectiveTagSet() or c1.getTagSet() # sort by tags depending on the actual Choice value (dynamic sort)
tagSet2 = isinstance(c2, univ.Choice) and \ return sorted(components, key=lambda x: isinstance(x, univ.Choice) and x.getComponent().tagSet or x.tagSet)
c2.getEffectiveTagSet() or c2.getTagSet()
return cmp(tagSet1, tagSet2)
tagMap = encoder.tagMap.copy() tagMap = encoder.tagMap.copy()
tagMap.update({ tagMap.update({
# 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 # Set & SetOf have same tags
univ.SetOf().tagSet: SetOfEncoder() univ.SetOf.tagSet: SetOfEncoder()
})
typeMap = encoder.typeMap.copy()
typeMap.update({
# Set & SetOf have same tags
univ.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder()
}) })
typeMap = encoder.typeMap
class Encoder(encoder.Encoder): class Encoder(encoder.Encoder):
supportIndefLength = False fixedDefLengthMode = True
def __call__(self, client, defMode=True, maxChunkSize=0): fixedChunkSize = 0
if not defMode:
raise error.PyAsn1Error('DER forbids indefinite length mode')
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
#: Turns ASN.1 object into DER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a DER octet stream.
#:
#: Parameters
#: ----------
# value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: A pyasn1 object to encode
#:
#: defMode: :py:class:`bool`
#: If `False`, produces indefinite length encoding
#:
#: maxChunkSize: :py:class:`int`
#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
#:
#: Returns
#: -------
#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: Given ASN.1 object encoded into BER octetstream
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap) encode = Encoder(tagMap, typeMap)

View File

@ -0,0 +1 @@
# This file is necessary to make this directory a package.

View File

@ -0,0 +1,194 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import base, univ, char, useful, tag
from pyasn1 import debug, error
__all__ = ['decode']
class AbstractScalarDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
return asn1Spec.clone(pyObject)
class BitStringDecoder(AbstractScalarDecoder):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject))
class SequenceOrSetDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
componentsTypes = asn1Spec.componentType
for field in asn1Value:
if field in pyObject:
asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
return asn1Value
class SequenceOfOrSetOfDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
for pyValue in pyObject:
asn1Value.append(decodeFun(pyValue, asn1Spec.componentType), **options)
return asn1Value
class ChoiceDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
componentsTypes = asn1Spec.componentType
for field in pyObject:
if field in componentsTypes:
asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
break
return asn1Value
tagMap = {
univ.Integer.tagSet: AbstractScalarDecoder(),
univ.Boolean.tagSet: AbstractScalarDecoder(),
univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: AbstractScalarDecoder(),
univ.Null.tagSet: AbstractScalarDecoder(),
univ.ObjectIdentifier.tagSet: AbstractScalarDecoder(),
univ.Enumerated.tagSet: AbstractScalarDecoder(),
univ.Real.tagSet: AbstractScalarDecoder(),
univ.Sequence.tagSet: SequenceOrSetDecoder(), # conflicts with SequenceOf
univ.Set.tagSet: SequenceOrSetDecoder(), # conflicts with SetOf
univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
# character string types
char.UTF8String.tagSet: AbstractScalarDecoder(),
char.NumericString.tagSet: AbstractScalarDecoder(),
char.PrintableString.tagSet: AbstractScalarDecoder(),
char.TeletexString.tagSet: AbstractScalarDecoder(),
char.VideotexString.tagSet: AbstractScalarDecoder(),
char.IA5String.tagSet: AbstractScalarDecoder(),
char.GraphicString.tagSet: AbstractScalarDecoder(),
char.VisibleString.tagSet: AbstractScalarDecoder(),
char.GeneralString.tagSet: AbstractScalarDecoder(),
char.UniversalString.tagSet: AbstractScalarDecoder(),
char.BMPString.tagSet: AbstractScalarDecoder(),
# useful types
useful.ObjectDescriptor.tagSet: AbstractScalarDecoder(),
useful.GeneralizedTime.tagSet: AbstractScalarDecoder(),
useful.UTCTime.tagSet: AbstractScalarDecoder()
}
# Put in ambiguous & non-ambiguous types for faster codec lookup
typeMap = {
univ.Integer.typeId: AbstractScalarDecoder(),
univ.Boolean.typeId: AbstractScalarDecoder(),
univ.BitString.typeId: BitStringDecoder(),
univ.OctetString.typeId: AbstractScalarDecoder(),
univ.Null.typeId: AbstractScalarDecoder(),
univ.ObjectIdentifier.typeId: AbstractScalarDecoder(),
univ.Enumerated.typeId: AbstractScalarDecoder(),
univ.Real.typeId: AbstractScalarDecoder(),
# ambiguous base types
univ.Set.typeId: SequenceOrSetDecoder(),
univ.SetOf.typeId: SequenceOfOrSetOfDecoder(),
univ.Sequence.typeId: SequenceOrSetDecoder(),
univ.SequenceOf.typeId: SequenceOfOrSetOfDecoder(),
univ.Choice.typeId: ChoiceDecoder(),
univ.Any.typeId: AbstractScalarDecoder(),
# character string types
char.UTF8String.typeId: AbstractScalarDecoder(),
char.NumericString.typeId: AbstractScalarDecoder(),
char.PrintableString.typeId: AbstractScalarDecoder(),
char.TeletexString.typeId: AbstractScalarDecoder(),
char.VideotexString.typeId: AbstractScalarDecoder(),
char.IA5String.typeId: AbstractScalarDecoder(),
char.GraphicString.typeId: AbstractScalarDecoder(),
char.VisibleString.typeId: AbstractScalarDecoder(),
char.GeneralString.typeId: AbstractScalarDecoder(),
char.UniversalString.typeId: AbstractScalarDecoder(),
char.BMPString.typeId: AbstractScalarDecoder(),
# useful types
useful.ObjectDescriptor.typeId: AbstractScalarDecoder(),
useful.GeneralizedTime.typeId: AbstractScalarDecoder(),
useful.UTCTime.typeId: AbstractScalarDecoder()
}
class Decoder(object):
# noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap):
self.__tagMap = tagMap
self.__typeMap = typeMap
def __call__(self, pyObject, asn1Spec, **options):
if debug.logger & debug.flagDecoder:
logger = debug.logger
else:
logger = None
if logger:
debug.scope.push(type(pyObject).__name__)
logger('decoder called at scope %s, working with type %s' % (debug.scope, type(pyObject).__name__))
if asn1Spec is None or not isinstance(asn1Spec, base.Asn1Item):
raise error.PyAsn1Error('asn1Spec is not valid (should be an instance of an ASN.1 Item, not %s)' % asn1Spec.__class__.__name__)
try:
valueDecoder = self.__typeMap[asn1Spec.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
baseTagSet = tag.TagSet(asn1Spec.tagSet.baseTag, asn1Spec.tagSet.baseTag)
try:
valueDecoder = self.__tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('Unknown ASN.1 tag %s' % asn1Spec.tagSet)
if logger:
logger('calling decoder %s on Python type %s <%s>' % (type(valueDecoder).__name__, type(pyObject).__name__, repr(pyObject)))
value = valueDecoder(pyObject, asn1Spec, self, **options)
if logger:
logger('decoder %s produced ASN.1 type %s <%s>' % (type(valueDecoder).__name__, type(value).__name__, repr(value)))
debug.scope.pop()
return value
#: Turns Python objects of built-in types into ASN.1 objects.
#:
#: Takes Python objects of built-in types and turns them into a tree of
#: ASN.1 objects (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
#: may be a scalar or an arbitrary nested structure.
#:
#: Parameters
#: ----------
#: pyObject: :py:class:`object`
#: A scalar or nested Python objects
#:
#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A pyasn1 type object to act as a template guiding the decoder. It is required
#: for successful interpretation of Python objects mapping into their ASN.1
#: representations.
#:
#: Returns
#: -------
#: : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A scalar or constructed pyasn1 object
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On decoding errors
decode = Decoder(tagMap, typeMap)

View File

@ -0,0 +1,212 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
try:
from collections import OrderedDict
except ImportError:
OrderedDict = dict
from pyasn1.type import base, univ, tag, char, useful
from pyasn1 import debug, error
__all__ = ['encode']
class AbstractItemEncoder(object):
def encode(self, value, encodeFun, **options):
raise error.PyAsn1Error('Not implemented')
class BooleanEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return bool(value)
class IntegerEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return int(value)
class BitStringEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return str(value)
class OctetStringEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return value.asOctets()
class TextStringEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return value.prettyPrint()
class NullEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return None
class ObjectIdentifierEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return str(value)
class RealEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return float(value)
class SetEncoder(AbstractItemEncoder):
protoDict = dict
def encode(self, value, encodeFun, **options):
value.verifySizeSpec()
namedTypes = value.componentType
substrate = self.protoDict()
for idx, (key, subValue) in enumerate(value.items()):
if namedTypes and namedTypes[idx].isOptional and not value[idx].isValue:
continue
substrate[key] = encodeFun(subValue, **options)
return substrate
class SequenceEncoder(SetEncoder):
protoDict = OrderedDict
class SequenceOfEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
value.verifySizeSpec()
return [encodeFun(x, **options) for x in value]
class ChoiceEncoder(SequenceEncoder):
pass
class AnyEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return value.asOctets()
tagMap = {
univ.Boolean.tagSet: BooleanEncoder(),
univ.Integer.tagSet: IntegerEncoder(),
univ.BitString.tagSet: BitStringEncoder(),
univ.OctetString.tagSet: OctetStringEncoder(),
univ.Null.tagSet: NullEncoder(),
univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
univ.Enumerated.tagSet: IntegerEncoder(),
univ.Real.tagSet: RealEncoder(),
# Sequence & Set have same tags as SequenceOf & SetOf
univ.SequenceOf.tagSet: SequenceOfEncoder(),
univ.SetOf.tagSet: SequenceOfEncoder(),
univ.Choice.tagSet: ChoiceEncoder(),
# character string types
char.UTF8String.tagSet: TextStringEncoder(),
char.NumericString.tagSet: TextStringEncoder(),
char.PrintableString.tagSet: TextStringEncoder(),
char.TeletexString.tagSet: TextStringEncoder(),
char.VideotexString.tagSet: TextStringEncoder(),
char.IA5String.tagSet: TextStringEncoder(),
char.GraphicString.tagSet: TextStringEncoder(),
char.VisibleString.tagSet: TextStringEncoder(),
char.GeneralString.tagSet: TextStringEncoder(),
char.UniversalString.tagSet: TextStringEncoder(),
char.BMPString.tagSet: TextStringEncoder(),
# useful types
useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.tagSet: OctetStringEncoder(),
useful.UTCTime.tagSet: OctetStringEncoder()
}
# Type-to-codec map for ambiguous ASN.1 types
typeMap = {
univ.Set.typeId: SetEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(),
univ.Any.typeId: AnyEncoder()
}
class Encoder(object):
# noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap
def __call__(self, value, **options):
if not isinstance(value, base.Asn1Item):
raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
if debug.logger & debug.flagEncoder:
logger = debug.logger
else:
logger = None
if logger:
debug.scope.push(type(value).__name__)
logger('encoder called for type %s <%s>' % (type(value).__name__, value.prettyPrint()))
tagSet = value.tagSet
try:
concreteEncoder = self.__typeMap[value.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
try:
concreteEncoder = self.__tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('No encoder for %s' % (value,))
if logger:
logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
pyObject = concreteEncoder.encode(value, self, **options)
if logger:
logger('encoder %s produced: %s' % (type(concreteEncoder).__name__, repr(pyObject)))
debug.scope.pop()
return pyObject
#: Turns ASN.1 object into a Python built-in type object(s).
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a Python built-in type or a tree
#: of those.
#:
#: One exception is that instead of :py:class:`dict`, the :py:class:`OrderedDict`
#: can be produced (whenever available) to preserve ordering of the components
#: in ASN.1 SEQUENCE.
#:
#: Parameters
#: ----------
# asn1Value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: pyasn1 object to encode (or a tree of them)
#:
#: Returns
#: -------
#: : :py:class:`object`
#: Python built-in type instance (or a tree of them)
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap)

View File

@ -1,10 +1,33 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info from sys import version_info
if version_info[0:2] < (2, 6): if version_info[0:2] < (2, 6):
def bin(x): def bin(value):
if x <= 1: bitstring = []
return '0b'+str(x)
if value > 0:
prefix = '0b'
elif value < 0:
prefix = '-0b'
value = abs(value)
else: else:
return bin(x>>1) + str(x&1) prefix = '0b0'
while value:
if value & 1 == 1:
bitstring.append('1')
else:
bitstring.append('0')
value >>= 1
bitstring.reverse()
return prefix + ''.join(bitstring)
else: else:
bin = bin bin = bin

View File

@ -0,0 +1,20 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
__all__ = ['callable']
if (2, 7) < version_info[:2] < (3, 2):
import collections
def callable(x):
return isinstance(x, collections.Callable)
else:
callable = callable

View File

@ -0,0 +1,22 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
from datetime import datetime
import time
__all__ = ['strptime']
if version_info[:2] <= (2, 4):
def strptime(text, dateFormat):
return datetime(*(time.strptime(text, dateFormat)[0:6]))
else:
def strptime(text, dateFormat):
return datetime.strptime(text, dateFormat)

View File

@ -0,0 +1,108 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys
try:
import platform
implementation = platform.python_implementation()
except (ImportError, AttributeError):
implementation = 'CPython'
from pyasn1.compat.octets import oct2int, null, ensureString
if sys.version_info[0:2] < (3, 2) or implementation != 'CPython':
from binascii import a2b_hex, b2a_hex
if sys.version_info[0] > 2:
long = int
def from_bytes(octets, signed=False):
if not octets:
return 0
value = long(b2a_hex(ensureString(octets)), 16)
if signed and oct2int(octets[0]) & 0x80:
return value - (1 << len(octets) * 8)
return value
def to_bytes(value, signed=False, length=0):
if value < 0:
if signed:
bits = bitLength(value)
# two's complement form
maxValue = 1 << bits
valueToEncode = (value + maxValue) % maxValue
else:
raise OverflowError('can\'t convert negative int to unsigned')
elif value == 0 and length == 0:
return null
else:
bits = 0
valueToEncode = value
hexValue = hex(valueToEncode)[2:]
if hexValue.endswith('L'):
hexValue = hexValue[:-1]
if len(hexValue) & 1:
hexValue = '0' + hexValue
# padding may be needed for two's complement encoding
if value != valueToEncode or length:
hexLength = len(hexValue) * 4
padLength = max(length, bits)
if padLength > hexLength:
hexValue = '00' * ((padLength - hexLength - 1) // 8 + 1) + hexValue
elif length and hexLength - length > 7:
raise OverflowError('int too big to convert')
firstOctet = int(hexValue[:2], 16)
if signed:
if firstOctet & 0x80:
if value >= 0:
hexValue = '00' + hexValue
elif value < 0:
hexValue = 'ff' + hexValue
octets_value = a2b_hex(hexValue)
return octets_value
def bitLength(number):
# bits in unsigned number
hexValue = hex(abs(number))
bits = len(hexValue) - 2
if hexValue.endswith('L'):
bits -= 1
if bits & 1:
bits += 1
bits *= 4
# TODO: strip lhs zeros
return bits
else:
def from_bytes(octets, signed=False):
return int.from_bytes(bytes(octets), 'big', signed=signed)
def to_bytes(value, signed=False, length=0):
length = max(value.bit_length(), length)
if signed and length % 8 == 0:
length += 1
return value.to_bytes(length // 8 + (length % 8 and 1 or 0), 'big', signed=signed)
def bitLength(number):
return int(number).bit_length()

View File

@ -1,22 +1,46 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info from sys import version_info
if version_info[0] <= 2: if version_info[0] <= 2:
int2oct = chr int2oct = chr
ints2octs = lambda s: ''.join([ int2oct(x) for x in s ]) # noinspection PyPep8
ints2octs = lambda s: ''.join([int2oct(x) for x in s])
null = '' null = ''
oct2int = ord oct2int = ord
octs2ints = lambda s: [ oct2int(x) for x in s ] # TODO: refactor to return a sequence of ints
# noinspection PyPep8
octs2ints = lambda s: [oct2int(x) for x in s]
# noinspection PyPep8
str2octs = lambda x: x str2octs = lambda x: x
# noinspection PyPep8
octs2str = lambda x: x octs2str = lambda x: x
# noinspection PyPep8
isOctetsType = lambda s: isinstance(s, str) isOctetsType = lambda s: isinstance(s, str)
# noinspection PyPep8
isStringType = lambda s: isinstance(s, (str, unicode)) isStringType = lambda s: isinstance(s, (str, unicode))
# noinspection PyPep8
ensureString = str
else: else:
ints2octs = bytes ints2octs = bytes
# noinspection PyPep8
int2oct = lambda x: ints2octs((x,)) int2oct = lambda x: ints2octs((x,))
null = ints2octs() null = ints2octs()
# noinspection PyPep8
oct2int = lambda x: x oct2int = lambda x: x
octs2ints = lambda s: [ x for x in s ] # noinspection PyPep8
str2octs = lambda x: x.encode() octs2ints = lambda x: x
octs2str = lambda x: x.decode() # noinspection PyPep8
str2octs = lambda x: x.encode('iso-8859-1')
# noinspection PyPep8
octs2str = lambda x: x.decode('iso-8859-1')
# noinspection PyPep8
isOctetsType = lambda s: isinstance(s, bytes) isOctetsType = lambda s: isinstance(s, bytes)
# noinspection PyPep8
isStringType = lambda s: isinstance(s, str) isStringType = lambda s: isinstance(s, str)
# noinspection PyPep8
ensureString = bytes

View File

@ -0,0 +1,26 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
if version_info[:2] <= (2, 5):
def partition(string, sep):
try:
a, c = string.split(sep, 1)
except ValueError:
a, b, c = string, '', ''
else:
b = sep
return a, b, c
else:
def partition(string, sep):
return string.partition(sep)

View File

@ -1,9 +1,16 @@
import time #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import logging import logging
from pyasn1.compat.octets import octs2ints from pyasn1.compat.octets import octs2ints
from pyasn1 import error from pyasn1 import error
from pyasn1 import __version__ from pyasn1 import __version__
__all__ = ['Debug', 'setLogger', 'hexdump']
flagNone = 0x0000 flagNone = 0x0000
flagEncoder = 0x0001 flagEncoder = 0x0001
flagDecoder = 0x0002 flagDecoder = 0x0002
@ -13,63 +20,80 @@ flagMap = {
'encoder': flagEncoder, 'encoder': flagEncoder,
'decoder': flagDecoder, 'decoder': flagDecoder,
'all': flagAll 'all': flagAll
} }
class Printer:
class Printer(object):
# noinspection PyShadowingNames
def __init__(self, logger=None, handler=None, formatter=None): def __init__(self, logger=None, handler=None, formatter=None):
if logger is None: if logger is None:
logger = logging.getLogger('pyasn1') logger = logging.getLogger('pyasn1')
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
if handler is None: if handler is None:
handler = logging.StreamHandler() handler = logging.StreamHandler()
if formatter is None: if formatter is None:
formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s') formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
handler.setFormatter(formatter) handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG) handler.setLevel(logging.DEBUG)
logger.addHandler(handler) logger.addHandler(handler)
self.__logger = logger self.__logger = logger
def __call__(self, msg): self.__logger.debug(msg) def __call__(self, msg):
def __str__(self): return '<python built-in logging>' self.__logger.debug(msg)
def __str__(self):
return '<python logging>'
if hasattr(logging, 'NullHandler'): if hasattr(logging, 'NullHandler'):
NullHandler = logging.NullHandler NullHandler = logging.NullHandler
else: else:
# Python 2.6 and older # Python 2.6 and older
class NullHandler(logging.Handler): class NullHandler(logging.Handler):
def emit(self, record): def emit(self, record):
pass pass
class Debug:
defaultPrinter = None class Debug(object):
defaultPrinter = Printer()
def __init__(self, *flags, **options): def __init__(self, *flags, **options):
self._flags = flagNone self._flags = flagNone
if options.get('printer') is not None:
self._printer = options.get('printer')
elif self.defaultPrinter is not None:
self._printer = self.defaultPrinter
if 'loggerName' in options: if 'loggerName' in options:
# route our logs to parent logger # route our logs to parent logger
self._printer = Printer( self._printer = Printer(
logger=logging.getLogger(options['loggerName']), logger=logging.getLogger(options['loggerName']),
handler=NullHandler() handler=NullHandler()
) )
elif 'printer' in options:
self._printer = options.get('printer')
else: else:
self._printer = Printer() self._printer = self.defaultPrinter
self('running pyasn1 version %s' % __version__)
for f in flags: self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags)))
inverse = f and f[0] in ('!', '~')
for flag in flags:
inverse = flag and flag[0] in ('!', '~')
if inverse: if inverse:
f = f[1:] flag = flag[1:]
try: try:
if inverse: if inverse:
self._flags &= ~flagMap[f] self._flags &= ~flagMap[flag]
else: else:
self._flags |= flagMap[f] self._flags |= flagMap[flag]
except KeyError: except KeyError:
raise error.PyAsn1Error('bad debug flag %s' % f) raise error.PyAsn1Error('bad debug flag %s' % flag)
self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled')) self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled'))
def __str__(self): def __str__(self):
return 'logger %s, flags %x' % (self._printer, self._flags) return 'logger %s, flags %x' % (self._printer, self._flags)
@ -83,19 +107,27 @@ class Debug:
def __rand__(self, flag): def __rand__(self, flag):
return flag & self._flags return flag & self._flags
logger = 0 logger = 0
def setLogger(l):
def setLogger(userLogger):
global logger global logger
logger = l
if userLogger:
logger = userLogger
else:
logger = 0
def hexdump(octets): def hexdump(octets):
return ' '.join( return ' '.join(
[ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x) ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
for n,x in zip(range(len(octets)), octs2ints(octets)) ] for n, x in zip(range(len(octets)), octs2ints(octets))]
) )
class Scope:
class Scope(object):
def __init__(self): def __init__(self):
self._list = [] self._list = []
@ -107,4 +139,5 @@ class Scope:
def pop(self): def pop(self):
return self._list.pop() return self._list.pop()
scope = Scope() scope = Scope()

View File

@ -1,3 +1,18 @@
class PyAsn1Error(Exception): pass #
class ValueConstraintError(PyAsn1Error): pass # This file is part of pyasn1 software.
class SubstrateUnderrunError(PyAsn1Error): pass #
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
class PyAsn1Error(Exception):
pass
class ValueConstraintError(PyAsn1Error):
pass
class SubstrateUnderrunError(PyAsn1Error):
pass

View File

@ -1,151 +1,393 @@
# Base classes for ASN.1 types #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys import sys
from pyasn1.type import constraint, tagmap, tag from pyasn1.type import constraint, tagmap, tag
from pyasn1.compat import calling
from pyasn1 import error from pyasn1 import error
class Asn1Item: pass __all__ = ['Asn1Item', 'Asn1ItemBase', 'AbstractSimpleAsn1Item', 'AbstractConstructedAsn1Item']
class Asn1Item(object):
@classmethod
def getTypeId(cls, increment=1):
try:
Asn1Item._typeCounter += increment
except AttributeError:
Asn1Item._typeCounter = increment
return Asn1Item._typeCounter
class Asn1ItemBase(Asn1Item): class Asn1ItemBase(Asn1Item):
# Set of tags for this ASN.1 type #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
#: ASN.1 tag(s) associated with |ASN.1| type.
tagSet = tag.TagSet() tagSet = tag.TagSet()
# A list of constraint.Constraint instances for checking values #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
#: object imposing constraints on initialization values.
subtypeSpec = constraint.ConstraintsIntersection() subtypeSpec = constraint.ConstraintsIntersection()
# Used for ambiguous ASN.1 types identification # Disambiguation ASN.1 types identification
typeId = None typeId = None
def __init__(self, tagSet=None, subtypeSpec=None): def __init__(self, **kwargs):
if tagSet is None: readOnly = {
self._tagSet = self.tagSet 'tagSet': self.tagSet,
else: 'subtypeSpec': self.subtypeSpec
self._tagSet = tagSet }
if subtypeSpec is None:
self._subtypeSpec = self.subtypeSpec
else:
self._subtypeSpec = subtypeSpec
def _verifySubtypeSpec(self, value, idx=None): readOnly.update(kwargs)
try:
self._subtypeSpec(value, idx)
except error.PyAsn1Error:
c, i, t = sys.exc_info()
raise c('%s at %s' % (i, self.__class__.__name__))
def getSubtypeSpec(self): return self._subtypeSpec self.__dict__.update(readOnly)
def getTagSet(self): return self._tagSet self._readOnly = readOnly
def getEffectiveTagSet(self): return self._tagSet # used by untagged types
def getTagMap(self): return tagmap.TagMap({self._tagSet: self}) def __setattr__(self, name, value):
if name[0] != '_' and name in self._readOnly:
raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
self.__dict__[name] = value
@property
def readOnly(self):
return self._readOnly
@property
def effectiveTagSet(self):
"""For |ASN.1| type is equivalent to *tagSet*
"""
return self.tagSet # used by untagged types
@property
def tagMap(self):
"""Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
"""
return tagmap.TagMap({self.tagSet: self})
def isSameTypeWith(self, other, matchTags=True, matchConstraints=True): def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
return self is other or \ """Examine |ASN.1| type for equality with other ASN.1 type.
(not matchTags or \
self._tagSet == other.getTagSet()) and \ ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
(not matchConstraints or \ (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
self._subtypeSpec==other.getSubtypeSpec()) out ASN.1 types comparison.
No Python inheritance relationship between PyASN1 objects is considered.
Parameters
----------
other: a pyasn1 type object
Class instance representing ASN.1 type.
Returns
-------
: :class:`bool`
:class:`True` if *other* is |ASN.1| type,
:class:`False` otherwise.
"""
return (self is other or
(not matchTags or self.tagSet == other.tagSet) and
(not matchConstraints or self.subtypeSpec == other.subtypeSpec))
def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True): def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
"""Returns true if argument is a ASN1 subtype of ourselves""" """Examine |ASN.1| type for subtype relationship with other ASN.1 type.
return (not matchTags or \
self._tagSet.isSuperTagSetOf(other.getTagSet())) and \ ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
(not matchConstraints or \ (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
(self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec()))) out ASN.1 types comparison.
No Python inheritance relationship between PyASN1 objects is considered.
Parameters
----------
other: a pyasn1 type object
Class instance representing ASN.1 type.
Returns
-------
: :class:`bool`
:class:`True` if *other* is a subtype of |ASN.1| type,
:class:`False` otherwise.
"""
return (not matchTags or
(self.tagSet.isSuperTagSetOf(other.tagSet)) and
(not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
@staticmethod
def isNoValue(*values):
for value in values:
if value is not None and value is not noValue:
return False
return True
# backward compatibility
def getTagSet(self):
return self.tagSet
def getEffectiveTagSet(self):
return self.effectiveTagSet
def getTagMap(self):
return self.tagMap
def getSubtypeSpec(self):
return self.subtypeSpec
def hasValue(self):
return self.isValue
class NoValue(object):
"""Create a singleton instance of NoValue class.
NoValue object can be used as an initializer on PyASN1 type class
instantiation to represent ASN.1 type rather than ASN.1 data value.
No operations other than type comparison can be performed on
a PyASN1 type object.
"""
skipMethods = ('__getattribute__', '__getattr__', '__setattr__', '__delattr__',
'__class__', '__init__', '__del__', '__new__', '__repr__',
'__qualname__', '__objclass__', 'im_class', '__sizeof__')
_instance = None
def __new__(cls):
if cls._instance is None:
def getPlug(name):
def plug(self, *args, **kw):
raise error.PyAsn1Error('Uninitialized ASN.1 value ("%s" attribute looked up)' % name)
return plug
op_names = [name
for typ in (str, int, list, dict)
for name in dir(typ)
if (name not in cls.skipMethods and
name.startswith('__') and
name.endswith('__') and
calling.callable(getattr(typ, name)))]
for name in set(op_names):
setattr(cls, name, getPlug(name))
cls._instance = object.__new__(cls)
return cls._instance
class NoValue:
def __getattr__(self, attr): def __getattr__(self, attr):
raise error.PyAsn1Error('No value for %s()' % attr) if attr in self.skipMethods:
def __getitem__(self, i): raise AttributeError('attribute %s not present' % attr)
raise error.PyAsn1Error('No value') raise error.PyAsn1Error('No value for "%s"' % attr)
def __repr__(self): return '%s()' % self.__class__.__name__
def __repr__(self):
return '%s()' % self.__class__.__name__
noValue = NoValue() noValue = NoValue()
# Base class for "simple" ASN.1 objects. These are immutable. # Base class for "simple" ASN.1 objects. These are immutable.
class AbstractSimpleAsn1Item(Asn1ItemBase): class AbstractSimpleAsn1Item(Asn1ItemBase):
#: Default payload value
defaultValue = noValue defaultValue = noValue
def __init__(self, value=None, tagSet=None, subtypeSpec=None):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec) def __init__(self, value=noValue, **kwargs):
if value is None or value is noValue: Asn1ItemBase.__init__(self, **kwargs)
if value is noValue or value is None:
value = self.defaultValue value = self.defaultValue
if value is None or value is noValue:
self.__hashedValue = value = noValue
else: else:
value = self.prettyIn(value) value = self.prettyIn(value)
self._verifySubtypeSpec(value) try:
self.__hashedValue = hash(value) self.subtypeSpec(value)
except error.PyAsn1Error:
exType, exValue, exTb = sys.exc_info()
raise exType('%s at %s' % (exValue, self.__class__.__name__))
self._value = value self._value = value
self._len = None
def __repr__(self): def __repr__(self):
r = [] representation = []
if self._value is not self.defaultValue: if self._value is not self.defaultValue:
r.append(self.prettyOut(self._value)) representation.append(self.prettyOut(self._value))
if self._tagSet is not self.tagSet: if self.tagSet is not self.__class__.tagSet:
r.append('tagSet=%r' % (self._tagSet,)) representation.append('tagSet=%r' % (self.tagSet,))
if self._subtypeSpec is not self.subtypeSpec: if self.subtypeSpec is not self.__class__.subtypeSpec:
r.append('subtypeSpec=%r' % (self._subtypeSpec,)) representation.append('subtypeSpec=%r' % (self.subtypeSpec,))
return '%s(%s)' % (self.__class__.__name__, ', '.join(r)) return '%s(%s)' % (self.__class__.__name__, ', '.join(representation))
def __str__(self):
return str(self._value)
def __str__(self): return str(self._value)
def __eq__(self, other): def __eq__(self, other):
return self is other and True or self._value == other return self is other and True or self._value == other
def __ne__(self, other): return self._value != other
def __lt__(self, other): return self._value < other def __ne__(self, other):
def __le__(self, other): return self._value <= other return self._value != other
def __gt__(self, other): return self._value > other
def __ge__(self, other): return self._value >= other def __lt__(self, other):
return self._value < other
def __le__(self, other):
return self._value <= other
def __gt__(self, other):
return self._value > other
def __ge__(self, other):
return self._value >= other
if sys.version_info[0] <= 2: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._value) def __nonzero__(self):
return self._value and True or False
else: else:
def __bool__(self): return bool(self._value) def __bool__(self):
return self._value and True or False
def __hash__(self): def __hash__(self):
return self.__hashedValue is noValue and hash(noValue) or self.__hashedValue return hash(self._value)
def hasValue(self): @property
return not isinstance(self._value, NoValue) def isValue(self):
"""Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value.
def clone(self, value=None, tagSet=None, subtypeSpec=None): In other words, if *isValue* is `True`, then the ASN.1 object is
if value is None and tagSet is None and subtypeSpec is None: initialized.
Returns
-------
: :class:`bool`
:class:`True` if object represents ASN.1 value and type,
:class:`False` if object represents just ASN.1 type.
Note
----
There is an important distinction between PyASN1 type and value objects.
The PyASN1 type objects can only participate in ASN.1 type
operations (subtyping, comparison etc) and serve as a
blueprint for serialization codecs to resolve ambiguous types.
The PyASN1 value objects can additionally participate in most
of built-in Python operations.
"""
return self._value is not noValue
def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`tuple`, :class:`str` or |ASN.1| object
Initialization value to pass to new ASN.1 object instead of
inheriting one from the caller.
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing ASN.1 tag(s) to use in new object instead of inheriting from the caller
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing ASN.1 subtype constraint(s) to use in new object instead of inheriting from the caller
Returns
-------
:
new instance of |ASN.1| type/value
"""
if value is noValue or value is None:
if not kwargs:
return self return self
if value is None:
value = self._value
if tagSet is None:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
return self.__class__(value, tagSet, subtypeSpec)
def subtype(self, value=None, implicitTag=None, explicitTag=None,
subtypeSpec=None):
if value is None:
value = self._value value = self._value
initilaizers = self.readOnly.copy()
initilaizers.update(kwargs)
return self.__class__(value, **initilaizers)
def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`tuple`, :class:`str` or |ASN.1| object
Initialization value to pass to new ASN.1 object instead of
inheriting one from the caller.
implicitTag: :py:class:`~pyasn1.type.tag.Tag`
Implicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
explicitTag: :py:class:`~pyasn1.type.tag.Tag`
Explicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Add ASN.1 constraints object to one of the caller, then
use the result as new object's ASN.1 constraints.
Returns
-------
:
new instance of |ASN.1| type/value
"""
if value is noValue or value is None:
if not kwargs:
return self
value = self._value
initializers = self.readOnly.copy()
implicitTag = kwargs.pop('implicitTag', None)
if implicitTag is not None: if implicitTag is not None:
tagSet = self._tagSet.tagImplicitly(implicitTag) initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
elif explicitTag is not None:
tagSet = self._tagSet.tagExplicitly(explicitTag)
else:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
else:
subtypeSpec = subtypeSpec + self._subtypeSpec
return self.__class__(value, tagSet, subtypeSpec)
def prettyIn(self, value): return value explicitTag = kwargs.pop('explicitTag', None)
def prettyOut(self, value): return str(value) if explicitTag is not None:
initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
for arg, option in kwargs.items():
initializers[arg] += option
return self.__class__(value, **initializers)
def prettyIn(self, value):
return value
def prettyOut(self, value):
return str(value)
def prettyPrint(self, scope=0): def prettyPrint(self, scope=0):
if self.hasValue(): """Provide human-friendly printable object representation.
Returns
-------
: :class:`str`
human-friendly type and/or value representation.
"""
if self.isValue:
return self.prettyOut(self._value) return self.prettyOut(self._value)
else: else:
return '<no value>' return '<no value>'
# XXX Compatibility stub # XXX Compatibility stub
def prettyPrinter(self, scope=0): return self.prettyPrint(scope) def prettyPrinter(self, scope=0):
return self.prettyPrint(scope)
# noinspection PyUnusedLocal
def prettyPrintType(self, scope=0): def prettyPrintType(self, scope=0):
return '%s -> %s' % (self.getTagSet(), self.__class__.__name__) return '%s -> %s' % (self.tagSet, self.__class__.__name__)
# #
# Constructed types: # Constructed types:
@ -166,113 +408,194 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
# of types for Sequence/Set/Choice. # of types for Sequence/Set/Choice.
# #
def setupComponent():
"""Returns a sentinel value.
Indicates to a constructed type to set up its inner component so that it
can be referred to. This is useful in situation when you want to populate
descendants of a constructed type what requires being able to refer to
their parent types along the way.
Example
-------
>>> constructed['record'] = setupComponent()
>>> constructed['record']['scalar'] = 42
"""
return noValue
class AbstractConstructedAsn1Item(Asn1ItemBase): class AbstractConstructedAsn1Item(Asn1ItemBase):
#: If `True`, requires exact component type matching,
#: otherwise subtype relation is only enforced
strictConstraints = False
componentType = None componentType = None
sizeSpec = constraint.ConstraintsIntersection() sizeSpec = None
def __init__(self, componentType=None, tagSet=None,
subtypeSpec=None, sizeSpec=None): def __init__(self, **kwargs):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec) readOnly = {
if componentType is None: 'componentType': self.componentType,
self._componentType = self.componentType 'sizeSpec': self.sizeSpec
else: }
self._componentType = componentType readOnly.update(kwargs)
if sizeSpec is None:
self._sizeSpec = self.sizeSpec Asn1ItemBase.__init__(self, **readOnly)
else:
self._sizeSpec = sizeSpec
self._componentValues = [] self._componentValues = []
self._componentValuesSet = 0
def __repr__(self): def __repr__(self):
r = [] representation = []
if self._componentType is not self.componentType: if self.componentType is not self.__class__.componentType:
r.append('componentType=%r' % (self._componentType,)) representation.append('componentType=%r' % (self.componentType,))
if self._tagSet is not self.tagSet: if self.tagSet is not self.__class__.tagSet:
r.append('tagSet=%r' % (self._tagSet,)) representation.append('tagSet=%r' % (self.tagSet,))
if self._subtypeSpec is not self.subtypeSpec: if self.subtypeSpec is not self.__class__.subtypeSpec:
r.append('subtypeSpec=%r' % (self._subtypeSpec,)) representation.append('subtypeSpec=%r' % (self.subtypeSpec,))
r = '%s(%s)' % (self.__class__.__name__, ', '.join(r)) representation = '%s(%s)' % (self.__class__.__name__, ', '.join(representation))
if self._componentValues: if self._componentValues:
r += '.setComponents(%s)' % ', '.join([repr(x) for x in self._componentValues]) for idx, component in enumerate(self._componentValues):
return r if component is None or component is noValue:
continue
representation += '.setComponentByPosition(%d, %s)' % (idx, repr(component))
return representation
def __eq__(self, other): def __eq__(self, other):
return self is other and True or self._componentValues == other return self is other and True or self._componentValues == other
def __ne__(self, other): return self._componentValues != other
def __lt__(self, other): return self._componentValues < other def __ne__(self, other):
def __le__(self, other): return self._componentValues <= other return self._componentValues != other
def __gt__(self, other): return self._componentValues > other
def __ge__(self, other): return self._componentValues >= other def __lt__(self, other):
return self._componentValues < other
def __le__(self, other):
return self._componentValues <= other
def __gt__(self, other):
return self._componentValues > other
def __ge__(self, other):
return self._componentValues >= other
if sys.version_info[0] <= 2: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._componentValues) def __nonzero__(self):
return self._componentValues and True or False
else: else:
def __bool__(self): return bool(self._componentValues) def __bool__(self):
return self._componentValues and True or False
def getComponentTagMap(self): def _cloneComponentValues(self, myClone, cloneValueFlag):
raise error.PyAsn1Error('Method not implemented') pass
def _cloneComponentValues(self, myClone, cloneValueFlag): pass def clone(self, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
properties of the |ASN.1| object.
Parameters
----------
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 size constraint(s)
Returns
-------
:
new instance of |ASN.1| type/value
"""
cloneValueFlag = kwargs.pop('cloneValueFlag', False)
initilaizers = self.readOnly.copy()
initilaizers.update(kwargs)
clone = self.__class__(**initilaizers)
def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None,
cloneValueFlag=None):
if tagSet is None:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
if sizeSpec is None:
sizeSpec = self._sizeSpec
r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag: if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag) self._cloneComponentValues(clone, cloneValueFlag)
return r
def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None, return clone
sizeSpec=None, cloneValueFlag=None):
def subtype(self, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
properties of the |ASN.1| object.
Parameters
----------
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 size constraint(s)
Returns
-------
:
new instance of |ASN.1| type/value
"""
initializers = self.readOnly.copy()
cloneValueFlag = kwargs.pop('cloneValueFlag', False)
implicitTag = kwargs.pop('implicitTag', None)
if implicitTag is not None: if implicitTag is not None:
tagSet = self._tagSet.tagImplicitly(implicitTag) initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
elif explicitTag is not None:
tagSet = self._tagSet.tagExplicitly(explicitTag) explicitTag = kwargs.pop('explicitTag', None)
else: if explicitTag is not None:
tagSet = self._tagSet initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec for arg, option in kwargs.items():
else: initializers[arg] += option
subtypeSpec = subtypeSpec + self._subtypeSpec
if sizeSpec is None: clone = self.__class__(**initializers)
sizeSpec = self._sizeSpec
else:
sizeSpec = sizeSpec + self._sizeSpec
r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag: if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag) self._cloneComponentValues(clone, cloneValueFlag)
return r
def _verifyComponent(self, idx, value): pass return clone
def verifySizeSpec(self): self._sizeSpec(self) def verifySizeSpec(self):
self.sizeSpec(self)
def getComponentByPosition(self, idx): def getComponentByPosition(self, idx):
raise error.PyAsn1Error('Method not implemented') raise error.PyAsn1Error('Method not implemented')
def setComponentByPosition(self, idx, value, verifyConstraints=True): def setComponentByPosition(self, idx, value, verifyConstraints=True):
raise error.PyAsn1Error('Method not implemented') raise error.PyAsn1Error('Method not implemented')
def setComponents(self, *args, **kwargs): def setComponents(self, *args, **kwargs):
for idx in range(len(args)): for idx, value in enumerate(args):
self[idx] = args[idx] self[idx] = value
for k in kwargs: for k in kwargs:
self[k] = kwargs[k] self[k] = kwargs[k]
return self return self
def getComponentType(self): return self._componentType def __len__(self):
return len(self._componentValues)
def setDefaultComponents(self): pass
def __getitem__(self, idx): return self.getComponentByPosition(idx)
def __setitem__(self, idx, value): self.setComponentByPosition(idx, value)
def __len__(self): return len(self._componentValues)
def clear(self): def clear(self):
self._componentValues = [] self._componentValues = []
self._componentValuesSet = 0
# backward compatibility
def setDefaultComponents(self):
pass
def getComponentType(self):
return self.componentType

View File

@ -1,64 +1,374 @@
# ASN.1 "character string" types #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys
from pyasn1.type import univ, tag from pyasn1.type import univ, tag
from pyasn1 import error
class NumericString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly( __all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
NoValue = univ.NoValue
noValue = univ.noValue
class AbstractCharacterString(univ.OctetString):
"""Creates |ASN.1| type or object.
|ASN.1| objects are immutable and duck-type Python 2 :class:`unicode` or Python 3 :class:`str`.
When used in octet-stream context, |ASN.1| type assumes "|encoding|" encoding.
Parameters
----------
value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
unicode object (Python 2) or string (Python 3), alternatively string
(Python 2) or bytes (Python 3) representing octet-stream of serialized
unicode string (note `encoding` parameter) or |ASN.1| class instance.
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
encoding: :py:class:`str`
Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
:class:`str` (Python 3) the payload when |ASN.1| object is used
in octet-stream context.
Raises
------
: :py:class:`pyasn1.error.PyAsn1Error`
On constraint violation or bad initializer.
"""
if sys.version_info[0] <= 2:
def __str__(self):
try:
return self._value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
"Can't encode string '%s' with codec %s" % (self._value, self.encoding)
)
def __unicode__(self):
return unicode(self._value)
def prettyIn(self, value):
try:
if isinstance(value, unicode):
return value
elif isinstance(value, str):
return value.decode(self.encoding)
elif isinstance(value, (tuple, list)):
return self.prettyIn(''.join([chr(x) for x in value]))
elif isinstance(value, univ.OctetString):
return value.asOctets().decode(self.encoding)
else:
return unicode(value)
except (UnicodeDecodeError, LookupError):
raise error.PyAsn1Error(
"Can't decode string '%s' with codec %s" % (value, self.encoding)
)
def asOctets(self, padding=True):
return str(self)
def asNumbers(self, padding=True):
return tuple([ord(x) for x in str(self)])
else:
def __str__(self):
return str(self._value)
def __bytes__(self):
try:
return self._value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
"Can't encode string '%s' with codec %s" % (self._value, self.encoding)
)
def prettyIn(self, value):
try:
if isinstance(value, str):
return value
elif isinstance(value, bytes):
return value.decode(self.encoding)
elif isinstance(value, (tuple, list)):
return self.prettyIn(bytes(value))
elif isinstance(value, univ.OctetString):
return value.asOctets().decode(self.encoding)
else:
return str(value)
except (UnicodeDecodeError, LookupError):
raise error.PyAsn1Error(
"Can't decode string '%s' with codec %s" % (value, self.encoding)
)
def asOctets(self, padding=True):
return bytes(self)
def asNumbers(self, padding=True):
return tuple(bytes(self))
def prettyOut(self, value):
return value
def __reversed__(self):
return reversed(self._value)
def clone(self, value=noValue, **kwargs):
"""Creates a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
unicode object (Python 2) or string (Python 3), alternatively string
(Python 2) or bytes (Python 3) representing octet-stream of serialized
unicode string (note `encoding` parameter) or |ASN.1| class instance.
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
encoding: :py:class:`str`
Unicode codec ID to encode/decode :py:class:`unicode` (Python 2) or
:py:class:`str` (Python 3) the payload when |ASN.1| object is used
in octet-stream context.
Returns
-------
:
new instance of |ASN.1| type/value
"""
return univ.OctetString.clone(self, value, **kwargs)
def subtype(self, value=noValue, **kwargs):
"""Creates a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
unicode object (Python 2) or string (Python 3), alternatively string
(Python 2) or bytes (Python 3) representing octet-stream of serialized
unicode string (note `encoding` parameter) or |ASN.1| class instance.
implicitTag: :py:class:`~pyasn1.type.tag.Tag`
Implicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
explicitTag: :py:class:`~pyasn1.type.tag.Tag`
Explicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
encoding: :py:class:`str`
Unicode codec ID to encode/decode :py:class:`unicode` (Python 2) or
:py:class:`str` (Python 3) the payload when |ASN.1| object is used
in octet-stream context.
Returns
-------
:
new instance of |ASN.1| type/value
"""
return univ.OctetString.subtype(self, value, **kwargs)
class NumericString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
) )
encoding = 'us-ascii'
class PrintableString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class PrintableString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
) )
encoding = 'us-ascii'
class TeletexString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class TeletexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
) )
encoding = 'iso-8859-1'
class T61String(TeletexString): pass # Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class VideotexString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly( class T61String(TeletexString):
__doc__ = TeletexString.__doc__
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class VideotexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
) )
encoding = 'iso-8859-1'
class IA5String(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class IA5String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
) )
encoding = 'us-ascii'
class GraphicString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class GraphicString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
) )
encoding = 'iso-8859-1'
class VisibleString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class VisibleString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
) )
encoding = 'us-ascii'
class ISO646String(VisibleString): pass # Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class GeneralString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly( class ISO646String(VisibleString):
__doc__ = VisibleString.__doc__
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class GeneralString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
) )
encoding = 'iso-8859-1'
class UniversalString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class UniversalString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
) )
encoding = "utf-32-be" encoding = "utf-32-be"
class BMPString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class BMPString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
) )
encoding = "utf-16-be" encoding = "utf-16-be"
class UTF8String(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class UTF8String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
) )
encoding = "utf-8" encoding = "utf-8"
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()

View File

@ -1,86 +1,124 @@
# #
# ASN.1 subtype constraints classes. # This file is part of pyasn1 software.
# #
# Constraints are relatively rare, but every ASN1 object # Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# is doing checks all the time for whether they have any # License: http://pyasn1.sf.net/license.html
# constraints and whether they are applicable to the object.
#
# What we're going to do is define objects/functions that
# can be called unconditionally if they are present, and that
# are simply not present if there are no constraints.
# #
# Original concept and code by Mike C. Fletcher. # Original concept and code by Mike C. Fletcher.
# #
import sys import sys
from pyasn1.type import error from pyasn1.type import error
class AbstractConstraint: __all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint', 'ValueRangeConstraint',
'ValueSizeConstraint', 'PermittedAlphabetConstraint', 'InnerTypeConstraint',
'ConstraintsExclusion', 'ConstraintsIntersection', 'ConstraintsUnion']
class AbstractConstraint(object):
"""Abstract base-class for constraint objects """Abstract base-class for constraint objects
Constraints should be stored in a simple sequence in the Constraints should be stored in a simple sequence in the
namespace of their client Asn1Item sub-classes. namespace of their client Asn1Item sub-classes in cases
when ASN.1 constraint is define.
""" """
def __init__(self, *values): def __init__(self, *values):
self._valueMap = {} self._valueMap = set()
self._setValues(values) self._setValues(values)
self.__hashedValues = None self.__hash = hash((self.__class__.__name__, self._values))
def __call__(self, value, idx=None): def __call__(self, value, idx=None):
if not self._values:
return
try: try:
self._testValue(value, idx) self._testValue(value, idx)
except error.ValueConstraintError: except error.ValueConstraintError:
raise error.ValueConstraintError( raise error.ValueConstraintError(
'%s failed at: \"%s\"' % (self, sys.exc_info()[1]) '%s failed at: %r' % (self, sys.exc_info()[1])
) )
def __repr__(self): def __repr__(self):
return '%s(%s)' % ( return '%s(%s)' % (
self.__class__.__name__, self.__class__.__name__,
', '.join([repr(x) for x in self._values]) ', '.join([repr(x) for x in self._values])
) )
def __eq__(self, other): def __eq__(self, other):
return self is other and True or self._values == other return self is other and True or self._values == other
def __ne__(self, other): return self._values != other
def __lt__(self, other): return self._values < other def __ne__(self, other):
def __le__(self, other): return self._values <= other return self._values != other
def __gt__(self, other): return self._values > other
def __ge__(self, other): return self._values >= other def __lt__(self, other):
return self._values < other
def __le__(self, other):
return self._values <= other
def __gt__(self, other):
return self._values > other
def __ge__(self, other):
return self._values >= other
if sys.version_info[0] <= 2: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._values) def __nonzero__(self):
return self._values and True or False
else: else:
def __bool__(self): return bool(self._values) def __bool__(self):
return self._values and True or False
def __hash__(self): def __hash__(self):
if self.__hashedValues is None: return self.__hash
self.__hashedValues = hash((self.__class__.__name__, self._values))
return self.__hashedValues def _setValues(self, values):
self._values = values
def _setValues(self, values): self._values = values
def _testValue(self, value, idx): def _testValue(self, value, idx):
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
# Constraints derivation logic # Constraints derivation logic
def getValueMap(self): return self._valueMap def getValueMap(self):
return self._valueMap
def isSuperTypeOf(self, otherConstraint): def isSuperTypeOf(self, otherConstraint):
return self in otherConstraint.getValueMap() or \ # TODO: fix possible comparison of set vs scalars here
otherConstraint is self or otherConstraint == self return (otherConstraint is self or
not self._values or
otherConstraint == self or
self in otherConstraint.getValueMap())
def isSubTypeOf(self, otherConstraint): def isSubTypeOf(self, otherConstraint):
return otherConstraint in self._valueMap or \ return (otherConstraint is self or
otherConstraint is self or otherConstraint == self not self or
otherConstraint == self or
otherConstraint in self._valueMap)
class SingleValueConstraint(AbstractConstraint): class SingleValueConstraint(AbstractConstraint):
"""Value must be part of defined values constraint""" """Value must be part of defined values constraint"""
def _setValues(self, values):
self._values = values
self._set = set(values)
def _testValue(self, value, idx): def _testValue(self, value, idx):
# XXX index vals for performance? if value not in self._set:
if value not in self._values:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
class ContainedSubtypeConstraint(AbstractConstraint): class ContainedSubtypeConstraint(AbstractConstraint):
"""Value must satisfy all of defined set of constraints""" """Value must satisfy all of defined set of constraints"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
for c in self._values: for c in self._values:
c(value, idx) c(value, idx)
class ValueRangeConstraint(AbstractConstraint): class ValueRangeConstraint(AbstractConstraint):
"""Value must be within start and stop values (inclusive)""" """Value must be within start and stop values (inclusive)"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
if value < self.start or value > self.stop: if value < self.start or value > self.stop:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
@ -100,27 +138,30 @@ class ValueRangeConstraint(AbstractConstraint):
) )
AbstractConstraint._setValues(self, values) AbstractConstraint._setValues(self, values)
class ValueSizeConstraint(ValueRangeConstraint): class ValueSizeConstraint(ValueRangeConstraint):
"""len(value) must be within start and stop values (inclusive)""" """len(value) must be within start and stop values (inclusive)"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
l = len(value) valueSize = len(value)
if l < self.start or l > self.stop: if valueSize < self.start or valueSize > self.stop:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
class PermittedAlphabetConstraint(SingleValueConstraint): class PermittedAlphabetConstraint(SingleValueConstraint):
def _setValues(self, values): def _setValues(self, values):
self._values = () self._values = values
for v in values: self._set = set(values)
self._values = self._values + tuple(v)
def _testValue(self, value, idx): def _testValue(self, value, idx):
for v in value: if not self._set.issuperset(value):
if v not in self._values:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
# This is a bit kludgy, meaning two op modes within a single constraing
# This is a bit kludgy, meaning two op modes within a single constraint
class InnerTypeConstraint(AbstractConstraint): class InnerTypeConstraint(AbstractConstraint):
"""Value must satisfy type and presense constraints""" """Value must satisfy type and presense constraints"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
if self.__singleTypeConstraint: if self.__singleTypeConstraint:
self.__singleTypeConstraint(value) self.__singleTypeConstraint(value)
@ -142,10 +183,12 @@ class InnerTypeConstraint(AbstractConstraint):
self.__singleTypeConstraint = v self.__singleTypeConstraint = v
AbstractConstraint._setValues(self, values) AbstractConstraint._setValues(self, values)
# Boolean ops on constraints # Boolean ops on constraints
class ConstraintsExclusion(AbstractConstraint): class ConstraintsExclusion(AbstractConstraint):
"""Value must not fit the single constraint""" """Value must not fit the single constraint"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
try: try:
self._values[0](value, idx) self._values[0](value, idx)
@ -159,35 +202,50 @@ class ConstraintsExclusion(AbstractConstraint):
raise error.PyAsn1Error('Single constraint expected') raise error.PyAsn1Error('Single constraint expected')
AbstractConstraint._setValues(self, values) AbstractConstraint._setValues(self, values)
class AbstractConstraintSet(AbstractConstraint): class AbstractConstraintSet(AbstractConstraint):
"""Value must not satisfy the single constraint""" """Value must not satisfy the single constraint"""
def __getitem__(self, idx): return self._values[idx]
def __add__(self, value): return self.__class__(self, value) def __getitem__(self, idx):
def __radd__(self, value): return self.__class__(self, value) return self._values[idx]
def __len__(self): return len(self._values) def __iter__(self):
return iter(self._values)
def __add__(self, value):
return self.__class__(*(self._values + (value,)))
def __radd__(self, value):
return self.__class__(*((value,) + self._values))
def __len__(self):
return len(self._values)
# Constraints inclusion in sets # Constraints inclusion in sets
def _setValues(self, values): def _setValues(self, values):
self._values = values self._values = values
for v in values: for constraint in values:
self._valueMap[v] = 1 if constraint:
self._valueMap.update(v.getValueMap()) self._valueMap.add(constraint)
self._valueMap.update(constraint.getValueMap())
class ConstraintsIntersection(AbstractConstraintSet): class ConstraintsIntersection(AbstractConstraintSet):
"""Value must satisfy all constraints""" """Value must satisfy all constraints"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
for v in self._values: for constraint in self._values:
v(value, idx) constraint(value, idx)
class ConstraintsUnion(AbstractConstraintSet): class ConstraintsUnion(AbstractConstraintSet):
"""Value must satisfy at least one constraint""" """Value must satisfy at least one constraint"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
for v in self._values: for constraint in self._values:
try: try:
v(value, idx) constraint(value, idx)
except error.ValueConstraintError: except error.ValueConstraintError:
pass pass
else: else:

View File

@ -1,3 +1,11 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.error import PyAsn1Error from pyasn1.error import PyAsn1Error
class ValueConstraintError(PyAsn1Error): pass
class ValueConstraintError(PyAsn1Error):
pass

View File

@ -1,149 +1,511 @@
# NamedType specification for constructed types #
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys import sys
from pyasn1.type import tagmap from pyasn1.type import tag, tagmap
from pyasn1 import error from pyasn1 import error
class NamedType: __all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType', 'NamedTypes']
isOptional = 0
isDefaulted = 0
def __init__(self, name, t): class NamedType(object):
self.__name = name; self.__type = t """Create named field object for a constructed ASN.1 type.
def __repr__(self): return '%s(%r, %r)' % (
self.__class__.__name__, self.__name, self.__type The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
)
def __eq__(self, other): return tuple(self) == tuple(other) |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
def __ne__(self, other): return tuple(self) != tuple(other) holding *name* and *asn1Object* components.
def __lt__(self, other): return tuple(self) < tuple(other)
def __le__(self, other): return tuple(self) <= tuple(other) Parameters
def __gt__(self, other): return tuple(self) > tuple(other) ----------
def __ge__(self, other): return tuple(self) >= tuple(other) name: :py:class:`str`
def __hash__(self): return hash(tuple(self)) Field name
asn1Object:
ASN.1 type object
"""
isOptional = False
isDefaulted = False
def __init__(self, name, asn1Object):
self.__name = name
self.__type = asn1Object
self.__nameAndType = name, asn1Object
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.__name, self.__type)
def __eq__(self, other):
return self.__nameAndType == other
def __ne__(self, other):
return self.__nameAndType != other
def __lt__(self, other):
return self.__nameAndType < other
def __le__(self, other):
return self.__nameAndType <= other
def __gt__(self, other):
return self.__nameAndType > other
def __ge__(self, other):
return self.__nameAndType >= other
def __hash__(self):
return hash(self.__nameAndType)
def getType(self): return self.__type
def getName(self): return self.__name
def __getitem__(self, idx): def __getitem__(self, idx):
if idx == 0: return self.__name return self.__nameAndType[idx]
if idx == 1: return self.__type
raise IndexError() def __iter__(self):
return iter(self.__nameAndType)
@property
def name(self):
return self.__name
@property
def asn1Object(self):
return self.__type
# Backward compatibility
def getName(self):
return self.name
def getType(self):
return self.asn1Object
class OptionalNamedType(NamedType): class OptionalNamedType(NamedType):
isOptional = 1 __doc__ = NamedType.__doc__
class DefaultedNamedType(NamedType):
isDefaulted = 1
class NamedTypes: isOptional = True
def __init__(self, *namedTypes):
class DefaultedNamedType(NamedType):
__doc__ = NamedType.__doc__
isDefaulted = True
class NamedTypes(object):
"""Create a collection of named fields for a constructed ASN.1 type.
The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
*NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
holding *name* as keys and ASN.1 type object as values.
Parameters
----------
*namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
"""
def __init__(self, *namedTypes, **kwargs):
self.__namedTypes = namedTypes self.__namedTypes = namedTypes
self.__namedTypesLen = len(self.__namedTypes) self.__namedTypesLen = len(self.__namedTypes)
self.__minTagSet = None self.__minTagSet = self.__computeMinTagSet()
self.__tagToPosIdx = {}; self.__nameToPosIdx = {} self.__nameToPosMap = self.__computeNameToPosMap()
self.__tagMap = { False: None, True: None } self.__tagToPosMap = self.__computeTagToPosMap()
self.__ambigiousTypes = {} self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
self.__uniqueTagMap = self.__computeTagMaps(unique=True)
self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
self.__hasOptionalOrDefault = bool([True for namedType in self.__namedTypes
if namedType.isDefaulted or namedType.isOptional])
self.__requiredComponents = frozenset(
[idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
)
self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
def __repr__(self): def __repr__(self):
return '%s(%s)' % ( return '%s(%s)' % (
self.__class__.__name__, self.__class__.__name__, ', '.join([repr(x) for x in self.__namedTypes])
', '.join([ repr(x) for x in self.__namedTypes ])
) )
def __eq__(self, other): return tuple(self) == tuple(other)
def __ne__(self, other): return tuple(self) != tuple(other)
def __lt__(self, other): return tuple(self) < tuple(other)
def __le__(self, other): return tuple(self) <= tuple(other)
def __gt__(self, other): return tuple(self) > tuple(other)
def __ge__(self, other): return tuple(self) >= tuple(other)
def __hash__(self): return hash(tuple(self))
def __getitem__(self, idx): return self.__namedTypes[idx] def __eq__(self, other):
return self.__namedTypes == other
def __ne__(self, other):
return self.__namedTypes != other
def __lt__(self, other):
return self.__namedTypes < other
def __le__(self, other):
return self.__namedTypes <= other
def __gt__(self, other):
return self.__namedTypes > other
def __ge__(self, other):
return self.__namedTypes >= other
def __hash__(self):
return hash(self.__namedTypes)
def __getitem__(self, idx):
try:
return self.__namedTypes[idx]
except TypeError:
return self.__namedTypes[self.__nameToPosMap[idx]]
def __contains__(self, key):
return key in self.__nameToPosMap
def __iter__(self):
return (x[0] for x in self.__namedTypes)
if sys.version_info[0] <= 2: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self.__namedTypesLen) def __nonzero__(self):
return self.__namedTypesLen > 0
else: else:
def __bool__(self): return bool(self.__namedTypesLen) def __bool__(self):
def __len__(self): return self.__namedTypesLen return self.__namedTypesLen > 0
def clone(self): return self.__class__(*self.__namedTypes) def __len__(self):
return self.__namedTypesLen
# Python dict protocol
def values(self):
return self.__values
def keys(self):
return self.__keys
def items(self):
return self.__items
def clone(self):
return self.__class__(*self.__namedTypes)
class PostponedError(object):
def __init__(self, errorMsg):
self.__errorMsg = errorMsg
def __getitem__(self, item):
raise error.PyAsn1Error(self.__errorMsg)
def __computeTagToPosMap(self):
tagToPosMap = {}
for idx, namedType in enumerate(self.__namedTypes):
tagMap = namedType.asn1Object.tagMap
if isinstance(tagMap, NamedTypes.PostponedError):
return tagMap
if not tagMap:
continue
for _tagSet in tagMap.presentTypes:
if _tagSet in tagToPosMap:
return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
tagToPosMap[_tagSet] = idx
return tagToPosMap
def __computeNameToPosMap(self):
nameToPosMap = {}
for idx, namedType in enumerate(self.__namedTypes):
if namedType.name in nameToPosMap:
return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
nameToPosMap[namedType.name] = idx
return nameToPosMap
def __computeAmbiguousTypes(self):
ambigiousTypes = {}
partialAmbigiousTypes = ()
for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
if namedType.isOptional or namedType.isDefaulted:
partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
else:
partialAmbigiousTypes = (namedType,)
if len(partialAmbigiousTypes) == len(self.__namedTypes):
ambigiousTypes[idx] = self
else:
ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes, **dict(terminal=True))
return ambigiousTypes
def getTypeByPosition(self, idx): def getTypeByPosition(self, idx):
if idx < 0 or idx >= self.__namedTypesLen: """Return ASN.1 type object by its position in fields set.
Parameters
----------
idx: :py:class:`int`
Field index
Returns
-------
:
ASN.1 type
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If given position is out of fields range
"""
try:
return self.__namedTypes[idx].asn1Object
except IndexError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
else:
return self.__namedTypes[idx].getType()
def getPositionByType(self, tagSet): def getPositionByType(self, tagSet):
if not self.__tagToPosIdx: """Return field position by its ASN.1 type.
idx = self.__namedTypesLen
while idx > 0: Parameters
idx = idx - 1 ----------
tagMap = self.__namedTypes[idx].getType().getTagMap() tagSet: :class:`~pysnmp.type.tag.TagSet`
for t in tagMap.getPosMap(): ASN.1 tag set distinguishing one ASN.1 type from others.
if t in self.__tagToPosIdx:
raise error.PyAsn1Error('Duplicate type %s' % (t,)) Returns
self.__tagToPosIdx[t] = idx -------
: :py:class:`int`
ASN.1 type position in fields set
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
"""
try: try:
return self.__tagToPosIdx[tagSet] return self.__tagToPosMap[tagSet]
except KeyError: except KeyError:
raise error.PyAsn1Error('Type %s not found' % (tagSet,)) raise error.PyAsn1Error('Type %s not found' % (tagSet,))
def getNameByPosition(self, idx): def getNameByPosition(self, idx):
"""Return field name by its position in fields set.
Parameters
----------
idx: :py:class:`idx`
Field index
Returns
-------
: :py:class:`str`
Field name
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If given field name is not present in callee *NamedTypes*
"""
try: try:
return self.__namedTypes[idx].getName() return self.__namedTypes[idx].name
except IndexError: except IndexError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
def getPositionByName(self, name): def getPositionByName(self, name):
if not self.__nameToPosIdx: """Return field position by filed name.
idx = self.__namedTypesLen
while idx > 0: Parameters
idx = idx - 1 ----------
n = self.__namedTypes[idx].getName() name: :py:class:`str`
if n in self.__nameToPosIdx: Field name
raise error.PyAsn1Error('Duplicate name %s' % (n,))
self.__nameToPosIdx[n] = idx Returns
-------
: :py:class:`int`
Field position in fields set
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If *name* is not present or not unique within callee *NamedTypes*
"""
try: try:
return self.__nameToPosIdx[name] return self.__nameToPosMap[name]
except KeyError: except KeyError:
raise error.PyAsn1Error('Name %s not found' % (name,)) raise error.PyAsn1Error('Name %s not found' % (name,))
def __buildAmbigiousTagMap(self):
ambigiousTypes = ()
idx = self.__namedTypesLen
while idx > 0:
idx = idx - 1
t = self.__namedTypes[idx]
if t.isOptional or t.isDefaulted:
ambigiousTypes = (t, ) + ambigiousTypes
else:
ambigiousTypes = (t, )
self.__ambigiousTypes[idx] = NamedTypes(*ambigiousTypes)
def getTagMapNearPosition(self, idx): def getTagMapNearPosition(self, idx):
if not self.__ambigiousTypes: self.__buildAmbigiousTagMap() """Return ASN.1 types that are allowed at or past given field position.
Some ASN.1 serialization allow for skipping optional and defaulted fields.
Some constructed ASN.1 types allow reordering of the fields. When recovering
such objects it may be important to know which types can possibly be
present at any given position in the field sets.
Parameters
----------
idx: :py:class:`int`
Field index
Returns
-------
: :class:`~pyasn1.type.tagmap.TagMap`
Map if ASN.1 types allowed at given field position
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If given position is out of fields range
"""
try: try:
return self.__ambigiousTypes[idx].getTagMap() return self.__ambiguousTypes[idx].tagMap
except KeyError: except KeyError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
def getPositionNearType(self, tagSet, idx): def getPositionNearType(self, tagSet, idx):
if not self.__ambigiousTypes: self.__buildAmbigiousTagMap() """Return the closest field position where given ASN.1 type is allowed.
Some ASN.1 serialization allow for skipping optional and defaulted fields.
Some constructed ASN.1 types allow reordering of the fields. When recovering
such objects it may be important to know at which field position, in field set,
given *tagSet* is allowed at or past *idx* position.
Parameters
----------
tagSet: :class:`~pyasn1.type.tag.TagSet`
ASN.1 type which field position to look up
idx: :py:class:`int`
Field position at or past which to perform ASN.1 type look up
Returns
-------
: :py:class:`int`
Field position in fields set
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If *tagSet* is not present or not unique within callee *NamedTypes*
or *idx* is out of fields range
"""
try: try:
return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet) return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet)
except KeyError: except KeyError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
def genMinTagSet(self): def __computeMinTagSet(self):
if self.__minTagSet is None: minTagSet = None
for t in self.__namedTypes: for namedType in self.__namedTypes:
__type = t.getType() asn1Object = namedType.asn1Object
tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)()
if self.__minTagSet is None or tagSet < self.__minTagSet: try:
self.__minTagSet = tagSet tagSet = asn1Object.minTagSet
except AttributeError:
tagSet = asn1Object.tagSet
if minTagSet is None or tagSet < minTagSet:
minTagSet = tagSet
return minTagSet or tag.TagSet()
@property
def minTagSet(self):
"""Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
Some ASN.1 types/serialization protocols require ASN.1 types to be
arranged based on their numerical tag value. The *minTagSet* property
returns that.
Returns
-------
: :class:`~pyasn1.type.tagset.TagSet`
Minimal TagSet among ASN.1 types in callee *NamedTypes*
"""
return self.__minTagSet return self.__minTagSet
def getTagMap(self, uniq=False): def __computeTagMaps(self, unique):
if self.__tagMap[uniq] is None: presentTypes = {}
tagMap = tagmap.TagMap() skipTypes = {}
for nt in self.__namedTypes: defaultType = None
tagMap = tagMap.clone( for namedType in self.__namedTypes:
nt.getType(), nt.getType().getTagMap(), uniq tagMap = namedType.asn1Object.tagMap
) if isinstance(tagMap, NamedTypes.PostponedError):
self.__tagMap[uniq] = tagMap return tagMap
return self.__tagMap[uniq] for tagSet in tagMap:
if unique and tagSet in presentTypes:
return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
presentTypes[tagSet] = namedType.asn1Object
skipTypes.update(tagMap.skipTypes)
if defaultType is None:
defaultType = tagMap.defaultType
elif tagMap.defaultType is not None:
return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
return tagmap.TagMap(presentTypes, skipTypes, defaultType)
@property
def tagMap(self):
"""Return a *TagMap* object from tags and types recursively.
Return a :class:`~pyasn1.type.tagmap.TagMap` object by
combining tags from *TagMap* objects of children types and
associating them with their immediate child type.
Example
-------
.. code-block:: python
OuterType ::= CHOICE {
innerType INTEGER
}
Calling *.tagMap* on *OuterType* will yield a map like this:
.. code-block:: python
Integer.tagSet -> Choice
"""
return self.__nonUniqueTagMap
@property
def tagMapUnique(self):
"""Return a *TagMap* object from unique tags and types recursively.
Return a :class:`~pyasn1.type.tagmap.TagMap` object by
combining tags from *TagMap* objects of children types and
associating them with their immediate child type.
Example
-------
.. code-block:: python
OuterType ::= CHOICE {
innerType INTEGER
}
Calling *.tagMapUnique* on *OuterType* will yield a map like this:
.. code-block:: python
Integer.tagSet -> Choice
Note
----
Duplicate *TagSet* objects found in the tree of children
types would cause error.
"""
return self.__uniqueTagMap
@property
def hasOptionalOrDefault(self):
return self.__hasOptionalOrDefault
@property
def namedTypes(self):
return iter(self.__namedTypes)
@property
def requiredComponents(self):
return self.__requiredComponents

View File

@ -1,58 +1,181 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
# ASN.1 named integers # ASN.1 named integers
#
from pyasn1 import error from pyasn1 import error
__all__ = [ 'NamedValues' ] __all__ = ['NamedValues']
class NamedValues(object):
"""Create named values object.
The |NamedValues| object represents a collection of string names
associated with numeric IDs. These objects are used for giving
names to otherwise numerical values.
|NamedValues| objects are immutable and duck-type Python
:class:`dict` object mapping ID to name and vice-versa.
Parameters
----------
\*args: variable number of two-element :py:class:`tuple`
\*\*kwargs: keyword parameters of:
name: :py:class:`str`
Value name
value: :py:class:`int`
A numerical value
Examples
--------
>>> nv = namedval.NamedValues('a', 'b', ('c', 0), d=1)
>>> nv
>>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
>>> nv[0]
'c'
>>> nv['a']
2
"""
def __init__(self, *args, **kwargs):
self.__names = {}
self.__numbers = {}
anonymousNames = []
for namedValue in args:
if isinstance(namedValue, (tuple, list)):
try:
name, number = namedValue
except ValueError:
raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
class NamedValues:
def __init__(self, *namedValues):
self.nameToValIdx = {}; self.valToNameIdx = {}
self.namedValues = ()
automaticVal = 1
for namedValue in namedValues:
if isinstance(namedValue, tuple):
name, val = namedValue
else: else:
name = namedValue anonymousNames.append(namedValue)
val = automaticVal continue
if name in self.nameToValIdx:
if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,)) raise error.PyAsn1Error('Duplicate name %s' % (name,))
self.nameToValIdx[name] = val
if val in self.valToNameIdx: if number in self.__numbers:
raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val)) raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
self.valToNameIdx[val] = name
self.namedValues = self.namedValues + ((name, val),) self.__names[name] = number
automaticVal = automaticVal + 1 self.__numbers[number] = name
for name, number in kwargs.items():
if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
if number in self.__numbers:
raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
self.__names[name] = number
self.__numbers[number] = name
if anonymousNames:
number = self.__numbers and max(self.__numbers) + 1 or 0
for name in anonymousNames:
if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
self.__names[name] = number
self.__numbers[number] = name
number += 1
def __repr__(self): def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues])) return '%s(%r)' % (self.__class__.__name__, tuple(self.items()))
def __str__(self): return str(self.namedValues) def __str__(self):
return str(self.items())
def __eq__(self, other): return tuple(self) == tuple(other) def __eq__(self, other):
def __ne__(self, other): return tuple(self) != tuple(other) return dict(self) == other
def __lt__(self, other): return tuple(self) < tuple(other)
def __le__(self, other): return tuple(self) <= tuple(other)
def __gt__(self, other): return tuple(self) > tuple(other)
def __ge__(self, other): return tuple(self) >= tuple(other)
def __hash__(self): return hash(tuple(self))
def getName(self, value): def __ne__(self, other):
if value in self.valToNameIdx: return dict(self) != other
return self.valToNameIdx[value]
def getValue(self, name): def __lt__(self, other):
if name in self.nameToValIdx: return dict(self) < other
return self.nameToValIdx[name]
def __getitem__(self, i): return self.namedValues[i] def __le__(self, other):
def __len__(self): return len(self.namedValues) return dict(self) <= other
def __gt__(self, other):
return dict(self) > other
def __ge__(self, other):
return dict(self) >= other
def __hash__(self):
return hash(self.items())
# Python dict protocol (read-only)
def __getitem__(self, key):
try:
return self.__numbers[key]
except KeyError:
return self.__names[key]
def __len__(self):
return len(self.__names)
def __contains__(self, key):
return key in self.__names or key in self.__numbers
def __iter__(self):
return iter(self.__names)
def values(self):
return iter(self.__numbers)
def keys(self):
return iter(self.__names)
def items(self):
for name in self.__names:
yield name, self.__names[name]
# support merging
def __add__(self, namedValues): def __add__(self, namedValues):
return self.__class__(*self.namedValues + namedValues) return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
def __radd__(self, namedValues):
return self.__class__(*namedValues + tuple(self))
def clone(self, *namedValues): # XXX clone/subtype?
return self.__class__(*tuple(self) + namedValues)
# XXX clone/subtype? def clone(self, *args, **kwargs):
new = self.__class__(*args, **kwargs)
return self + new
# legacy protocol
def getName(self, value):
if value in self.__numbers:
return self.__numbers[value]
def getValue(self, name):
if name in self.__names:
return self.__names[name]
def getValues(self, *names):
try:
return [self.__names[name] for name in names]
except KeyError:
raise error.PyAsn1Error(
'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
)

View File

@ -1,128 +1,318 @@
# ASN.1 types tags #
from operator import getitem # This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1 import error from pyasn1 import error
__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext',
'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed',
'tagCategoryImplicit', 'tagCategoryExplicit', 'tagCategoryUntagged',
'Tag', 'TagSet']
#: Identifier for ASN.1 class UNIVERSAL
tagClassUniversal = 0x00 tagClassUniversal = 0x00
#: Identifier for ASN.1 class APPLICATION
tagClassApplication = 0x40 tagClassApplication = 0x40
#: Identifier for ASN.1 class context-specific
tagClassContext = 0x80 tagClassContext = 0x80
#: Identifier for ASN.1 class private
tagClassPrivate = 0xC0 tagClassPrivate = 0xC0
#: Identifier for "simple" ASN.1 structure (e.g. scalar)
tagFormatSimple = 0x00 tagFormatSimple = 0x00
#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components)
tagFormatConstructed = 0x20 tagFormatConstructed = 0x20
tagCategoryImplicit = 0x01 tagCategoryImplicit = 0x01
tagCategoryExplicit = 0x02 tagCategoryExplicit = 0x02
tagCategoryUntagged = 0x04 tagCategoryUntagged = 0x04
class Tag:
class Tag(object):
"""Create ASN.1 tag
Represents ASN.1 tag that can be attached to a ASN.1 type to make
types distinguishable from each other.
*Tag* objects are immutable and duck-type Python :class:`tuple` objects
holding three integer components of a tag.
Parameters
----------
tagClass: :py:class:`int`
Tag *class* value
tagFormat: :py:class:`int`
Tag *format* value
tagId: :py:class:`int`
Tag ID value
"""
def __init__(self, tagClass, tagFormat, tagId): def __init__(self, tagClass, tagFormat, tagId):
if tagId < 0: if tagId < 0:
raise error.PyAsn1Error( raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
'Negative tag ID (%s) not allowed' % (tagId,) self.__tagClass = tagClass
) self.__tagFormat = tagFormat
self.__tag = (tagClass, tagFormat, tagId) self.__tagId = tagId
self.uniq = (tagClass, tagId) self.__tagClassId = tagClass, tagId
self.__hashedUniqTag = hash(self.uniq) self.__hash = hash(self.__tagClassId)
def __str__(self): def __str__(self):
return '[%s:%s:%s]' % self.__tag return '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId)
def __repr__(self): def __repr__(self):
return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % ( return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % (
(self.__class__.__name__,) + self.__tag (self.__class__.__name__, self.__tagClass, self.__tagFormat, self.__tagId)
) )
# These is really a hotspot -- expose public "uniq" attribute to save on
# function calls
def __eq__(self, other): return self.uniq == other.uniq
def __ne__(self, other): return self.uniq != other.uniq
def __lt__(self, other): return self.uniq < other.uniq
def __le__(self, other): return self.uniq <= other.uniq
def __gt__(self, other): return self.uniq > other.uniq
def __ge__(self, other): return self.uniq >= other.uniq
def __hash__(self): return self.__hashedUniqTag
def __getitem__(self, idx): return self.__tag[idx]
def __and__(self, otherTag):
(tagClass, tagFormat, tagId) = otherTag
return self.__class__(
self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId
)
def __or__(self, otherTag):
(tagClass, tagFormat, tagId) = otherTag
return self.__class__(
self.__tag[0]|tagClass,
self.__tag[1]|tagFormat,
self.__tag[2]|tagId
)
def asTuple(self): return self.__tag # __getitem__() is slow
class TagSet: def __eq__(self, other):
return self.__tagClassId == other
def __ne__(self, other):
return self.__tagClassId != other
def __lt__(self, other):
return self.__tagClassId < other
def __le__(self, other):
return self.__tagClassId <= other
def __gt__(self, other):
return self.__tagClassId > other
def __ge__(self, other):
return self.__tagClassId >= other
def __hash__(self):
return self.__hash
def __getitem__(self, idx):
if idx == 0:
return self.__tagClass
elif idx == 1:
return self.__tagFormat
elif idx == 2:
return self.__tagId
else:
raise IndexError()
def __iter__(self):
yield self.__tagClass
yield self.__tagFormat
yield self.__tagId
def __and__(self, otherTag):
return self.__class__(self.__tagClass & otherTag.tagClass,
self.__tagFormat & otherTag.tagFormat,
self.__tagId & otherTag.tagId)
def __or__(self, otherTag):
return self.__class__(self.__tagClass | otherTag.tagClass,
self.__tagFormat | otherTag.tagFormat,
self.__tagId | otherTag.tagId)
@property
def tagClass(self):
"""ASN.1 tag class
Returns
-------
: :py:class:`int`
Tag class
"""
return self.__tagClass
@property
def tagFormat(self):
"""ASN.1 tag format
Returns
-------
: :py:class:`int`
Tag format
"""
return self.__tagFormat
@property
def tagId(self):
"""ASN.1 tag ID
Returns
-------
: :py:class:`int`
Tag ID
"""
return self.__tagId
class TagSet(object):
"""Create a collection of ASN.1 tags
Represents a combination of :class:`~pyasn1.type.tag.Tag` objects
that can be attached to a ASN.1 type to make types distinguishable
from each other.
*TagSet* objects are immutable and duck-type Python :class:`tuple` objects
holding arbitrary number of :class:`~pyasn1.type.tag.Tag` objects.
Parameters
----------
baseTag: :class:`~pyasn1.type.tag.Tag`
Base *Tag* object. This tag survives IMPLICIT tagging.
*superTags: :class:`~pyasn1.type.tag.Tag`
Additional *Tag* objects taking part in subtyping.
"""
def __init__(self, baseTag=(), *superTags): def __init__(self, baseTag=(), *superTags):
self.__baseTag = baseTag self.__baseTag = baseTag
self.__superTags = superTags self.__superTags = superTags
self.__hashedSuperTags = hash(superTags) self.__superTagsClassId = tuple(
_uniq = () [(superTag.tagClass, superTag.tagId) for superTag in superTags]
for t in superTags: )
_uniq = _uniq + t.uniq
self.uniq = _uniq
self.__lenOfSuperTags = len(superTags) self.__lenOfSuperTags = len(superTags)
self.__hash = hash(self.__superTagsClassId)
def __str__(self): def __str__(self):
return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]' return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]'
def __repr__(self): def __repr__(self):
return '%s(%s)' % ( return '%s(%s)' % (
self.__class__.__name__, self.__class__.__name__, '(), ' + ', '.join([repr(x) for x in self.__superTags])
'(), ' + ', '.join([repr(x) for x in self.__superTags])
) )
def __add__(self, superTag): def __add__(self, superTag):
return self.__class__( return self.__class__(self.__baseTag, *self.__superTags + (superTag,))
self.__baseTag, *self.__superTags + (superTag,)
)
def __radd__(self, superTag): def __radd__(self, superTag):
return self.__class__( return self.__class__(self.__baseTag, *(superTag,) + self.__superTags)
self.__baseTag, *(superTag,) + self.__superTags
) def __getitem__(self, i):
if i.__class__ is slice:
return self.__class__(self.__baseTag, *self.__superTags[i])
else:
return self.__superTags[i]
def __eq__(self, other):
return self.__superTagsClassId == other
def __ne__(self, other):
return self.__superTagsClassId != other
def __lt__(self, other):
return self.__superTagsClassId < other
def __le__(self, other):
return self.__superTagsClassId <= other
def __gt__(self, other):
return self.__superTagsClassId > other
def __ge__(self, other):
return self.__superTagsClassId >= other
def __hash__(self):
return self.__hash
def __len__(self):
return self.__lenOfSuperTags
@property
def baseTag(self):
"""Return base ASN.1 tag
Returns
-------
: :class:`~pyasn1.type.tag.Tag`
Base tag of this *TagSet*
"""
return self.__baseTag
@property
def superTags(self):
"""Return ASN.1 tags
Returns
-------
: :py:class:`tuple`
Tuple of :class:`~pyasn1.type.tag.Tag` objects that this *TagSet* contains
"""
return self.__superTags
def tagExplicitly(self, superTag): def tagExplicitly(self, superTag):
tagClass, tagFormat, tagId = superTag """Return explicitly tagged *TagSet*
if tagClass == tagClassUniversal:
raise error.PyAsn1Error( Create a new *TagSet* representing callee *TagSet* explicitly tagged
'Can\'t tag with UNIVERSAL-class tag' with passed tag(s). With explicit tagging mode, new tags are appended
) to existing tag(s).
if tagFormat != tagFormatConstructed:
superTag = Tag(tagClass, tagFormatConstructed, tagId) Parameters
----------
superTag: :class:`~pyasn1.type.tag.Tag`
*Tag* object to tag this *TagSet*
Returns
-------
: :class:`~pyasn1.type.tag.TagSet`
New *TagSet* object
"""
if superTag.tagClass == tagClassUniversal:
raise error.PyAsn1Error("Can't tag with UNIVERSAL class tag")
if superTag.tagFormat != tagFormatConstructed:
superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
return self + superTag return self + superTag
def tagImplicitly(self, superTag): def tagImplicitly(self, superTag):
tagClass, tagFormat, tagId = superTag """Return implicitly tagged *TagSet*
Create a new *TagSet* representing callee *TagSet* implicitly tagged
with passed tag(s). With implicit tagging mode, new tag(s) replace the
last existing tag.
Parameters
----------
superTag: :class:`~pyasn1.type.tag.Tag`
*Tag* object to tag this *TagSet*
Returns
-------
: :class:`~pyasn1.type.tag.TagSet`
New *TagSet* object
"""
if self.__superTags: if self.__superTags:
superTag = Tag(tagClass, self.__superTags[-1][1], tagId) superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId)
return self[:-1] + superTag return self[:-1] + superTag
def getBaseTag(self): return self.__baseTag
def __getitem__(self, idx):
if isinstance(idx, slice):
return self.__class__(
self.__baseTag, *getitem(self.__superTags, idx)
)
return self.__superTags[idx]
def __eq__(self, other): return self.uniq == other.uniq
def __ne__(self, other): return self.uniq != other.uniq
def __lt__(self, other): return self.uniq < other.uniq
def __le__(self, other): return self.uniq <= other.uniq
def __gt__(self, other): return self.uniq > other.uniq
def __ge__(self, other): return self.uniq >= other.uniq
def __hash__(self): return self.__hashedSuperTags
def __len__(self): return self.__lenOfSuperTags
def isSuperTagSetOf(self, tagSet): def isSuperTagSetOf(self, tagSet):
if len(tagSet) < self.__lenOfSuperTags: """Test type relationship against given *TagSet*
return
idx = self.__lenOfSuperTags - 1
while idx >= 0:
if self.__superTags[idx] != tagSet[idx]:
return
idx = idx - 1
return 1
def initTagSet(tag): return TagSet(tag, tag) The callee is considered to be a supertype of given *TagSet*
tag-wise if all tags in *TagSet* are present in the callee and
they are in the same order.
Parameters
----------
tagSet: :class:`~pyasn1.type.tag.TagSet`
*TagSet* object to evaluate against the callee
Returns
-------
: :py:class:`bool`
`True` if callee is a supertype of *tagSet*
"""
if len(tagSet) < self.__lenOfSuperTags:
return False
return self.__superTags == tagSet[:self.__lenOfSuperTags]
# Backward compatibility
def getBaseTag(self):
return self.__baseTag
def initTagSet(tag):
return TagSet(tag, tag)

View File

@ -1,66 +1,102 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1 import error from pyasn1 import error
class TagMap: __all__ = ['TagMap']
def __init__(self, posMap={}, negMap={}, defType=None):
self.__posMap = posMap.copy()
self.__negMap = negMap.copy() class TagMap(object):
self.__defType = defType """Map *TagSet* objects to ASN.1 types
Create an object mapping *TagSet* object to ASN.1 type.
*TagMap* objects are immutable and duck-type read-only Python
:class:`dict` objects holding *TagSet* objects as keys and ASN.1
type objects as values.
Parameters
----------
presentTypes: :py:class:`dict`
Map of :class:`~pyasn1.type.tag.TagSet` to ASN.1 objects considered
as being unconditionally present in the *TagMap*.
skipTypes: :py:class:`dict`
A collection of :class:`~pyasn1.type.tag.TagSet` objects considered
as absent in the *TagMap* even when *defaultType* is present.
defaultType: ASN.1 type object
An ASN.1 type object callee *TagMap* returns for any *TagSet* key not present
in *presentTypes* (unless given key is present in *skipTypes*).
"""
def __init__(self, presentTypes=None, skipTypes=None, defaultType=None):
self.__presentTypes = presentTypes or {}
self.__skipTypes = skipTypes or {}
self.__defaultType = defaultType
def __contains__(self, tagSet): def __contains__(self, tagSet):
return tagSet in self.__posMap or \ return (tagSet in self.__presentTypes or
self.__defType is not None and tagSet not in self.__negMap self.__defaultType is not None and tagSet not in self.__skipTypes)
def __getitem__(self, tagSet): def __getitem__(self, tagSet):
if tagSet in self.__posMap: try:
return self.__posMap[tagSet] return self.__presentTypes[tagSet]
elif tagSet in self.__negMap: except KeyError:
raise error.PyAsn1Error('Key in negative map') if self.__defaultType is None:
elif self.__defType is not None:
return self.__defType
else:
raise KeyError() raise KeyError()
elif tagSet in self.__skipTypes:
raise error.PyAsn1Error('Key in negative map')
else:
return self.__defaultType
def __iter__(self):
return iter(self.__presentTypes)
def __repr__(self): def __repr__(self):
s = self.__class__.__name__ + '(' s = self.__class__.__name__ + '('
if self.__posMap: if self.__presentTypes:
s = s + 'posMap=%r, ' % (self.__posMap,) s += 'presentTypes=%r, ' % (self.__presentTypes,)
if self.__negMap: if self.__skipTypes:
s = s + 'negMap=%r, ' % (self.__negMap,) s += 'skipTypes=%r, ' % (self.__skipTypes,)
if self.__defType is not None: if self.__defaultType is not None:
s = s + 'defType=%r' % (self.__defType,) s += 'defaultType=%r' % (self.__defaultType,)
return s + ')' return s + ')'
def __str__(self): def __str__(self):
s = self.__class__.__name__ + ':\n' s = self.__class__.__name__ + ': '
if self.__posMap: if self.__presentTypes:
s = s + 'posMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__posMap.values()]) s += 'presentTypes: %s, ' % ', '.join([x.prettyPrintType() for x in self.__presentTypes.values()])
if self.__negMap: if self.__skipTypes:
s = s + 'negMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__negMap.values()]) s += 'skipTypes: %s, ' % ', '.join([x.prettyPrintType() for x in self.__skipTypes.values()])
if self.__defType is not None: if self.__defaultType is not None:
s = s + 'defType:\n%s, ' % self.__defType.prettyPrintType() s += 'defaultType: %s, ' % self.__defaultType.prettyPrintType()
return s return s
def clone(self, parentType, tagMap, uniq=False): @property
if self.__defType is not None and tagMap.getDef() is not None: def presentTypes(self):
raise error.PyAsn1Error('Duplicate default value at %s' % (self,)) """Return *TagSet* to ASN.1 type map present in callee *TagMap*"""
if tagMap.getDef() is not None: return self.__presentTypes
defType = tagMap.getDef()
else:
defType = self.__defType
posMap = self.__posMap.copy() @property
for k in tagMap.getPosMap(): def skipTypes(self):
if uniq and k in posMap: """Return *TagSet* collection unconditionally absent in callee *TagMap*"""
raise error.PyAsn1Error('Duplicate positive key %s' % (k,)) return self.__skipTypes
posMap[k] = parentType
negMap = self.__negMap.copy() @property
negMap.update(tagMap.getNegMap()) def defaultType(self):
"""Return default ASN.1 type being returned for any missing *TagSet*"""
return self.__defaultType
return self.__class__( # Backward compatibility
posMap, negMap, defType,
)
def getPosMap(self): return self.__posMap.copy() def getPosMap(self):
def getNegMap(self): return self.__negMap.copy() return self.presentTypes
def getDef(self): return self.__defType
def getNegMap(self):
return self.skipTypes
def getDef(self):
return self.defaultType

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,187 @@
# ASN.1 "useful" types #
from pyasn1.type import char, tag # This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import datetime
from pyasn1.type import univ, char, tag
from pyasn1.compat import string, dateandtime
from pyasn1 import error
__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
NoValue = univ.NoValue
noValue = univ.noValue
class ObjectDescriptor(char.GraphicString): class ObjectDescriptor(char.GraphicString):
__doc__ = char.GraphicString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.GraphicString.tagSet.tagImplicitly( tagSet = char.GraphicString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
) )
class GeneralizedTime(char.VisibleString): # Optimization for faster codec lookup
typeId = char.GraphicString.getTypeId()
class TimeMixIn(object):
_yearsDigits = 4
_hasSubsecond = False
_optionalMinutes = False
_shortTZ = False
class FixedOffset(datetime.tzinfo):
"""Fixed offset in minutes east from UTC."""
# defaulted arguments required
# https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html
def __init__(self, offset=0, name='UTC'):
self.__offset = datetime.timedelta(minutes=offset)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return datetime.timedelta(0)
UTC = FixedOffset()
@property
def asDateTime(self):
"""Create :py:class:`datetime.datetime` object from a |ASN.1| object.
Returns
-------
:
new instance of :py:class:`datetime.datetime` object
"""
text = str(self)
if text.endswith('Z'):
tzinfo = TimeMixIn.UTC
text = text[:-1]
elif '-' in text or '+' in text:
if '+' in text:
text, plusminus, tz = string.partition(text, '+')
else:
text, plusminus, tz = string.partition(text, '-')
if self._shortTZ and len(tz) == 2:
tz += '00'
if len(tz) != 4:
raise error.PyAsn1Error('malformed time zone offset %s' % tz)
try:
minutes = int(tz[:2]) * 60 + int(tz[2:])
if plusminus == '-':
minutes *= -1
except ValueError:
raise error.PyAsn1Error('unknown time specification %s' % self)
tzinfo = TimeMixIn.FixedOffset(minutes, '?')
else:
tzinfo = None
if '.' in text or ',' in text:
if '.' in text:
text, _, ms = string.partition(text, '.')
else:
text, _, ms = string.partition(text, ',')
try:
ms = int(ms) * 10000
except ValueError:
raise error.PyAsn1Error('bad sub-second time specification %s' % self)
else:
ms = 0
if self._optionalMinutes and len(text) - self._yearsDigits == 6:
text += '0000'
elif len(text) - self._yearsDigits == 8:
text += '00'
try:
dt = dateandtime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
except ValueError:
raise error.PyAsn1Error('malformed datetime format %s' % self)
return dt.replace(microsecond=ms, tzinfo=tzinfo)
@classmethod
def fromDateTime(cls, dt):
"""Create |ASN.1| object from a :py:class:`datetime.datetime` object.
Parameters
----------
dt : :py:class:`datetime.datetime` object
The `datetime.datetime` object to initialize the |ASN.1| object from
Returns
-------
:
new instance of |ASN.1| value
"""
text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
if cls._hasSubsecond:
text += '.%d' % (dt.microsecond // 10000)
if dt.utcoffset():
seconds = dt.utcoffset().seconds
if seconds < 0:
text += '-'
else:
text += '+'
text += '%.2d%.2d' % (seconds // 3600, seconds % 3600)
else:
text += 'Z'
return cls(text)
class GeneralizedTime(char.VisibleString, TimeMixIn):
__doc__ = char.VisibleString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly( tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
) )
class UTCTime(char.VisibleString): # Optimization for faster codec lookup
typeId = char.VideotexString.getTypeId()
_yearsDigits = 4
_hasSubsecond = True
_optionalMinutes = True
_shortTZ = True
class UTCTime(char.VisibleString, TimeMixIn):
__doc__ = char.VisibleString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly( tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
) )
# Optimization for faster codec lookup
typeId = char.VideotexString.getTypeId()
_yearsDigits = 2
_hasSubsecond = False
_optionalMinutes = False
_shortTZ = False