mirror of
https://github.com/GAM-team/GAM.git
synced 2025-07-09 14:13:35 +00:00
upgrade pyasn1 to 0.3.7
This commit is contained in:
@ -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
@ -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:
|
||||||
@ -317,45 +356,51 @@ class RealEncoder(AbstractItemEncoder):
|
|||||||
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(),
|
||||||
@ -389,45 +434,115 @@ tagMap = {
|
|||||||
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)
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
1
src/pyasn1/codec/native/__init__.py
Normal file
1
src/pyasn1/codec/native/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# This file is necessary to make this directory a package.
|
194
src/pyasn1/codec/native/decoder.py
Normal file
194
src/pyasn1/codec/native/decoder.py
Normal 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)
|
212
src/pyasn1/codec/native/encoder.py
Normal file
212
src/pyasn1/codec/native/encoder.py
Normal 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)
|
@ -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
|
||||||
|
20
src/pyasn1/compat/calling.py
Normal file
20
src/pyasn1/compat/calling.py
Normal 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
|
22
src/pyasn1/compat/dateandtime.py
Normal file
22
src/pyasn1/compat/dateandtime.py
Normal 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)
|
108
src/pyasn1/compat/integer.py
Normal file
108
src/pyasn1/compat/integer.py
Normal 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()
|
@ -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
|
||||||
|
# noinspection PyPep8
|
||||||
ints2octs = lambda s: ''.join([int2oct(x) for x in s])
|
ints2octs = lambda s: ''.join([int2oct(x) for x in s])
|
||||||
null = ''
|
null = ''
|
||||||
oct2int = ord
|
oct2int = ord
|
||||||
|
# TODO: refactor to return a sequence of ints
|
||||||
|
# noinspection PyPep8
|
||||||
octs2ints = lambda s: [oct2int(x) for x in s]
|
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
|
||||||
|
26
src/pyasn1/compat/string.py
Normal file
26
src/pyasn1/compat/string.py
Normal 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)
|
@ -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
|
||||||
@ -15,61 +22,78 @@ flagMap = {
|
|||||||
'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,11 +107,18 @@ 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(
|
||||||
@ -95,7 +126,8 @@ def hexdump(octets):
|
|||||||
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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
|
||||||
def __init__(self, *namedValues):
|
class NamedValues(object):
|
||||||
self.nameToValIdx = {}; self.valToNameIdx = {}
|
"""Create named values object.
|
||||||
self.namedValues = ()
|
|
||||||
automaticVal = 1
|
The |NamedValues| object represents a collection of string names
|
||||||
for namedValue in namedValues:
|
associated with numeric IDs. These objects are used for giving
|
||||||
if isinstance(namedValue, tuple):
|
names to otherwise numerical values.
|
||||||
name, val = namedValue
|
|
||||||
|
|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,))
|
||||||
|
|
||||||
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):
|
|
||||||
return self.__class__(*tuple(self) + namedValues)
|
|
||||||
|
|
||||||
# XXX clone/subtype?
|
# 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),)
|
||||||
|
)
|
||||||
|
@ -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)
|
||||||
|
@ -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
@ -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
|
||||||
|
Reference in New Issue
Block a user