From 4e6ab1abf9985abe51c8fb03978a0ba085e2fb62 Mon Sep 17 00:00:00 2001 From: Ruud Date: Fri, 3 Jul 2015 13:47:09 +0200 Subject: [PATCH] Update pyasn1 --- libs/pyasn1/__init__.py | 4 +- libs/pyasn1/codec/ber/decoder.py | 153 ++++++++++++++++---------- libs/pyasn1/codec/ber/encoder.py | 168 ++++++++++++++++++++-------- libs/pyasn1/codec/cer/encoder.py | 61 +++++++++-- libs/pyasn1/codec/der/decoder.py | 4 +- libs/pyasn1/codec/der/encoder.py | 12 +- libs/pyasn1/compat/binary.py | 10 ++ libs/pyasn1/compat/iterfunc.py | 10 ++ libs/pyasn1/debug.py | 65 +++++++++-- libs/pyasn1/type/base.py | 78 ++++++++----- libs/pyasn1/type/char.py | 17 +-- libs/pyasn1/type/namedtype.py | 31 ++++-- libs/pyasn1/type/namedval.py | 12 ++ libs/pyasn1/type/tag.py | 10 +- libs/pyasn1/type/tagmap.py | 18 ++- libs/pyasn1/type/univ.py | 230 +++++++++++++++++++++++++++++---------- libs/pyasn1/type/useful.py | 5 + 17 files changed, 656 insertions(+), 232 deletions(-) create mode 100644 libs/pyasn1/compat/binary.py create mode 100644 libs/pyasn1/compat/iterfunc.py diff --git a/libs/pyasn1/__init__.py b/libs/pyasn1/__init__.py index 88aff79..77793cd 100644 --- a/libs/pyasn1/__init__.py +++ b/libs/pyasn1/__init__.py @@ -1,8 +1,8 @@ import sys # http://www.python.org/dev/peps/pep-0396/ -__version__ = '0.1.7' +__version__ = '0.1.8' 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') diff --git a/libs/pyasn1/codec/ber/decoder.py b/libs/pyasn1/codec/ber/decoder.py index be0cf49..61bfbce 100644 --- a/libs/pyasn1/codec/ber/decoder.py +++ b/libs/pyasn1/codec/ber/decoder.py @@ -1,7 +1,7 @@ # BER decoder -from pyasn1.type import tag, base, univ, char, useful, tagmap +from pyasn1.type import tag, univ, char, useful, tagmap from pyasn1.codec.ber import eoo -from pyasn1.compat.octets import oct2int, octs2ints, isOctetsType +from pyasn1.compat.octets import oct2int, isOctetsType from pyasn1 import debug, error class AbstractDecoder: @@ -11,14 +11,14 @@ class AbstractDecoder: raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,)) def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, - length, state, decodeFun, substrateFun): + length, state, decodeFun, substrateFun): raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,)) class AbstractSimpleDecoder(AbstractDecoder): tagFormats = (tag.tagFormatSimple,) def _createComponent(self, asn1Spec, tagSet, value=None): if tagSet[0][1] not in self.tagFormats: - raise error.PyAsn1Error('Invalid tag format %r for %r' % (tagSet[0], self.protoComponent,)) + raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType())) if asn1Spec is None: return self.protoComponent.clone(value, tagSet) elif value is None: @@ -30,17 +30,12 @@ class AbstractConstructedDecoder(AbstractDecoder): tagFormats = (tag.tagFormatConstructed,) def _createComponent(self, asn1Spec, tagSet, value=None): if tagSet[0][1] not in self.tagFormats: - raise error.PyAsn1Error('Invalid tag format %r for %r' % (tagSet[0], self.protoComponent,)) + raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType())) if asn1Spec is None: return self.protoComponent.clone(tagSet) else: return asn1Spec.clone() -class EndOfOctetsDecoder(AbstractSimpleDecoder): - def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, - length, state, decodeFun, substrateFun): - return eoo.endOfOctets, substrate[length:] - class ExplicitTagDecoder(AbstractSimpleDecoder): protoComponent = univ.Any('') tagFormats = (tag.tagFormatConstructed,) @@ -63,7 +58,7 @@ class ExplicitTagDecoder(AbstractSimpleDecoder): substrate, length ) value, substrate = decodeFun(substrate, asn1Spec, tagSet, length) - terminator, substrate = decodeFun(substrate) + terminator, substrate = decodeFun(substrate, allowEoo=True) if eoo.endOfOctets.isSameTypeWith(terminator) and \ terminator == eoo.endOfOctets: return value, substrate @@ -129,14 +124,14 @@ class BitStringDecoder(AbstractSimpleDecoder): 'Trailing bits overflow %s' % trailingBits ) head = head[1:] - lsb = p = 0; l = len(head)-1; b = () + lsb = p = 0; l = len(head)-1; b = [] while p <= l: if p == l: lsb = trailingBits j = 7 o = oct2int(head[p]) while j >= lsb: - b = b + ((o>>j)&0x01,) + b.append((o>>j)&0x01) j = j - 1 p = p + 1 return self._createComponent(asn1Spec, tagSet, b), tail @@ -144,7 +139,7 @@ class BitStringDecoder(AbstractSimpleDecoder): if substrateFun: return substrateFun(r, substrate, length) while head: - component, head = decodeFun(head) + component, head = decodeFun(head, self.protoComponent) r = r + component return r, tail @@ -154,7 +149,8 @@ class BitStringDecoder(AbstractSimpleDecoder): if substrateFun: return substrateFun(r, substrate, length) while substrate: - component, substrate = decodeFun(substrate) + component, substrate = decodeFun(substrate, self.protoComponent, + allowEoo=True) if eoo.endOfOctets.isSameTypeWith(component) and \ component == eoo.endOfOctets: break @@ -177,7 +173,7 @@ class OctetStringDecoder(AbstractSimpleDecoder): if substrateFun: return substrateFun(r, substrate, length) while head: - component, head = decodeFun(head) + component, head = decodeFun(head, self.protoComponent) r = r + component return r, tail @@ -187,7 +183,8 @@ class OctetStringDecoder(AbstractSimpleDecoder): if substrateFun: return substrateFun(r, substrate, length) while substrate: - component, substrate = decodeFun(substrate) + component, substrate = decodeFun(substrate, self.protoComponent, + allowEoo=True) if eoo.endOfOctets.isSameTypeWith(component) and \ component == eoo.endOfOctets: break @@ -216,20 +213,14 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder): if not head: raise error.PyAsn1Error('Empty substrate') - # Get the first subid - subId = oct2int(head[0]) - oid = divmod(subId, 40) - - index = 1 + oid = () + index = 0 substrateLen = len(head) while index < substrateLen: subId = oct2int(head[index]) - index = index + 1 - if subId == 128: - # ASN.1 spec forbids leading zeros (0x80) in sub-ID OID - # encoding, tolerating it opens a vulnerability. - # See http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf page 7 - raise error.PyAsn1Error('Invalid leading 0x80 in sub-OID') + index += 1 + if subId < 128: + oid = oid + (subId,) elif subId > 128: # Construct subid from a number of octets nextSubId = subId @@ -239,11 +230,27 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder): if index >= substrateLen: raise error.SubstrateUnderrunError( 'Short substrate for sub-OID past %s' % (oid,) - ) + ) nextSubId = oct2int(head[index]) - index = index + 1 - subId = (subId << 7) + nextSubId - oid = oid + (subId,) + index += 1 + oid = oid + ((subId << 7) + nextSubId,) + elif subId == 128: + # ASN.1 spec forbids leading zeros (0x80) in OID + # encoding, tolerating it opens a vulnerability. See + # http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf + # page 7 + raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding') + + # Decode two leading arcs + if 0 <= oid[0] <= 39: + oid = (0,) + oid + elif 40 <= oid[0] <= 79: + oid = (1, oid[0]-40) + oid[1:] + elif oid[0] >= 80: + oid = (2, oid[0]-80) + oid[1:] + else: + raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0]) + return self._createComponent(asn1Spec, tagSet, oid), tail class RealDecoder(AbstractSimpleDecoder): @@ -254,10 +261,13 @@ class RealDecoder(AbstractSimpleDecoder): if not head: return self._createComponent(asn1Spec, tagSet, 0.0), tail fo = oct2int(head[0]); head = head[1:] - if fo & 0x80: # binary enoding + if fo & 0x80: # binary encoding + if not head: + raise error.PyAsn1Error("Incomplete floating-point value") n = (fo & 0x03) + 1 if n == 4: n = oct2int(head[0]) + head = head[1:] eo, head = head[:n], head[n:] if not eo or not head: raise error.PyAsn1Error('Real exponent screwed') @@ -266,6 +276,13 @@ class RealDecoder(AbstractSimpleDecoder): e <<= 8 e |= oct2int(eo[0]) eo = eo[1:] + b = fo >> 4 & 0x03 # base bits + if b > 2: + raise error.PyAsn1Error('Illegal Real base') + if b == 1: # encbase = 8 + e *= 3 + elif b == 2: # encbase = 16 + e *= 4 p = 0 while head: # value p <<= 8 @@ -273,10 +290,14 @@ class RealDecoder(AbstractSimpleDecoder): head = head[1:] if fo & 0x40: # sign bit p = -p + sf = fo >> 2 & 0x03 # scale bits + p *= 2**sf value = (p, 2, e) elif fo & 0x40: # infinite value value = fo & 0x01 and '-inf' or 'inf' elif fo & 0xc0 == 0: # character encoding + if not head: + raise error.PyAsn1Error("Incomplete floating-point value") try: if fo & 0x3 == 0x1: # NR1 value = (int(head), 10, 0) @@ -336,7 +357,7 @@ class SequenceDecoder(AbstractConstructedDecoder): idx = 0 while substrate: asn1Spec = self._getComponentTagMap(r, idx) - component, substrate = decodeFun(substrate, asn1Spec) + component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True) if eoo.endOfOctets.isSameTypeWith(component) and \ component == eoo.endOfOctets: break @@ -378,7 +399,7 @@ class SequenceOfDecoder(AbstractConstructedDecoder): asn1Spec = r.getComponentType() idx = 0 while substrate: - component, substrate = decodeFun(substrate, asn1Spec) + component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True) if eoo.endOfOctets.isSameTypeWith(component) and \ component == eoo.endOfOctets: break @@ -437,7 +458,8 @@ class ChoiceDecoder(AbstractConstructedDecoder): return substrateFun(r, substrate, length) if r.getTagSet() == tagSet: # explicitly tagged Choice component, substrate = decodeFun(substrate, r.getComponentTagMap()) - eooMarker, substrate = decodeFun(substrate) # eat up EOO marker + # eat up EOO marker + eooMarker, substrate = decodeFun(substrate, allowEoo=True) if not eoo.endOfOctets.isSameTypeWith(eooMarker) or \ eooMarker != eoo.endOfOctets: raise error.PyAsn1Error('No EOO seen before substrate ends') @@ -485,7 +507,7 @@ class AnyDecoder(AbstractSimpleDecoder): if substrateFun: return substrateFun(r, substrate, length) while substrate: - component, substrate = decodeFun(substrate, asn1Spec) + component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True) if eoo.endOfOctets.isSameTypeWith(component) and \ component == eoo.endOfOctets: break @@ -521,13 +543,14 @@ class BMPStringDecoder(OctetStringDecoder): protoComponent = char.BMPString() # "useful" types +class ObjectDescriptorDecoder(OctetStringDecoder): + protoComponent = useful.ObjectDescriptor() class GeneralizedTimeDecoder(OctetStringDecoder): protoComponent = useful.GeneralizedTime() class UTCTimeDecoder(OctetStringDecoder): protoComponent = useful.UTCTime() tagMap = { - eoo.endOfOctets.tagSet: EndOfOctetsDecoder(), univ.Integer.tagSet: IntegerDecoder(), univ.Boolean.tagSet: BooleanDecoder(), univ.BitString.tagSet: BitStringDecoder(), @@ -552,9 +575,10 @@ tagMap = { char.UniversalString.tagSet: UniversalStringDecoder(), char.BMPString.tagSet: BMPStringDecoder(), # useful types + useful.ObjectDescriptor.tagSet: ObjectDescriptorDecoder(), useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(), useful.UTCTime.tagSet: UTCTimeDecoder() - } +} # Type-to-codec map for ambiguous ASN.1 types typeMap = { @@ -564,7 +588,7 @@ typeMap = { univ.SequenceOf.typeId: SequenceOfDecoder(), univ.Choice.typeId: ChoiceDecoder(), univ.Any.typeId: AnyDecoder() - } +} ( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec, stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue, @@ -574,23 +598,22 @@ class Decoder: defaultErrorState = stErrorCondition # defaultErrorState = stDumpRawValue defaultRawDecoder = AnyDecoder() + supportIndefLength = True def __init__(self, tagMap, typeMap={}): self.__tagMap = tagMap self.__typeMap = typeMap - self.__endOfOctetsTagSet = eoo.endOfOctets.getTagSet() # Tag & TagSet objects caches self.__tagCache = {} self.__tagSetCache = {} def __call__(self, substrate, asn1Spec=None, tagSet=None, length=None, state=stDecodeTag, recursiveFlag=1, - substrateFun=None): + substrateFun=None, allowEoo=False): if debug.logger & debug.flagDecoder: debug.logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate))) fullSubstrate = substrate while state != stStop: if state == stDecodeTag: - # Decode tag if not substrate: raise error.SubstrateUnderrunError( 'Short octet stream on tag decoding' @@ -598,13 +621,25 @@ class Decoder: if not isOctetsType(substrate) and \ not isinstance(substrate, univ.OctetString): raise error.PyAsn1Error('Bad octet stream type') - + # Decode tag firstOctet = substrate[0] substrate = substrate[1:] if firstOctet in self.__tagCache: lastTag = self.__tagCache[firstOctet] else: t = oct2int(firstOctet) + # Look for end-of-octets sentinel + if t == 0: + if substrate and oct2int(substrate[0]) == 0: + if allowEoo and self.supportIndefLength: + debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets sentinel found') + value, substrate = eoo.endOfOctets, substrate[1:] + state = stStop + continue + else: + raise error.PyAsn1Error('Unexpected end-of-contents sentinel') + else: + raise error.PyAsn1Error('Zero tag encountered') tagClass = t&0xC0 tagFormat = t&0x20 tagId = t&0x1F @@ -622,7 +657,7 @@ class Decoder: break lastTag = tag.Tag( tagClass=tagClass, tagFormat=tagFormat, tagId=tagId - ) + ) if tagId < 31: # cache short tags self.__tagCache[firstOctet] = lastTag @@ -637,13 +672,13 @@ class Decoder: else: tagSet = lastTag + tagSet state = stDecodeLength - debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %r, decoding length' % tagSet) + debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %s, decoding length' % tagSet) if state == stDecodeLength: # Decode length if not substrate: - raise error.SubstrateUnderrunError( - 'Short octet stream on length decoding' - ) + raise error.SubstrateUnderrunError( + 'Short octet stream on length decoding' + ) firstOctet = oct2int(substrate[0]) if firstOctet == 128: size = 1 @@ -670,6 +705,8 @@ class Decoder: raise error.SubstrateUnderrunError( '%d-octet short' % (length - len(substrate)) ) + if length == -1 and not self.supportIndefLength: + error.PyAsn1Error('Indefinite length encoding not supported by this codec') state = stGetValueDecoder debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length]))) if state == stGetValueDecoder: @@ -722,12 +759,12 @@ class Decoder: if debug.logger and debug.logger & debug.flagDecoder: debug.logger('candidate ASN.1 spec is a map of:') for t, v in asn1Spec.getPosMap().items(): - debug.logger(' %r -> %s' % (t, v.__class__.__name__)) + debug.logger(' %s -> %s' % (t, v.__class__.__name__)) if asn1Spec.getNegMap(): debug.logger('but neither of: ') - for i in asn1Spec.getNegMap().items(): - debug.logger(' %r -> %s' % (t, v.__class__.__name__)) - debug.logger('new candidate ASN.1 spec is %s, chosen by %r' % (__chosenSpec is None and '' or __chosenSpec.__class__.__name__, tagSet)) + for t, v in asn1Spec.getNegMap().items(): + debug.logger(' %s -> %s' % (t, v.__class__.__name__)) + debug.logger('new candidate ASN.1 spec is %s, chosen by %s' % (__chosenSpec is None and '' or __chosenSpec.prettyPrintType(), tagSet)) else: __chosenSpec = asn1Spec debug.logger and debug.logger & debug.flagDecoder and debug.logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__) @@ -745,7 +782,7 @@ class Decoder: elif baseTagSet in self.__tagMap: # base type or tagged subtype concreteDecoder = self.__tagMap[baseTagSet] - debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen by base %r' % (baseTagSet,)) + debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen by base %s' % (baseTagSet,)) else: concreteDecoder = None if concreteDecoder: @@ -753,10 +790,6 @@ class Decoder: state = stDecodeValue else: state = stTryAsExplicitTag - elif tagSet == self.__endOfOctetsTagSet: - concreteDecoder = self.__tagMap[tagSet] - state = stDecodeValue - debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets found') else: concreteDecoder = None state = stTryAsExplicitTag @@ -795,8 +828,8 @@ class Decoder: debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, value.prettyPrint(), substrate and debug.hexdump(substrate) or '')) if state == stErrorCondition: raise error.PyAsn1Error( - '%r not in asn1Spec: %r' % (tagSet, asn1Spec) - ) + '%s not in asn1Spec: %s' % (tagSet, asn1Spec) + ) if debug.logger and debug.logger & debug.flagDecoder: debug.scope.pop() debug.logger('decoder left scope %s, call completed' % debug.scope) diff --git a/libs/pyasn1/codec/ber/encoder.py b/libs/pyasn1/codec/ber/encoder.py index 173949d..0fb4ae7 100644 --- a/libs/pyasn1/codec/ber/encoder.py +++ b/libs/pyasn1/codec/ber/encoder.py @@ -114,13 +114,17 @@ class IntegerEncoder(AbstractItemEncoder): class BitStringEncoder(AbstractItemEncoder): def encodeValue(self, encodeFun, value, defMode, maxChunkSize): if not maxChunkSize or len(value) <= maxChunkSize*8: - r = {}; l = len(value); p = 0; j = 7 - while p < l: - i, j = divmod(p, 8) - r[i] = r.get(i,0) | value[p]<<(7-j) - p = p + 1 - keys = list(r); keys.sort() - return int2oct(7-j) + ints2octs([r[k] for k in keys]), 0 + out_len = (len(value) + 7) // 8 + out_list = out_len * [0] + 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: pos = 0; substrate = null while 1: @@ -156,47 +160,98 @@ class ObjectIdentifierEncoder(AbstractItemEncoder): precomputedValues = { (1, 3, 6, 1, 2): (43, 6, 1, 2), (1, 3, 6, 1, 4): (43, 6, 1, 4) - } + } def encodeValue(self, encodeFun, value, defMode, maxChunkSize): oid = value.asTuple() if oid[:5] in self.precomputedValues: octets = self.precomputedValues[oid[:5]] - index = 5 + oid = oid[5:] else: if len(oid) < 2: raise error.PyAsn1Error('Short OID %s' % (value,)) + octets = () + # Build the first twos - if oid[0] > 6 or oid[1] > 39 or oid[0] == 6 and oid[1] > 15: + 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( - 'Initial sub-ID overflow %s in OID %s' % (oid[:2], value) + 'Impossible initial arcs %s at %s' % (oid[:2], value) ) - octets = (oid[0] * 40 + oid[1],) - index = 2 - # Cycle through subids - for subid in oid[index:]: - if subid > -1 and subid < 128: + # Cycle through subIds + for subId in oid: + if subId > -1 and subId < 128: # Optimize for the common case - octets = octets + (subid & 0x7f,) - elif subid < 0 or subid > 0xFFFFFFFF: + octets = octets + (subId & 0x7f,) + elif subId < 0: raise error.PyAsn1Error( - 'SubId overflow %s in %s' % (subid, value) - ) + 'Negative OID arc %s at %s' % (subId, value) + ) else: # Pack large Sub-Object IDs - res = (subid & 0x7f,) - subid = subid >> 7 - while subid > 0: - res = (0x80 | (subid & 0x7f),) + res - subid = subid >> 7 + res = (subId & 0x7f,) + subId = subId >> 7 + while subId > 0: + res = (0x80 | (subId & 0x7f),) + res + subId = subId >> 7 # Add packed Sub-Object ID to resulted Object ID octets += res - + return ints2octs(octets), 0 class RealEncoder(AbstractItemEncoder): supportIndefLenMode = 0 + binEncBase = 2 # set to None to choose encoding base automatically + def _dropFloatingPoint(self, m, encbase, e): + ms, es = 1, 1 + if m < 0: + ms = -1 # mantissa sign + if e < 0: + es = -1 # exponenta sign + m *= ms + if encbase == 8: + m = m*2**(abs(e) % 3 * es) + e = abs(e) // 3 * es + elif encbase == 16: + m = m*2**(abs(e) % 4 * es) + e = abs(e) // 4 * es + + while 1: + if int(m) != m: + m *= encbase + e -= 1 + continue + break + return ms, int(m), encbase, e + + def _chooseEncBase(self, value): + m, b, e = value + base = [2, 8, 16] + if value.binEncBase in base: + return self._dropFloatingPoint(m, value.binEncBase, e) + elif self.binEncBase in base: + return self._dropFloatingPoint(m, self.binEncBase, e) + # auto choosing base 2/8/16 + mantissa = [m, m, m] + exponenta = [e, e, e] + encbase = 2 + e = float('inf') + for i in range(3): + sign, mantissa[i], base[i], exponenta[i] = \ + self._dropFloatingPoint(mantissa[i], base[i], exponenta[i]) + if abs(exponenta[i]) < abs(e) or \ + (abs(exponenta[i]) == abs(e) and mantissa[i] < m): + e = exponenta[i] + m = int(mantissa[i]) + encbase = base[i] + return sign, m, encbase, e + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): if value.isPlusInfinity(): return int2oct(0x40), 0 @@ -208,22 +263,43 @@ class RealEncoder(AbstractItemEncoder): if b == 10: return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0 elif b == 2: - fo = 0x80 # binary enoding - if m < 0: - fo = fo | 0x40 # sign bit - m = -m - while int(m) != m: # drop floating point - m *= 2 - e -= 1 - while m & 0x1 == 0: # mantissa normalization + fo = 0x80 # binary encoding + ms, m, encbase, e = self._chooseEncBase(value) + if ms < 0: # mantissa sign + fo = fo | 0x40 # sign bit + # exponenta & mantissa normalization + if encbase == 2: + while m & 0x1 == 0: + m >>= 1 + e += 1 + elif encbase == 8: + while m & 0x7 == 0: + m >>= 3 + e += 1 + fo |= 0x10 + else: # encbase = 16 + while m & 0xf == 0: + m >>= 4 + e += 1 + fo |= 0x20 + sf = 0 # scale factor + while m & 0x1 == 0: m >>= 1 - e += 1 + sf += 1 + if sf > 3: + raise error.PyAsn1Error('Scale factor overflow') # bug if raised + fo |= sf << 2 eo = null - while e not in (0, -1): - eo = int2oct(e&0xff) + eo - e >>= 8 - if e == 0 and eo and oct2int(eo[0]) & 0x80: - eo = int2oct(0) + eo + if e == 0 or e == -1: + eo = int2oct(e&0xff) + else: + while e not in (0, -1): + eo = int2oct(e&0xff) + eo + e >>= 8 + if e == 0 and eo and oct2int(eo[0]) & 0x80: + eo = int2oct(0) + eo + if e == -1 and eo and not (oct2int(eo[0]) & 0x80): + eo = int2oct(0xff) + eo n = len(eo) if n > 0xff: raise error.PyAsn1Error('Real exponent overflow') @@ -235,7 +311,7 @@ class RealEncoder(AbstractItemEncoder): fo |= 2 else: fo |= 3 - eo = int2oct(n//0xff+1) + eo + eo = int2oct(n&0xff) + eo po = null while m: po = int2oct(m&0xff) + po @@ -308,6 +384,7 @@ tagMap = { char.UniversalString.tagSet: OctetStringEncoder(), char.BMPString.tagSet: OctetStringEncoder(), # useful types + useful.ObjectDescriptor.tagSet: OctetStringEncoder(), useful.GeneralizedTime.tagSet: OctetStringEncoder(), useful.UTCTime.tagSet: OctetStringEncoder() } @@ -323,12 +400,15 @@ typeMap = { } class Encoder: + supportIndefLength = True def __init__(self, tagMap, typeMap={}): self.__tagMap = tagMap self.__typeMap = typeMap - def __call__(self, value, defMode=1, maxChunkSize=0): - 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.__class__.__name__, value.prettyPrint())) + def __call__(self, value, defMode=True, maxChunkSize=0): + if not defMode and not self.supportIndefLength: + raise error.PyAsn1Error('Indefinite length encoding not supported by this codec') + 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())) tagSet = value.getTagSet() if len(tagSet) > 1: concreteEncoder = explicitlyTaggedItemEncoder @@ -343,7 +423,7 @@ class Encoder: concreteEncoder = self.__tagMap[tagSet] else: raise Error('No encoder for %s' % (value,)) - debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %r' % (concreteEncoder.__class__.__name__, tagSet)) + debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet)) substrate = concreteEncoder.encode( self, value, defMode, maxChunkSize ) diff --git a/libs/pyasn1/codec/cer/encoder.py b/libs/pyasn1/codec/cer/encoder.py index 4c05130..61ce8a1 100644 --- a/libs/pyasn1/codec/cer/encoder.py +++ b/libs/pyasn1/codec/cer/encoder.py @@ -1,7 +1,9 @@ # CER encoder from pyasn1.type import univ +from pyasn1.type import useful from pyasn1.codec.ber import encoder -from pyasn1.compat.octets import int2oct, null +from pyasn1.compat.octets import int2oct, str2octs, null +from pyasn1 import error class BooleanEncoder(encoder.IntegerEncoder): def encodeValue(self, encodeFun, client, defMode, maxChunkSize): @@ -15,18 +17,56 @@ 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): + def _chooseEncBase(self, value): + m, b, e = value + return self._dropFloatingPoint(m, b, e) -# specialized RealEncoder here # specialized GeneralStringEncoder here -# specialized GeneralizedTimeEncoder here -# specialized UTCTimeEncoder here + +class GeneralizedTimeEncoder(OctetStringEncoder): + zchar = str2octs('Z') + pluschar = str2octs('+') + minuschar = str2octs('-') + zero = str2octs('0') + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + octets = client.asOctets() +# This breaks too many existing data items +# if '.' not in octets: +# raise error.PyAsn1Error('Format must include fraction of second: %r' % octets) + if len(octets) < 15: + raise error.PyAsn1Error('Bad UTC time length: %r' % octets) + if self.pluschar in octets or self.minuschar in octets: + raise error.PyAsn1Error('Must be UTC time: %r' % octets) + if octets[-1] != self.zchar[0]: + raise error.PyAsn1Error('Missing timezone specifier: %r' % octets) + return encoder.OctetStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class UTCTimeEncoder(encoder.OctetStringEncoder): + zchar = str2octs('Z') + pluschar = str2octs('+') + minuschar = str2octs('-') + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + octets = client.asOctets() + if self.pluschar in octets or self.minuschar in octets: + raise error.PyAsn1Error('Must be UTC time: %r' % octets) + if octets and octets[-1] != self.zchar[0]: + 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): def encodeValue(self, encodeFun, client, defMode, maxChunkSize): @@ -69,17 +109,20 @@ tagMap.update({ univ.Boolean.tagSet: BooleanEncoder(), univ.BitString.tagSet: BitStringEncoder(), univ.OctetString.tagSet: OctetStringEncoder(), + univ.Real.tagSet: RealEncoder(), + useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(), + useful.UTCTime.tagSet: UTCTimeEncoder(), univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set - }) +}) typeMap = encoder.typeMap.copy() typeMap.update({ univ.Set.typeId: SetOfEncoder(), univ.SetOf.typeId: SetOfEncoder() - }) +}) class Encoder(encoder.Encoder): - def __call__(self, client, defMode=0, maxChunkSize=0): + def __call__(self, client, defMode=False, maxChunkSize=0): return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) encode = Encoder(tagMap, typeMap) diff --git a/libs/pyasn1/codec/der/decoder.py b/libs/pyasn1/codec/der/decoder.py index 604abec..ea58d6d 100644 --- a/libs/pyasn1/codec/der/decoder.py +++ b/libs/pyasn1/codec/der/decoder.py @@ -1,9 +1,9 @@ # DER decoder -from pyasn1.type import univ from pyasn1.codec.cer import decoder tagMap = decoder.tagMap typeMap = decoder.typeMap -Decoder = decoder.Decoder +class Decoder(decoder.Decoder): + supportIndefLength = False decode = Decoder(tagMap, typeMap) diff --git a/libs/pyasn1/codec/der/encoder.py b/libs/pyasn1/codec/der/encoder.py index 4e5faef..7f55eeb 100644 --- a/libs/pyasn1/codec/der/encoder.py +++ b/libs/pyasn1/codec/der/encoder.py @@ -1,6 +1,7 @@ # DER encoder from pyasn1.type import univ from pyasn1.codec.cer import encoder +from pyasn1 import error class SetOfEncoder(encoder.SetOfEncoder): def _cmpSetComponents(self, c1, c2): @@ -12,17 +13,20 @@ class SetOfEncoder(encoder.SetOfEncoder): tagMap = encoder.tagMap.copy() tagMap.update({ - # Overload CER encodrs with BER ones (a bit hackerish XXX) + # Overload CER encoders with BER ones (a bit hackerish XXX) univ.BitString.tagSet: encoder.encoder.BitStringEncoder(), univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(), # Set & SetOf have same tags univ.SetOf().tagSet: SetOfEncoder() - }) +}) typeMap = encoder.typeMap class Encoder(encoder.Encoder): - def __call__(self, client, defMode=1, maxChunkSize=0): + supportIndefLength = False + def __call__(self, client, defMode=True, maxChunkSize=0): + if not defMode: + raise error.PyAsn1Error('DER forbids indefinite length mode') return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) - + encode = Encoder(tagMap, typeMap) diff --git a/libs/pyasn1/compat/binary.py b/libs/pyasn1/compat/binary.py new file mode 100644 index 0000000..b38932a --- /dev/null +++ b/libs/pyasn1/compat/binary.py @@ -0,0 +1,10 @@ +from sys import version_info + +if version_info[0:2] < (2, 6): + def bin(x): + if x <= 1: + return '0b'+str(x) + else: + return bin(x>>1) + str(x&1) +else: + bin = bin diff --git a/libs/pyasn1/compat/iterfunc.py b/libs/pyasn1/compat/iterfunc.py new file mode 100644 index 0000000..0720bde --- /dev/null +++ b/libs/pyasn1/compat/iterfunc.py @@ -0,0 +1,10 @@ +from sys import version_info + +if version_info[0] <= 2 and version_info[1] <= 4: + def all(iterable): + for element in iterable: + if not element: + return False + return True +else: + all = all diff --git a/libs/pyasn1/debug.py b/libs/pyasn1/debug.py index c27cb1d..9b69886 100644 --- a/libs/pyasn1/debug.py +++ b/libs/pyasn1/debug.py @@ -1,4 +1,5 @@ -import sys +import time +import logging from pyasn1.compat.octets import octs2ints from pyasn1 import error from pyasn1 import __version__ @@ -14,23 +15,67 @@ flagMap = { 'all': flagAll } +class Printer: + def __init__(self, logger=None, handler=None, formatter=None): + if logger is None: + logger = logging.getLogger('pyasn1') + logger.setLevel(logging.DEBUG) + if handler is None: + handler = logging.StreamHandler() + if formatter is None: + formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s') + handler.setFormatter(formatter) + handler.setLevel(logging.DEBUG) + logger.addHandler(handler) + self.__logger = logger + + def __call__(self, msg): self.__logger.debug(msg) + def __str__(self): return '' + +if hasattr(logging, 'NullHandler'): + NullHandler = logging.NullHandler +else: + # Python 2.6 and older + class NullHandler(logging.Handler): + def emit(self, record): + pass + class Debug: - defaultPrinter = sys.stderr.write - def __init__(self, *flags): + defaultPrinter = None + def __init__(self, *flags, **options): self._flags = flagNone - self._printer = self.defaultPrinter + 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: + # route our logs to parent logger + self._printer = Printer( + logger=logging.getLogger(options['loggerName']), + handler=NullHandler() + ) + else: + self._printer = Printer() self('running pyasn1 version %s' % __version__) for f in flags: - if f not in flagMap: - raise error.PyAsn1Error('bad debug flag %s' % (f,)) - self._flags = self._flags | flagMap[f] - self('debug category \'%s\' enabled' % f) - + inverse = f and f[0] in ('!', '~') + if inverse: + f = f[1:] + try: + if inverse: + self._flags &= ~flagMap[f] + else: + self._flags |= flagMap[f] + except KeyError: + raise error.PyAsn1Error('bad debug flag %s' % f) + + self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled')) + def __str__(self): return 'logger %s, flags %x' % (self._printer, self._flags) def __call__(self, msg): - self._printer('DBG: %s\n' % msg) + self._printer(msg) def __and__(self, flag): return self._flags & flag diff --git a/libs/pyasn1/type/base.py b/libs/pyasn1/type/base.py index 4087371..155ed74 100644 --- a/libs/pyasn1/type/base.py +++ b/libs/pyasn1/type/base.py @@ -1,13 +1,13 @@ # Base classes for ASN.1 types import sys -from pyasn1.type import constraint, tagmap +from pyasn1.type import constraint, tagmap, tag from pyasn1 import error class Asn1Item: pass class Asn1ItemBase(Asn1Item): # Set of tags for this ASN.1 type - tagSet = () + tagSet = tag.TagSet() # A list of constraint.Constraint instances for checking values subtypeSpec = constraint.ConstraintsIntersection() @@ -38,22 +38,28 @@ class Asn1ItemBase(Asn1Item): def getEffectiveTagSet(self): return self._tagSet # used by untagged types def getTagMap(self): return tagmap.TagMap({self._tagSet: self}) - def isSameTypeWith(self, other): + def isSameTypeWith(self, other, matchTags=True, matchConstraints=True): return self is other or \ - self._tagSet == other.getTagSet() and \ - self._subtypeSpec == other.getSubtypeSpec() - def isSuperTypeOf(self, other): + (not matchTags or \ + self._tagSet == other.getTagSet()) and \ + (not matchConstraints or \ + self._subtypeSpec==other.getSubtypeSpec()) + + def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True): """Returns true if argument is a ASN1 subtype of ourselves""" - return self._tagSet.isSuperTagSetOf(other.getTagSet()) and \ - self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec()) + return (not matchTags or \ + self._tagSet.isSuperTagSetOf(other.getTagSet())) and \ + (not matchConstraints or \ + (self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec()))) -class __NoValue: +class NoValue: def __getattr__(self, attr): raise error.PyAsn1Error('No value for %s()' % attr) def __getitem__(self, i): raise error.PyAsn1Error('No value') + def __repr__(self): return '%s()' % self.__class__.__name__ -noValue = __NoValue() +noValue = NoValue() # Base class for "simple" ASN.1 objects. These are immutable. class AbstractSimpleAsn1Item(Asn1ItemBase): @@ -72,10 +78,15 @@ class AbstractSimpleAsn1Item(Asn1ItemBase): self._len = None def __repr__(self): - if self._value is noValue: - return self.__class__.__name__ + '()' - else: - return self.__class__.__name__ + '(%s)' % (self.prettyOut(self._value),) + r = [] + if self._value is not self.defaultValue: + r.append(self.prettyOut(self._value)) + if self._tagSet is not self.tagSet: + r.append('tagSet=%r' % (self._tagSet,)) + if self._subtypeSpec is not self.subtypeSpec: + r.append('subtypeSpec=%r' % (self._subtypeSpec,)) + return '%s(%s)' % (self.__class__.__name__, ', '.join(r)) + def __str__(self): return str(self._value) def __eq__(self, other): return self is other and True or self._value == other @@ -90,6 +101,9 @@ class AbstractSimpleAsn1Item(Asn1ItemBase): def __bool__(self): return bool(self._value) def __hash__(self): return self.__hashedValue + def hasValue(self): + return not isinstance(self._value, NoValue) + def clone(self, value=None, tagSet=None, subtypeSpec=None): if value is None and tagSet is None and subtypeSpec is None: return self @@ -121,14 +135,17 @@ class AbstractSimpleAsn1Item(Asn1ItemBase): def prettyOut(self, value): return str(value) def prettyPrint(self, scope=0): - if self._value is noValue: - return '' - else: + if self.hasValue(): return self.prettyOut(self._value) + else: + return '' # XXX Compatibility stub def prettyPrinter(self, scope=0): return self.prettyPrint(scope) + def prettyPrintType(self, scope=0): + return '%s -> %s' % (self.getTagSet(), self.__class__.__name__) + # # Constructed types: # * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice @@ -166,13 +183,16 @@ class AbstractConstructedAsn1Item(Asn1ItemBase): self._componentValuesSet = 0 def __repr__(self): - r = self.__class__.__name__ + '()' - for idx in range(len(self._componentValues)): - if self._componentValues[idx] is None: - continue - r = r + '.setComponentByPosition(%s, %r)' % ( - idx, self._componentValues[idx] - ) + r = [] + if self._componentType is not self.componentType: + r.append('componentType=%r' % (self._componentType,)) + if self._tagSet is not self.tagSet: + r.append('tagSet=%r' % (self._tagSet,)) + if self._subtypeSpec is not self.subtypeSpec: + r.append('subtypeSpec=%r' % (self._subtypeSpec,)) + r = '%s(%s)' % (self.__class__.__name__, ', '.join(r)) + if self._componentValues: + r += '.setComponents(%s)' % ', '.join([repr(x) for x in self._componentValues]) return r def __eq__(self, other): @@ -235,8 +255,17 @@ class AbstractConstructedAsn1Item(Asn1ItemBase): def setComponentByPosition(self, idx, value, verifyConstraints=True): raise error.PyAsn1Error('Method not implemented') + def setComponents(self, *args, **kwargs): + for idx in range(len(args)): + self[idx] = args[idx] + for k in kwargs: + self[k] = kwargs[k] + return self + def getComponentType(self): return self._componentType + def setDefaultComponents(self): pass + def __getitem__(self, idx): return self.getComponentByPosition(idx) def __setitem__(self, idx, value): self.setComponentByPosition(idx, value) @@ -246,4 +275,3 @@ class AbstractConstructedAsn1Item(Asn1ItemBase): self._componentValues = [] self._componentValuesSet = 0 - def setDefaultComponents(self): pass diff --git a/libs/pyasn1/type/char.py b/libs/pyasn1/type/char.py index ae112f8..af49ab3 100644 --- a/libs/pyasn1/type/char.py +++ b/libs/pyasn1/type/char.py @@ -1,12 +1,6 @@ # ASN.1 "character string" types from pyasn1.type import univ, tag -class UTF8String(univ.OctetString): - tagSet = univ.OctetString.tagSet.tagImplicitly( - tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) - ) - encoding = "utf-8" - class NumericString(univ.OctetString): tagSet = univ.OctetString.tagSet.tagImplicitly( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18) @@ -21,7 +15,8 @@ class TeletexString(univ.OctetString): tagSet = univ.OctetString.tagSet.tagImplicitly( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20) ) - + +class T61String(TeletexString): pass class VideotexString(univ.OctetString): tagSet = univ.OctetString.tagSet.tagImplicitly( @@ -43,6 +38,8 @@ class VisibleString(univ.OctetString): tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26) ) +class ISO646String(VisibleString): pass + class GeneralString(univ.OctetString): tagSet = univ.OctetString.tagSet.tagImplicitly( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27) @@ -59,3 +56,9 @@ class BMPString(univ.OctetString): tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30) ) encoding = "utf-16-be" + +class UTF8String(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + encoding = "utf-8" diff --git a/libs/pyasn1/type/namedtype.py b/libs/pyasn1/type/namedtype.py index 48967a5..aca4282 100644 --- a/libs/pyasn1/type/namedtype.py +++ b/libs/pyasn1/type/namedtype.py @@ -8,9 +8,17 @@ class NamedType: isDefaulted = 0 def __init__(self, name, t): self.__name = name; self.__type = t - def __repr__(self): return '%s(%s, %s)' % ( + def __repr__(self): return '%s(%r, %r)' % ( self.__class__.__name__, self.__name, self.__type ) + 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 getType(self): return self.__type def getName(self): return self.__name def __getitem__(self, idx): @@ -33,11 +41,18 @@ class NamedTypes: self.__ambigiousTypes = {} def __repr__(self): - r = '%s(' % self.__class__.__name__ - for n in self.__namedTypes: - r = r + '%r, ' % (n,) - return r + ')' - + return '%s(%s)' % ( + self.__class__.__name__, + ', '.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] if sys.version_info[0] <= 2: @@ -45,7 +60,9 @@ class NamedTypes: else: def __bool__(self): return bool(self.__namedTypesLen) def __len__(self): return self.__namedTypesLen - + + def clone(self): return self.__class__(*self.__namedTypes) + def getTypeByPosition(self, idx): if idx < 0 or idx >= self.__namedTypesLen: raise error.PyAsn1Error('Type position out of range') diff --git a/libs/pyasn1/type/namedval.py b/libs/pyasn1/type/namedval.py index d0fea7c..676cb93 100644 --- a/libs/pyasn1/type/namedval.py +++ b/libs/pyasn1/type/namedval.py @@ -22,7 +22,19 @@ class NamedValues: self.valToNameIdx[val] = name self.namedValues = self.namedValues + ((name, val),) automaticVal = automaticVal + 1 + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues])) + def __str__(self): return str(self.namedValues) + + 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 getName(self, value): if value in self.valToNameIdx: diff --git a/libs/pyasn1/type/tag.py b/libs/pyasn1/type/tag.py index 1144907..7471a9b 100644 --- a/libs/pyasn1/type/tag.py +++ b/libs/pyasn1/type/tag.py @@ -24,6 +24,9 @@ class Tag: self.uniq = (tagClass, tagId) self.__hashedUniqTag = hash(self.uniq) + def __str__(self): + return '[%s:%s:%s]' % self.__tag + def __repr__(self): return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % ( (self.__class__.__name__,) + self.__tag @@ -62,11 +65,14 @@ class TagSet: _uniq = _uniq + t.uniq self.uniq = _uniq self.__lenOfSuperTags = len(superTags) - + + def __str__(self): + return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]' + def __repr__(self): return '%s(%s)' % ( self.__class__.__name__, - ', '.join([repr(x) for x in self.__superTags]) + '(), ' + ', '.join([repr(x) for x in self.__superTags]) ) def __add__(self, superTag): diff --git a/libs/pyasn1/type/tagmap.py b/libs/pyasn1/type/tagmap.py index 7cec3a1..feb91ae 100644 --- a/libs/pyasn1/type/tagmap.py +++ b/libs/pyasn1/type/tagmap.py @@ -21,9 +21,23 @@ class TagMap: raise KeyError() def __repr__(self): - s = '%r/%r' % (self.__posMap, self.__negMap) + s = self.__class__.__name__ + '(' + if self.__posMap: + s = s + 'posMap=%r, ' % (self.__posMap,) + if self.__negMap: + s = s + 'negMap=%r, ' % (self.__negMap,) if self.__defType is not None: - s = s + '/%r' % (self.__defType,) + s = s + 'defType=%r' % (self.__defType,) + return s + ')' + + def __str__(self): + s = self.__class__.__name__ + ':\n' + if self.__posMap: + s = s + 'posMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__posMap.values()]) + if self.__negMap: + s = s + 'negMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__negMap.values()]) + if self.__defType is not None: + s = s + 'defType:\n%s, ' % self.__defType.prettyPrintType() return s def clone(self, parentType, tagMap, uniq=False): diff --git a/libs/pyasn1/type/univ.py b/libs/pyasn1/type/univ.py index 9cd16f8..f4bff81 100644 --- a/libs/pyasn1/type/univ.py +++ b/libs/pyasn1/type/univ.py @@ -1,5 +1,5 @@ # ASN.1 "universal" data types -import operator, sys +import operator, sys, math from pyasn1.type import base, tag, constraint, namedtype, namedval, tagmap from pyasn1.codec.ber import eoo from pyasn1.compat import octets @@ -22,6 +22,12 @@ class Integer(base.AbstractSimpleAsn1Item): self, value, tagSet, subtypeSpec ) + def __repr__(self): + if self.__namedValues is not self.namedValues: + return '%s, %r)' % (base.AbstractSimpleAsn1Item.__repr__(self)[:-1], self.__namedValues) + else: + return base.AbstractSimpleAsn1Item.__repr__(self) + def __and__(self, value): return self.clone(self._value & value) def __rand__(self, value): return self.clone(value & self._value) def __or__(self, value): return self.clone(self._value | value) @@ -57,8 +63,21 @@ class Integer(base.AbstractSimpleAsn1Item): if sys.version_info[0] <= 2: def __long__(self): return long(self._value) def __float__(self): return float(self._value) - def __abs__(self): return abs(self._value) + def __abs__(self): return self.clone(abs(self._value)) def __index__(self): return int(self._value) + def __pos__(self): return self.clone(+self._value) + def __neg__(self): return self.clone(-self._value) + def __invert__(self): return self.clone(~self._value) + def __round__(self, n=0): + r = round(self._value, n) + if n: + return self.clone(r) + else: + return r + def __floor__(self): return math.floor(self._value) + def __ceil__(self): return math.ceil(self._value) + if sys.version_info[0:2] > (2, 5): + def __trunc__(self): return self.clone(math.trunc(self._value)) def __lt__(self, value): return self._value < value def __le__(self, value): return self._value <= value @@ -73,7 +92,7 @@ class Integer(base.AbstractSimpleAsn1Item): return int(value) except: raise error.PyAsn1Error( - 'Can\'t coerce %s into integer: %s' % (value, sys.exc_info()[1]) + 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1]) ) r = self.__namedValues.getValue(value) if r is not None: @@ -82,7 +101,7 @@ class Integer(base.AbstractSimpleAsn1Item): return int(value) except: raise error.PyAsn1Error( - 'Can\'t coerce %s into integer: %s' % (value, sys.exc_info()[1]) + 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1]) ) def prettyOut(self, value): @@ -260,6 +279,15 @@ class BitString(base.AbstractSimpleAsn1Item): def prettyOut(self, value): return '\"\'%s\'B\"' % ''.join([str(x) for x in value]) +try: + all +except NameError: # Python 2.4 + def all(iterable): + for element in iterable: + if not element: + return False + return True + class OctetString(base.AbstractSimpleAsn1Item): tagSet = baseTagSet = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) @@ -280,7 +308,7 @@ class OctetString(base.AbstractSimpleAsn1Item): value = self.defaultHexValue if value is None or value is base.noValue: value = self.defaultBinValue - self.__intValue = None + self.__asNumbersCache = None base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec) def clone(self, value=None, tagSet=None, subtypeSpec=None, @@ -304,19 +332,33 @@ class OctetString(base.AbstractSimpleAsn1Item): def prettyIn(self, value): if isinstance(value, str): return value + elif isinstance(value, unicode): + try: + return value.encode(self._encoding) + except (LookupError, UnicodeEncodeError): + raise error.PyAsn1Error( + 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding) + ) elif isinstance(value, (tuple, list)): try: return ''.join([ chr(x) for x in value ]) except ValueError: raise error.PyAsn1Error( 'Bad OctetString initializer \'%s\'' % (value,) - ) + ) else: return str(value) else: def prettyIn(self, value): if isinstance(value, bytes): return value + elif isinstance(value, str): + try: + return value.encode(self._encoding) + except UnicodeEncodeError: + raise error.PyAsn1Error( + 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding) + ) elif isinstance(value, OctetString): return value.asOctets() elif isinstance(value, (tuple, list, map)): @@ -325,14 +367,14 @@ class OctetString(base.AbstractSimpleAsn1Item): except ValueError: raise error.PyAsn1Error( 'Bad OctetString initializer \'%s\'' % (value,) - ) + ) else: try: return str(value).encode(self._encoding) except UnicodeEncodeError: raise error.PyAsn1Error( 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding) - ) + ) def fromBinaryString(self, value): @@ -369,21 +411,33 @@ class OctetString(base.AbstractSimpleAsn1Item): def prettyOut(self, value): if sys.version_info[0] <= 2: - numbers = tuple([ ord(x) for x in value ]) + numbers = tuple(( ord(x) for x in value )) else: numbers = tuple(value) - if [ x for x in numbers if x < 32 or x > 126 ]: - return '0x' + ''.join([ '%.2x' % x for x in numbers ]) - else: + if all(x >= 32 and x <= 126 for x in numbers): return str(value) + else: + return '0x' + ''.join(( '%.2x' % x for x in numbers )) def __repr__(self): - if self._value is base.noValue: - return self.__class__.__name__ + '()' - if [ x for x in self.asNumbers() if x < 32 or x > 126 ]: - return self.__class__.__name__ + '(hexValue=\'' + ''.join([ '%.2x' % x for x in self.asNumbers() ])+'\')' - else: - return self.__class__.__name__ + '(\'' + self.prettyOut(self._value) + '\')' + r = [] + doHex = False + if self._value is not self.defaultValue: + for x in self.asNumbers(): + if x < 32 or x > 126: + doHex = True + break + if not doHex: + r.append('%r' % (self._value,)) + if self._tagSet is not self.tagSet: + r.append('tagSet=%r' % (self._tagSet,)) + if self._subtypeSpec is not self.subtypeSpec: + r.append('subtypeSpec=%r' % (self._subtypeSpec,)) + if self.encoding is not self._encoding: + r.append('encoding=%r' % (self._encoding,)) + if doHex: + r.append('hexValue=%r' % ''.join([ '%.2x' % x for x in self.asNumbers() ])) + return '%s(%s)' % (self.__class__.__name__, ', '.join(r)) if sys.version_info[0] <= 2: def __str__(self): return str(self._value) @@ -391,17 +445,17 @@ class OctetString(base.AbstractSimpleAsn1Item): return self._value.decode(self._encoding, 'ignore') def asOctets(self): return self._value def asNumbers(self): - if self.__intValue is None: - self.__intValue = tuple([ ord(x) for x in self._value ]) - return self.__intValue + if self.__asNumbersCache is None: + self.__asNumbersCache = tuple([ ord(x) for x in self._value ]) + return self.__asNumbersCache else: def __str__(self): return self._value.decode(self._encoding, 'ignore') def __bytes__(self): return self._value def asOctets(self): return self._value def asNumbers(self): - if self.__intValue is None: - self.__intValue = tuple(self._value) - return self.__intValue + if self.__asNumbersCache is None: + self.__asNumbersCache = tuple(self._value) + return self.__asNumbersCache # Immutable sequence object protocol @@ -419,7 +473,9 @@ class OctetString(base.AbstractSimpleAsn1Item): def __radd__(self, value): return self.clone(self.prettyIn(value) + self._value) def __mul__(self, value): return self.clone(self._value * value) def __rmul__(self, value): return self * value - + def __int__(self): return int(self._value) + def __float__(self): return float(self._value) + class Null(OctetString): defaultValue = ''.encode() # This is tightly constrained tagSet = baseTagSet = tag.initTagSet( @@ -430,7 +486,9 @@ class Null(OctetString): if sys.version_info[0] <= 2: intTypes = (int, long) else: - intTypes = int + intTypes = (int,) + +numericTypes = intTypes + (float,) class ObjectIdentifier(base.AbstractSimpleAsn1Item): tagSet = baseTagSet = tag.initTagSet( @@ -456,7 +514,9 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): return self._value[i] def __str__(self): return self.prettyPrint() - + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self.prettyPrint()) + def index(self, suboid): return self._value.index(suboid) def isPrefixOf(self, value): @@ -504,6 +564,7 @@ class ObjectIdentifier(base.AbstractSimpleAsn1Item): def prettyOut(self, value): return '.'.join([ str(x) for x in value ]) class Real(base.AbstractSimpleAsn1Item): + binEncBase = None # binEncBase = 16 is recommended for large numbers try: _plusInf = float('inf') _minusInf = float('-inf') @@ -526,11 +587,13 @@ class Real(base.AbstractSimpleAsn1Item): def prettyIn(self, value): if isinstance(value, tuple) and len(value) == 3: - for d in value: - if not isinstance(d, intTypes): - raise error.PyAsn1Error( - 'Lame Real value syntax: %s' % (value,) - ) + if not isinstance(value[0], numericTypes) or \ + not isinstance(value[1], intTypes) or \ + not isinstance(value[2], intTypes): + raise error.PyAsn1Error('Lame Real value syntax: %s' % (value,)) + if isinstance(value[0], float) and \ + self._inf and value[0] in self._inf: + return value[0] if value[1] not in (2, 10): raise error.PyAsn1Error( 'Prohibited base for Real value: %s' % (value[1],) @@ -540,7 +603,14 @@ class Real(base.AbstractSimpleAsn1Item): return value elif isinstance(value, intTypes): return self.__normalizeBase10((value, 10, 0)) - elif isinstance(value, float): + elif isinstance(value, (str, float)): + if isinstance(value, str): + try: + value = float(value) + except ValueError: + raise error.PyAsn1Error( + 'Bad real value syntax: %s' % (value,) + ) if self._inf and value in self._inf: return value else: @@ -551,11 +621,6 @@ class Real(base.AbstractSimpleAsn1Item): return self.__normalizeBase10((int(value), 10, e)) elif isinstance(value, Real): return tuple(value) - elif isinstance(value, str): # handle infinite literal - try: - return float(value) - except ValueError: - pass raise error.PyAsn1Error( 'Bad real value syntax: %s' % (value,) ) @@ -566,6 +631,12 @@ class Real(base.AbstractSimpleAsn1Item): else: return str(value) + def prettyPrint(self, scope=0): + if self.isInfinity(): + return self.prettyOut(self._value) + else: + return str(float(self)) + def isPlusInfinity(self): return self._value == self._plusInf def isMinusInfinity(self): return self._value == self._minusInf def isInfinity(self): return self._value in self._inf @@ -601,8 +672,20 @@ class Real(base.AbstractSimpleAsn1Item): else: return float( self._value[0] * pow(self._value[1], self._value[2]) - ) - def __abs__(self): return abs(float(self)) + ) + def __abs__(self): return self.clone(abs(float(self))) + def __pos__(self): return self.clone(+float(self)) + def __neg__(self): return self.clone(-float(self)) + def __round__(self, n=0): + r = round(float(self), n) + if n: + return self.clone(r) + else: + return r + def __floor__(self): return self.clone(math.floor(float(self))) + def __ceil__(self): return self.clone(math.ceil(float(self))) + if sys.version_info[0:2] > (2, 5): + def __trunc__(self): return self.clone(math.trunc(float(self))) def __lt__(self, value): return float(self) < value def __le__(self, value): return float(self) <= value @@ -636,6 +719,7 @@ class SetOf(base.AbstractConstructedAsn1Item): tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) ) typeId = 1 + strictConstraints = False def _cloneComponentValues(self, myClone, cloneValueFlag): idx = 0; l = len(self._componentValues) @@ -651,9 +735,14 @@ class SetOf(base.AbstractConstructedAsn1Item): idx = idx + 1 def _verifyComponent(self, idx, value): - if self._componentType is not None and \ - not self._componentType.isSuperTypeOf(value): - raise error.PyAsn1Error('Component type error %s' % (value,)) + t = self._componentType + if t is None: + return + if not t.isSameTypeWith(value,matchConstraints=self.strictConstraints): + raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, t)) + if self.strictConstraints and \ + not t.isSuperTypeOf(value, matchTags=False): + raise error.PyAsn1Error('Component value is constraints-incompatible: %r vs %r' % (value, t)) def getComponentByPosition(self, idx): return self._componentValues[idx] def setComponentByPosition(self, idx, value=None, verifyConstraints=True): @@ -698,6 +787,14 @@ class SetOf(base.AbstractConstructedAsn1Item): r = r + self._componentValues[idx].prettyPrint(scope) return r + def prettyPrintType(self, scope=0): + scope = scope + 1 + r = '%s -> %s {\n' % (self.getTagSet(), self.__class__.__name__) + if self._componentType is not None: + r = r + ' '*scope + r = r + self._componentType.prettyPrintType(scope) + return r + '\n' + ' '*(scope-1) + '}' + class SequenceOf(SetOf): tagSet = baseTagSet = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) @@ -706,15 +803,15 @@ class SequenceOf(SetOf): class SequenceAndSetBase(base.AbstractConstructedAsn1Item): componentType = namedtype.NamedTypes() + strictConstraints = False def __init__(self, componentType=None, tagSet=None, subtypeSpec=None, sizeSpec=None): + if componentType is None: + componentType = self.componentType base.AbstractConstructedAsn1Item.__init__( - self, componentType, tagSet, subtypeSpec, sizeSpec - ) - if self._componentType is None: - self._componentTypeLen = 0 - else: - self._componentTypeLen = len(self._componentType) + self, componentType.clone(), tagSet, subtypeSpec, sizeSpec + ) + self._componentTypeLen = len(self._componentType) def __getitem__(self, idx): if isinstance(idx, str): @@ -747,8 +844,11 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): 'Component type error out of range' ) t = self._componentType[idx].getType() - if not t.isSuperTypeOf(value): - raise error.PyAsn1Error('Component type error %r vs %r' % (t, value)) + if not t.isSameTypeWith(value,matchConstraints=self.strictConstraints): + raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, t)) + if self.strictConstraints and \ + not t.isSuperTypeOf(value, matchTags=False): + raise error.PyAsn1Error('Component value is constraints-incompatible: %r vs %r' % (value, t)) def getComponentByName(self, name): return self.getComponentByPosition( @@ -756,9 +856,8 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): ) def setComponentByName(self, name, value=None, verifyConstraints=True): return self.setComponentByPosition( - self._componentType.getPositionByName(name), value, - verifyConstraints - ) + self._componentType.getPositionByName(name),value,verifyConstraints + ) def getComponentByPosition(self, idx): try: @@ -767,7 +866,11 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): if idx < self._componentTypeLen: return raise - def setComponentByPosition(self, idx, value=None, verifyConstraints=True): + def setComponentByPosition(self, idx, value=None, + verifyConstraints=True, + exactTypes=False, + matchTags=True, + matchConstraints=True): l = len(self._componentValues) if idx >= l: self._componentValues = self._componentValues + (idx-l+1)*[None] @@ -834,6 +937,17 @@ class SequenceAndSetBase(base.AbstractConstructedAsn1Item): ) return r + def prettyPrintType(self, scope=0): + scope = scope + 1 + r = '%s -> %s {\n' % (self.getTagSet(), self.__class__.__name__) + for idx in range(len(self.componentType)): + r = r + ' '*scope + r = r + '"%s"' % self.componentType.getNameByPosition(idx) + r = '%s = %s\n' % ( + r, self._componentType.getTypeByPosition(idx).prettyPrintType(scope) + ) + return r + '\n' + ' '*(scope-1) + '}' + class Sequence(SequenceAndSetBase): tagSet = baseTagSet = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) @@ -877,16 +991,16 @@ class Set(SequenceAndSetBase): if t.getTagSet(): return self.setComponentByPosition( idx, value, verifyConstraints - ) + ) else: t = self.setComponentByPosition(idx).getComponentByPosition(idx) return t.setComponentByType( tagSet, value, innerFlag, verifyConstraints - ) + ) else: # set outer component by inner tagSet return self.setComponentByPosition( idx, value, verifyConstraints - ) + ) def getComponentTagMap(self): if self._componentType: diff --git a/libs/pyasn1/type/useful.py b/libs/pyasn1/type/useful.py index a7139c2..1766534 100644 --- a/libs/pyasn1/type/useful.py +++ b/libs/pyasn1/type/useful.py @@ -1,6 +1,11 @@ # ASN.1 "useful" types from pyasn1.type import char, tag +class ObjectDescriptor(char.GraphicString): + tagSet = char.GraphicString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7) + ) + class GeneralizedTime(char.VisibleString): tagSet = char.VisibleString.tagSet.tagImplicitly( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)