# # This file is part of pyasn1 software. # # Copyright (c) 2005-2017, Ilya Etingof # License: http://pyasn1.sf.net/license.html # import sys import math from pyasn1.type import base, tag, constraint, namedtype, namedval, tagmap from pyasn1.codec.ber import eoo from pyasn1.compat import octets, integer, binary from pyasn1 import error NoValue = base.NoValue noValue = NoValue() __all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null', 'ObjectIdentifier', 'Real', 'Enumerated', 'SequenceOfAndSetOfBase', 'SequenceOf', 'SetOf', 'SequenceAndSetBase', 'Sequence', 'Set', 'Choice', 'Any', 'NoValue', 'noValue'] # "Simple" ASN.1 types (yet incomplete) class Integer(base.AbstractSimpleAsn1Item): """Create |ASN.1| type or object. |ASN.1| objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object Python integer or string literal 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) namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers Raises ------ :py:class:`~pyasn1.error.PyAsn1Error` On constraint violation or bad initializer. """ #: 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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object #: representing symbolic aliases for numbers namedValues = namedval.NamedValues() # Optimization for faster codec lookup typeId = base.AbstractSimpleAsn1Item.getTypeId() def __init__(self, value=noValue, **kwargs): if 'namedValues' not in kwargs: kwargs['namedValues'] = self.namedValues base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) def __repr__(self): if self.namedValues is not self.__class__.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) def __ror__(self, value): return self.clone(value | self._value) def __xor__(self, value): return self.clone(self._value ^ value) def __rxor__(self, value): return self.clone(value ^ self._value) def __lshift__(self, value): return self.clone(self._value << value) def __rshift__(self, value): return self.clone(self._value >> value) def __add__(self, value): return self.clone(self._value + value) def __radd__(self, value): return self.clone(value + self._value) def __sub__(self, value): return self.clone(self._value - value) def __rsub__(self, value): return self.clone(value - self._value) def __mul__(self, value): return self.clone(self._value * value) def __rmul__(self, value): return self.clone(value * self._value) def __mod__(self, value): return self.clone(self._value % value) def __rmod__(self, value): return self.clone(value % self._value) def __pow__(self, value, modulo=None): return self.clone(pow(self._value, value, modulo)) def __rpow__(self, value): return self.clone(pow(value, self._value)) def __floordiv__(self, value): return self.clone(self._value // value) def __rfloordiv__(self, value): return self.clone(value // self._value) if sys.version_info[0] <= 2: def __div__(self, value): if isinstance(value, float): return Real(self._value / value) else: return self.clone(self._value / value) def __rdiv__(self, value): if isinstance(value, float): return Real(value / self._value) else: return self.clone(value / self._value) else: def __truediv__(self, value): return Real(self._value / value) def __rtruediv__(self, value): return Real(value / self._value) def __divmod__(self, value): return self.clone(divmod(self._value, value)) def __rdivmod__(self, value): return self.clone(divmod(value, self._value)) __hash__ = base.AbstractSimpleAsn1Item.__hash__ def __int__(self): return int(self._value) if sys.version_info[0] <= 2: def __long__(self): return long(self._value) def __float__(self): return float(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 def __eq__(self, value): return self._value == value def __ne__(self, value): return self._value != value def __gt__(self, value): return self._value > value def __ge__(self, value): return self._value >= value def prettyIn(self, value): try: return int(value) except ValueError: try: return self.namedValues[value] except KeyError: raise error.PyAsn1Error( 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1]) ) def prettyOut(self, value): try: return repr(self.namedValues[value]) except KeyError: return str(value) # backward compatibility def getNamedValues(self): return self.namedValues class Boolean(Integer): __doc__ = Integer.__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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01), ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = Integer.subtypeSpec + constraint.SingleValueConstraint(0, 1) #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object #: representing symbolic aliases for numbers namedValues = namedval.NamedValues(('False', 0), ('True', 1)) # Optimization for faster codec lookup typeId = Integer.getTypeId() class BitString(base.AbstractSimpleAsn1Item): """Create |ASN.1| schema or value object. |ASN.1| objects are immutable and duck-type both Python :class:`tuple` (as a tuple of bits) and :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object Python integer or string literal representing binary or hexadecimal number or sequence of integer bits or |ASN.1| object. 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) namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers binValue: :py:class:`str` Binary string initializer to use instead of the *value*. Example: '10110011'. hexValue: :py:class:`str` Hexadecimal string initializer to use instead of the *value*. Example: 'DEADBEEF'. Raises ------ :py:class:`~pyasn1.error.PyAsn1Error` On constraint violation or bad initializer. """ #: 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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object #: representing symbolic aliases for numbers namedValues = namedval.NamedValues() # Optimization for faster codec lookup typeId = base.AbstractSimpleAsn1Item.getTypeId() defaultBinValue = defaultHexValue = noValue if sys.version_info[0] < 3: SizedIntegerBase = long else: SizedIntegerBase = int class SizedInteger(SizedIntegerBase): bitLength = leadingZeroBits = None def setBitLength(self, bitLength): self.bitLength = bitLength self.leadingZeroBits = max(bitLength - integer.bitLength(self), 0) return self def __len__(self): if self.bitLength is None: self.setBitLength(integer.bitLength(self)) return self.bitLength def __init__(self, value=noValue, **kwargs): if value is noValue: if kwargs: try: value = self.fromBinaryString(kwargs.pop('binValue'), internalFormat=True) except KeyError: pass try: value = self.fromHexString(kwargs.pop('hexValue'), internalFormat=True) except KeyError: pass if value is noValue: if self.defaultBinValue is not noValue: value = self.fromBinaryString(self.defaultBinValue, internalFormat=True) elif self.defaultHexValue is not noValue: value = self.fromHexString(self.defaultHexValue, internalFormat=True) if 'namedValues' not in kwargs: kwargs['namedValues'] = self.namedValues base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) def __str__(self): return self.asBinary() def __eq__(self, other): other = self.prettyIn(other) return self is other or self._value == other and len(self._value) == len(other) def __ne__(self, other): other = self.prettyIn(other) return self._value != other or len(self._value) != len(other) def __lt__(self, other): other = self.prettyIn(other) return len(self._value) < len(other) or len(self._value) == len(other) and self._value < other def __le__(self, other): other = self.prettyIn(other) return len(self._value) <= len(other) or len(self._value) == len(other) and self._value <= other def __gt__(self, other): other = self.prettyIn(other) return len(self._value) > len(other) or len(self._value) == len(other) and self._value > other def __ge__(self, other): other = self.prettyIn(other) return len(self._value) >= len(other) or len(self._value) == len(other) and self._value >= other # Immutable sequence object protocol def __len__(self): return len(self._value) def __getitem__(self, i): if i.__class__ is slice: return self.clone([self[x] for x in range(*i.indices(len(self)))]) else: length = len(self._value) - 1 if i > length or i < 0: raise IndexError('bit index out of range') return (self._value >> (length - i)) & 1 def __iter__(self): length = len(self._value) while length: length -= 1 yield (self._value >> length) & 1 def __reversed__(self): return reversed(tuple(self)) # arithmetic operators def __add__(self, value): value = self.prettyIn(value) return self.clone(self.SizedInteger(self._value << len(value) | value).setBitLength(len(self._value) + len(value))) def __radd__(self, value): value = self.prettyIn(value) return self.clone(self.SizedInteger(value << len(self._value) | self._value).setBitLength(len(self._value) + len(value))) def __mul__(self, value): bitString = self._value while value > 1: bitString <<= len(self._value) bitString |= self._value value -= 1 return self.clone(bitString) def __rmul__(self, value): return self * value def __lshift__(self, count): return self.clone(self.SizedInteger(self._value << count).setBitLength(len(self._value) + count)) def __rshift__(self, count): return self.clone(self.SizedInteger(self._value >> count).setBitLength(max(0, len(self._value) - count))) def __int__(self): return self._value def __float__(self): return float(self._value) if sys.version_info[0] < 3: def __long__(self): return self._value def asNumbers(self): """Get |ASN.1| value as a sequence of 8-bit integers. If |ASN.1| object length is not a multiple of 8, result will be left-padded with zeros. """ return tuple(octets.octs2ints(self.asOctets())) def asOctets(self): """Get |ASN.1| value as a sequence of octets. If |ASN.1| object length is not a multiple of 8, result will be left-padded with zeros. """ return integer.to_bytes(self._value, length=len(self)) def asInteger(self): """Get |ASN.1| value as a single integer value. """ return self._value def asBinary(self): """Get |ASN.1| value as a text string of bits. """ binString = binary.bin(self._value)[2:] return '0' * (len(self._value) - len(binString)) + binString @classmethod def fromHexString(cls, value, internalFormat=False, prepend=None): """Create a |ASN.1| object initialized from the hex string. Parameters ---------- value: :class:`str` Text string like 'DEADBEEF' """ try: value = cls.SizedInteger(value, 16).setBitLength(len(value) * 4) except ValueError: raise error.PyAsn1Error('%s.fromHexString() error: %s' % (cls.__name__, sys.exc_info()[1])) if prepend is not None: value = cls.SizedInteger( (cls.SizedInteger(prepend) << len(value)) | value ).setBitLength(len(prepend) + len(value)) if not internalFormat: value = cls(value) return value @classmethod def fromBinaryString(cls, value, internalFormat=False, prepend=None): """Create a |ASN.1| object initialized from a string of '0' and '1'. Parameters ---------- value: :class:`str` Text string like '1010111' """ try: value = cls.SizedInteger(value or '0', 2).setBitLength(len(value)) except ValueError: raise error.PyAsn1Error('%s.fromBinaryString() error: %s' % (cls.__name__, sys.exc_info()[1])) if prepend is not None: value = cls.SizedInteger( (cls.SizedInteger(prepend) << len(value)) | value ).setBitLength(len(prepend) + len(value)) if not internalFormat: value = cls(value) return value @classmethod def fromOctetString(cls, value, internalFormat=False, prepend=None, padding=0): """Create a |ASN.1| object initialized from a string. Parameters ---------- value: :class:`str` (Py2) or :class:`bytes` (Py3) Text string like '\\\\x01\\\\xff' (Py2) or b'\\\\x01\\\\xff' (Py3) """ value = cls.SizedInteger(integer.from_bytes(value) >> padding).setBitLength(len(value) * 8 - padding) if prepend is not None: value = cls.SizedInteger( (cls.SizedInteger(prepend) << len(value)) | value ).setBitLength(len(prepend) + len(value)) if not internalFormat: value = cls(value) return value def prettyIn(self, value): if isinstance(value, self.SizedInteger): return value elif octets.isStringType(value): if not value: return self.SizedInteger(0).setBitLength(0) elif value[0] == '\'': # "'1011'B" -- ASN.1 schema representation (deprecated) if value[-2:] == '\'B': return self.fromBinaryString(value[1:-2], internalFormat=True) elif value[-2:] == '\'H': return self.fromHexString(value[1:-2], internalFormat=True) else: raise error.PyAsn1Error( 'Bad BIT STRING value notation %s' % (value,) ) elif self.namedValues and not value.isdigit(): # named bits like 'Urgent, Active' names = [x.strip() for x in value.split(',')] try: bitPositions = [self.namedValues[name] for name in names] except KeyError: raise error.PyAsn1Error('unknown bit name(s) in %r' % (names,)) rightmostPosition = max(bitPositions) number = 0 for bitPosition in bitPositions: number |= 1 << (rightmostPosition - bitPosition) return self.SizedInteger(number).setBitLength(rightmostPosition + 1) elif value.startswith('0x'): return self.fromHexString(value[2:], internalFormat=True) elif value.startswith('0b'): return self.fromBinaryString(value[2:], internalFormat=True) else: # assume plain binary string like '1011' return self.fromBinaryString(value, internalFormat=True) elif isinstance(value, (tuple, list)): return self.fromBinaryString(''.join([b and '1' or '0' for b in value]), internalFormat=True) elif isinstance(value, BitString): return self.SizedInteger(value).setBitLength(len(value)) elif isinstance(value, intTypes): return self.SizedInteger(value) else: raise error.PyAsn1Error( 'Bad BitString initializer type \'%s\'' % (value,) ) def prettyOut(self, value): return '\'%s\'' % str(self) try: # noinspection PyStatementEffect all except NameError: # Python 2.4 # noinspection PyShadowingBuiltins def all(iterable): for element in iterable: if not element: return False return True class OctetString(base.AbstractSimpleAsn1Item): """Create |ASN.1| schema or value object. |ASN.1| objects are immutable and duck-type Python 2 :class:`str` or Python 3 :class:`bytes`. When used in Unicode context, |ASN.1| type assumes "|encoding|" serialization. Keyword Args ------------ value: :class:`str`, :class:`bytes` or |ASN.1| object string (Python 2) or bytes (Python 3), alternatively unicode object (Python 2) or string (Python 3) representing character string to be serialized into octets (note `encoding` parameter) or |ASN.1| object. 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 text string context. binValue: :py:class:`str` Binary string initializer to use instead of the *value*. Example: '10110011'. hexValue: :py:class:`str` Hexadecimal string initializer to use instead of the *value*. Example: 'DEADBEEF'. Raises ------ :py:class:`~pyasn1.error.PyAsn1Error` On constraint violation or bad initializer. """ #: 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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup typeId = base.AbstractSimpleAsn1Item.getTypeId() defaultBinValue = defaultHexValue = noValue encoding = 'iso-8859-1' def __init__(self, value=noValue, **kwargs): if kwargs: if value is noValue: try: value = self.fromBinaryString(kwargs.pop('binValue')) except KeyError: pass try: value = self.fromHexString(kwargs.pop('hexValue')) except KeyError: pass if value is noValue: if self.defaultBinValue is not noValue: value = self.fromBinaryString(self.defaultBinValue) elif self.defaultHexValue is not noValue: value = self.fromHexString(self.defaultHexValue) if 'encoding' not in kwargs: kwargs['encoding'] = self.encoding base.AbstractSimpleAsn1Item.__init__(self, value, **kwargs) if sys.version_info[0] <= 2: 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 codec %s" % (value, self.encoding) ) elif isinstance(value, (tuple, list)): try: return ''.join([chr(x) for x in value]) except ValueError: raise error.PyAsn1Error( 'Bad %s initializer \'%s\'' % (self.__class__.__name__, value) ) else: return str(value) def __str__(self): return str(self._value) def __unicode__(self): try: return self._value.decode(self.encoding) except UnicodeDecodeError: raise error.PyAsn1Error( "Can't decode string '%s' with codec %s" % (self._value, self.encoding) ) def asOctets(self): return str(self._value) def asNumbers(self): return tuple([ord(x) for x in self._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): # a shortcut, bytes() would work the same way return value.asOctets() elif isinstance(value, base.AbstractSimpleAsn1Item): # this mostly targets Integer objects return self.prettyIn(str(value)) elif isinstance(value, (tuple, list)): return self.prettyIn(bytes(value)) else: return bytes(value) def __str__(self): try: return self._value.decode(self.encoding) except UnicodeDecodeError: raise error.PyAsn1Error( 'Can\'t decode string \'%s\' with \'%s\' codec at \'%s\'' % (self._value, self.encoding, self.__class__.__name__) ) def __bytes__(self): return bytes(self._value) def asOctets(self): return bytes(self._value) def asNumbers(self): return tuple(self._value) def prettyOut(self, value): if sys.version_info[0] <= 2: numbers = tuple((ord(x) for x in value)) else: numbers = tuple(value) for x in numbers: if x < 32 or x > 126: return '0x' + ''.join(('%.2x' % x for x in numbers)) else: try: return value.decode(self.encoding) except UnicodeDecodeError: raise error.PyAsn1Error( "Can't decode string '%s' with '%s' codec at '%s'" % (value, self.encoding, self.__class__.__name__) ) @staticmethod def fromBinaryString(value): """Create a |ASN.1| object initialized from a string of '0' and '1'. Parameters ---------- value: :class:`str` Text string like '1010111' """ bitNo = 8 byte = 0 r = [] for v in value: if bitNo: bitNo -= 1 else: bitNo = 7 r.append(byte) byte = 0 if v in ('0', '1'): v = int(v) else: raise error.PyAsn1Error( 'Non-binary OCTET STRING initializer %s' % (v,) ) byte |= v << bitNo r.append(byte) return octets.ints2octs(r) @staticmethod def fromHexString(value): """Create a |ASN.1| object initialized from the hex string. Parameters ---------- value: :class:`str` Text string like 'DEADBEEF' """ r = [] p = [] for v in value: if p: r.append(int(p + v, 16)) p = None else: p = v if p: r.append(int(p + '0', 16)) return octets.ints2octs(r) def __repr__(self): 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.__class__.tagSet: r.append('tagSet=%r' % (self.tagSet,)) if self.subtypeSpec is not self.__class__.subtypeSpec: r.append('subtypeSpec=%r' % (self.subtypeSpec,)) if self.encoding is not self.__class__.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)) # Immutable sequence object protocol def __len__(self): return len(self._value) def __getitem__(self, i): if i.__class__ is slice: return self.clone(self._value[i]) else: return self._value[i] def __iter__(self): return iter(self._value) def __contains__(self, value): return value in self._value def __add__(self, value): return self.clone(self._value + self.prettyIn(value)) 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) def __reversed__(self): return reversed(self._value) class Null(OctetString): """Create |ASN.1| schema or value object. |ASN.1| objects are immutable and duck-type Python :class:`str` objects (always empty). Keyword Args ------------ value: :class:`str` or :py:class:`~pyasn1.type.univ.Null` object Python empty string literal or any object that evaluates to `False` tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) Raises ------ :py:class:`~pyasn1.error.PyAsn1Error` On constraint violation or bad initializer. """ #: 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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05) ) subtypeSpec = OctetString.subtypeSpec + constraint.SingleValueConstraint(octets.str2octs('')) # Optimization for faster codec lookup typeId = OctetString.getTypeId() def prettyIn(self, value): if value: return value return octets.str2octs('') if sys.version_info[0] <= 2: intTypes = (int, long) else: intTypes = (int,) numericTypes = intTypes + (float,) class ObjectIdentifier(base.AbstractSimpleAsn1Item): """Create |ASN.1| schema or value object. |ASN.1| objects are immutable and duck-type Python :class:`tuple` objects (tuple of non-negative integers). Keyword Args ------------ value: :class:`tuple`, :class:`str` or |ASN.1| object Python sequence of :class:`int` or string literal or |ASN.1| object. 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) Raises ------ :py:class:`~pyasn1.error.PyAsn1Error` On constraint violation or bad initializer. """ #: 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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup typeId = base.AbstractSimpleAsn1Item.getTypeId() def __add__(self, other): return self.clone(self._value + other) def __radd__(self, other): return self.clone(other + self._value) def asTuple(self): return self._value # Sequence object protocol def __len__(self): return len(self._value) def __getitem__(self, i): if i.__class__ is slice: return self.clone(self._value[i]) else: return self._value[i] def __iter__(self): return iter(self._value) def __contains__(self, value): return value in self._value 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, other): """Indicate if this |ASN.1| object is a prefix of other |ASN.1| object. Parameters ---------- other: |ASN.1| object |ASN.1| object Returns ------- : :class:`bool` :class:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object or :class:`False` otherwise. """ l = len(self) if l <= len(other): if self._value[:l] == other[:l]: return True return False def prettyIn(self, value): if isinstance(value, ObjectIdentifier): return tuple(value) elif octets.isStringType(value): if '-' in value: raise error.PyAsn1Error( 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1]) ) try: return tuple([int(subOid) for subOid in value.split('.') if subOid]) except ValueError: raise error.PyAsn1Error( 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1]) ) try: tupleOfInts = tuple([int(subOid) for subOid in value if subOid >= 0]) except (ValueError, TypeError): raise error.PyAsn1Error( 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1]) ) if len(tupleOfInts) == len(value): return tupleOfInts raise error.PyAsn1Error('Malformed Object ID %s at %s' % (value, self.__class__.__name__)) def prettyOut(self, value): return '.'.join([str(x) for x in value]) class Real(base.AbstractSimpleAsn1Item): """Create |ASN.1| schema or value object. |ASN.1| objects are immutable and duck-type Python :class:`float` objects. Additionally, |ASN.1| objects behave like a :class:`tuple` in which case its elements are mantissa, base and exponent. Keyword Args ------------ value: :class:`tuple`, :class:`float` or |ASN.1| object Python sequence of :class:`int` (representing mantissa, base and exponent) or float instance or *Real* 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) Raises ------ :py:class:`~pyasn1.error.PyAsn1Error` On constraint violation or bad initializer. """ binEncBase = None # binEncBase = 16 is recommended for large numbers try: _plusInf = float('inf') _minusInf = float('-inf') _inf = (_plusInf, _minusInf) except ValueError: # Infinity support is platform and Python dependent _plusInf = _minusInf = None _inf = () #: 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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup typeId = base.AbstractSimpleAsn1Item.getTypeId() @staticmethod def __normalizeBase10(value): m, b, e = value while m and m % 10 == 0: m /= 10 e += 1 return m, b, e def prettyIn(self, value): if isinstance(value, tuple) and len(value) == 3: 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],) ) if value[1] == 10: value = self.__normalizeBase10(value) return value elif isinstance(value, intTypes): return self.__normalizeBase10((value, 10, 0)) elif isinstance(value, float) or octets.isStringType(value): if octets.isStringType(value): 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: e = 0 while int(value) != value: value *= 10 e -= 1 return self.__normalizeBase10((int(value), 10, e)) elif isinstance(value, Real): return tuple(value) raise error.PyAsn1Error( 'Bad real value syntax: %s' % (value,) ) def prettyOut(self, value): if value in self._inf: return "'%s'" % value else: return str(value) def prettyPrint(self, scope=0): if self.isInf: return self.prettyOut(self._value) else: return str(float(self)) @property def isPlusInf(self): """Indicate PLUS-INFINITY object value Returns ------- : :class:`bool` :class:`True` if calling object represents plus infinity or :class:`False` otherwise. """ return self._value == self._plusInf @property def isMinusInf(self): """Indicate MINUS-INFINITY object value Returns ------- : :class:`bool` :class:`True` if calling object represents minus infinity or :class:`False` otherwise. """ return self._value == self._minusInf @property def isInf(self): return self._value in self._inf def __str__(self): try: return str(float(self)) except OverflowError: return '' def __add__(self, value): return self.clone(float(self) + value) def __radd__(self, value): return self + value def __mul__(self, value): return self.clone(float(self) * value) def __rmul__(self, value): return self * value def __sub__(self, value): return self.clone(float(self) - value) def __rsub__(self, value): return self.clone(value - float(self)) def __mod__(self, value): return self.clone(float(self) % value) def __rmod__(self, value): return self.clone(value % float(self)) def __pow__(self, value, modulo=None): return self.clone(pow(float(self), value, modulo)) def __rpow__(self, value): return self.clone(pow(value, float(self))) if sys.version_info[0] <= 2: def __div__(self, value): return self.clone(float(self) / value) def __rdiv__(self, value): return self.clone(value / float(self)) else: def __truediv__(self, value): return self.clone(float(self) / value) def __rtruediv__(self, value): return self.clone(value / float(self)) def __divmod__(self, value): return self.clone(float(self) // value) def __rdivmod__(self, value): return self.clone(value // float(self)) def __int__(self): return int(float(self)) if sys.version_info[0] <= 2: def __long__(self): return long(float(self)) def __float__(self): if self._value in self._inf: return self._value else: return float( self._value[0] * pow(self._value[1], self._value[2]) ) 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 def __eq__(self, value): return float(self) == value def __ne__(self, value): return float(self) != value def __gt__(self, value): return float(self) > value def __ge__(self, value): return float(self) >= value if sys.version_info[0] <= 2: def __nonzero__(self): return bool(float(self)) else: def __bool__(self): return bool(float(self)) __hash__ = base.AbstractSimpleAsn1Item.__hash__ def __getitem__(self, idx): if self._value in self._inf: raise error.PyAsn1Error('Invalid infinite value operation') else: return self._value[idx] # compatibility stubs def isPlusInfinity(self): return self.isPlusInf def isMinusInfinity(self): return self.isMinusInf def isInfinity(self): return self.isInf class Enumerated(Integer): __doc__ = Integer.__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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() # Optimization for faster codec lookup typeId = Integer.getTypeId() #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object #: representing symbolic aliases for numbers namedValues = namedval.NamedValues() # "Structured" ASN.1 types class SequenceOfAndSetOfBase(base.AbstractConstructedAsn1Item): """Create |ASN.1| type. |ASN.1| objects are mutable and duck-type Python :class:`list` objects. Keyword Args ------------ componentType : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A pyasn1 object representing ASN.1 type allowed within |ASN.1| type 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 collection size constraint """ def __init__(self, *args, **kwargs): # support positional params for backward compatibility if args: for key, value in zip(('componentType', 'tagSet', 'subtypeSpec', 'sizeSpec'), args): if key in kwargs: raise error.PyAsn1Error('Conflicting positional and keyword params!') kwargs['componentType'] = value base.AbstractConstructedAsn1Item.__init__(self, **kwargs) # Python list protocol def __getitem__(self, idx): try: return self.getComponentByPosition(idx) except error.PyAsn1Error: raise IndexError(sys.exc_info()[1]) def __setitem__(self, idx, value): try: self.setComponentByPosition(idx, value) except error.PyAsn1Error: raise IndexError(sys.exc_info()[1]) def clear(self): self._componentValues = [] def append(self, value): self[len(self)] = value def count(self, value): return self._componentValues.count(value) def extend(self, values): for value in values: self.append(value) def index(self, value, start=0, stop=None): if stop is None: stop = len(self) try: return self._componentValues.index(value, start, stop) except error.PyAsn1Error: raise ValueError(sys.exc_info()[1]) def reverse(self): self._componentValues.reverse() def sort(self, key=None, reverse=False): self._componentValues.sort(key=key, reverse=reverse) def __iter__(self): return iter(self._componentValues) def _cloneComponentValues(self, myClone, cloneValueFlag): for idx, componentValue in enumerate(self._componentValues): if componentValue is not noValue: if isinstance(componentValue, base.AbstractConstructedAsn1Item): myClone.setComponentByPosition( idx, componentValue.clone(cloneValueFlag=cloneValueFlag) ) else: myClone.setComponentByPosition(idx, componentValue.clone()) def getComponentByPosition(self, idx, instantiate=True): """Return |ASN.1| type component value by position. Equivalent to Python sequence subscription operation (e.g. `[]`). Parameters ---------- idx : :class:`int` Component index (zero-based). Must either refer to an existing component or to N+1 component (if *componentType* is set). In the latter case a new component type gets instantiated and appended to the |ASN.1| sequence. Keyword Args ------------ instantiate: :class:`bool` If `True` (default), inner component will be automatically instantiated. If 'False' either existing component or the `noValue` object will be returned. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` Instantiate |ASN.1| component type or return existing component value Examples -------- .. code-block:: python # can also be SetOf class MySequenceOf(SequenceOf): componentType = OctetString() s = MySequenceOf() # returns noValue s.getComponentByPosition(0, instantiate=False) # sets component #0 to OctetString() ASN.1 schema # object and returns it s.getComponentByPosition(0, instantiate=True) # sets component #0 to ASN.1 value object s.setComponentByPosition(0, 'ABCD') # returns OctetString('ABCD') value object s.getComponentByPosition(0, instantiate=False) s.clear() # returns noValue s.getComponentByPosition(0, instantiate=False) """ try: return self._componentValues[idx] except IndexError: if not instantiate: return noValue self.setComponentByPosition(idx) return self._componentValues[idx] def setComponentByPosition(self, idx, value=noValue, verifyConstraints=True, matchTags=True, matchConstraints=True): """Assign |ASN.1| type component by position. Equivalent to Python sequence item assignment operation (e.g. `[]`) or list.append() (when idx == len(self)). Parameters ---------- idx: :class:`int` Component index (zero-based). Must either refer to existing component or to N+1 component. In the latter case a new component type gets instantiated (if *componentType* is set, or given ASN.1 object is taken otherwise) and appended to the |ASN.1| sequence. Keyword Args ------------ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. verifyConstraints: :class:`bool` If `False`, skip constraints validation matchTags: :class:`bool` If `False`, skip component tags matching matchConstraints: :class:`bool` If `False`, skip component constraints matching Returns ------- self Raises ------ IndexError: When idx > len(self) """ componentType = self.componentType try: currentValue = self._componentValues[idx] except IndexError: currentValue = noValue if len(self._componentValues) < idx: raise error.PyAsn1Error('Component index out of range') if value is noValue: if componentType is not None: value = componentType.clone() elif currentValue is noValue: raise error.PyAsn1Error('Component type not defined') elif not isinstance(value, base.Asn1Item): if componentType is not None and isinstance(componentType, base.AbstractSimpleAsn1Item): value = componentType.clone(value=value) elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item): value = currentValue.clone(value=value) else: raise error.PyAsn1Error('Non-ASN.1 value %r and undefined component type at %r' % (value, self)) elif componentType is not None: if self.strictConstraints: if not componentType.isSameTypeWith(value, matchTags, matchConstraints): raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) else: if not componentType.isSuperTypeOf(value, matchTags, matchConstraints): raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) if verifyConstraints and value.isValue: try: self.subtypeSpec(value, idx) except error.PyAsn1Error: exType, exValue, exTb = sys.exc_info() raise exType('%s at %s' % (exValue, self.__class__.__name__)) if currentValue is noValue: self._componentValues.append(value) else: self._componentValues[idx] = value return self @property def componentTagMap(self): if self.componentType is not None: return self.componentType.tagMap def prettyPrint(self, scope=0): scope += 1 representation = self.__class__.__name__ + ':\n' for idx, componentValue in enumerate(self._componentValues): representation += ' ' * scope if (componentValue is noValue and self.componentType is not None): representation += '' else: representation += componentValue.prettyPrint(scope) return representation def prettyPrintType(self, scope=0): scope += 1 representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__) if self.componentType is not None: representation += ' ' * scope representation += self.componentType.prettyPrintType(scope) return representation + '\n' + ' ' * (scope - 1) + '}' @property def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. If *isValue* is `False` then this object represents just ASN.1 schema. If *isValue* is `True` then, in addition to its ASN.1 schema features, this object can also be used like a Python built-in object (e.g. `int`, `str`, `dict` etc.). Returns ------- : :class:`bool` :class:`False` if object represents just ASN.1 schema. :class:`True` if object represents ASN.1 schema and can be used as a normal value. Note ---- There is an important distinction between PyASN1 schema and value objects. The PyASN1 schema objects can only participate in ASN.1 schema-related operations (e.g. defining or testing the structure of the data). Most obvious uses of ASN.1 schema is to guide serialization codecs whilst encoding/decoding serialised ASN.1 contents. The PyASN1 value objects can **additionally** participate in many operations involving regular Python objects (e.g. arithmetic, comprehension etc). """ for componentValue in self._componentValues: if not componentValue.isValue: return False return True class SequenceOf(SequenceOfAndSetOfBase): __doc__ = SequenceOfAndSetOfBase.__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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) ) #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative #: object representing ASN.1 type allowed within |ASN.1| type componentType = None #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` #: object imposing size constraint on |ASN.1| objects sizeSpec = constraint.ConstraintsIntersection() # Disambiguation ASN.1 types identification typeId = SequenceOfAndSetOfBase.getTypeId() class SetOf(SequenceOfAndSetOfBase): __doc__ = SequenceOfAndSetOfBase.__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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) ) #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative #: object representing ASN.1 type allowed within |ASN.1| type componentType = None #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` #: object imposing size constraint on |ASN.1| objects sizeSpec = constraint.ConstraintsIntersection() # Disambiguation ASN.1 types identification typeId = SequenceOfAndSetOfBase.getTypeId() class SequenceAndSetBase(base.AbstractConstructedAsn1Item): """Create |ASN.1| type. |ASN.1| objects are mutable and duck-type Python :class:`dict` objects. Keyword Args ------------ componentType: :py:class:`~pyasn1.type.namedtype.NamedType` Object holding named ASN.1 types allowed within this collection 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 collection size constraint """ #: Default :py:class:`~pyasn1.type.namedtype.NamedTypes` #: object representing named ASN.1 types allowed within |ASN.1| type componentType = namedtype.NamedTypes() class DynamicNames(object): """Fields names/positions mapping for component-less objects""" def __init__(self): self._keyToIdxMap = {} self._idxToKeyMap = {} def __len__(self): return len(self._keyToIdxMap) def __contains__(self, item): return item in self._keyToIdxMap or item in self._idxToKeyMap def __iter__(self): return (self._idxToKeyMap[idx] for idx in range(len(self._idxToKeyMap))) def __getitem__(self, item): try: return self._keyToIdxMap[item] except KeyError: return self._idxToKeyMap[item] def getNameByPosition(self, idx): try: return self._idxToKeyMap[idx] except KeyError: raise error.PyAsn1Error('Type position out of range') def getPositionByName(self, name): try: return self._keyToIdxMap[name] except KeyError: raise error.PyAsn1Error('Name %s not found' % (name,)) def addField(self, idx): self._keyToIdxMap['field-%d' % idx] = idx self._idxToKeyMap[idx] = 'field-%d' % idx def __init__(self, **kwargs): base.AbstractConstructedAsn1Item.__init__(self, **kwargs) self._componentTypeLen = len(self.componentType) self._dynamicNames = self._componentTypeLen or self.DynamicNames() def __getitem__(self, idx): if octets.isStringType(idx): try: return self.getComponentByName(idx) except error.PyAsn1Error: # duck-typing dict raise KeyError(sys.exc_info()[1]) else: try: return self.getComponentByPosition(idx) except error.PyAsn1Error: # duck-typing list raise IndexError(sys.exc_info()[1]) def __setitem__(self, idx, value): if octets.isStringType(idx): try: self.setComponentByName(idx, value) except error.PyAsn1Error: # duck-typing dict raise KeyError(sys.exc_info()[1]) else: try: self.setComponentByPosition(idx, value) except error.PyAsn1Error: # duck-typing list raise IndexError(sys.exc_info()[1]) def __contains__(self, key): if self._componentTypeLen: return key in self.componentType else: return key in self._dynamicNames def __iter__(self): return iter(self.componentType or self._dynamicNames) # Python dict protocol def values(self): for idx in range(self._componentTypeLen or len(self._dynamicNames)): yield self[idx] def keys(self): return iter(self) def items(self): for idx in range(self._componentTypeLen or len(self._dynamicNames)): if self._componentTypeLen: yield self.componentType[idx].name, self[idx] else: yield self._dynamicNames[idx], self[idx] def update(self, *iterValue, **mappingValue): for k, v in iterValue: self[k] = v for k in mappingValue: self[k] = mappingValue[k] def clear(self): self._componentValues = [] self._dynamicNames = self.DynamicNames() def _cloneComponentValues(self, myClone, cloneValueFlag): for idx, componentValue in enumerate(self._componentValues): if componentValue is not noValue: if isinstance(componentValue, base.AbstractConstructedAsn1Item): myClone.setComponentByPosition( idx, componentValue.clone(cloneValueFlag=cloneValueFlag) ) else: myClone.setComponentByPosition(idx, componentValue.clone()) def getComponentByName(self, name, instantiate=True): """Returns |ASN.1| type component by name. Equivalent to Python :class:`dict` subscription operation (e.g. `[]`). Parameters ---------- name: :class:`str` |ASN.1| type component name Keyword Args ------------ instantiate: :class:`bool` If `True` (default), inner component will be automatically instantiated. If 'False' either existing component or the `noValue` object will be returned. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` Instantiate |ASN.1| component type or return existing component value """ if self._componentTypeLen: idx = self.componentType.getPositionByName(name) else: try: idx = self._dynamicNames.getPositionByName(name) except KeyError: raise error.PyAsn1Error('Name %s not found' % (name,)) return self.getComponentByPosition(idx, instantiate) def setComponentByName(self, name, value=noValue, verifyConstraints=True, matchTags=True, matchConstraints=True): """Assign |ASN.1| type component by name. Equivalent to Python :class:`dict` item assignment operation (e.g. `[]`). Parameters ---------- name: :class:`str` |ASN.1| type component name Keyword Args ------------ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. verifyConstraints: :class:`bool` If `False`, skip constraints validation matchTags: :class:`bool` If `False`, skip component tags matching matchConstraints: :class:`bool` If `False`, skip component constraints matching Returns ------- self """ if self._componentTypeLen: idx = self.componentType.getPositionByName(name) else: try: idx = self._dynamicNames.getPositionByName(name) except KeyError: raise error.PyAsn1Error('Name %s not found' % (name,)) return self.setComponentByPosition( idx, value, verifyConstraints, matchTags, matchConstraints ) def getComponentByPosition(self, idx, instantiate=True): """Returns |ASN.1| type component by index. Equivalent to Python sequence subscription operation (e.g. `[]`). Parameters ---------- idx : :class:`int` Component index (zero-based). Must either refer to an existing component or (if *componentType* is set) new ASN.1 schema object gets instantiated. Keyword Args ------------ instantiate: :class:`bool` If `True` (default), inner component will be automatically instantiated. If 'False' either existing component or the `noValue` object will be returned. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` a PyASN1 object Examples -------- .. code-block:: python # can also be Set class MySequence(Sequence): componentType = NamedTypes( NamedType('id', OctetString()) ) s = MySequence() # returns noValue s.getComponentByPosition(0, instantiate=False) # sets component #0 to OctetString() ASN.1 schema # object and returns it s.getComponentByPosition(0, instantiate=True) # sets component #0 to ASN.1 value object s.setComponentByPosition(0, 'ABCD') # returns OctetString('ABCD') value object s.getComponentByPosition(0, instantiate=False) s.clear() # returns noValue s.getComponentByPosition(0, instantiate=False) """ try: componentValue = self._componentValues[idx] except IndexError: componentValue = noValue if not instantiate: return componentValue if componentValue is noValue: self.setComponentByPosition(idx) return self._componentValues[idx] def setComponentByPosition(self, idx, value=noValue, verifyConstraints=True, matchTags=True, matchConstraints=True): """Assign |ASN.1| type component by position. Equivalent to Python sequence item assignment operation (e.g. `[]`). Parameters ---------- idx : :class:`int` Component index (zero-based). Must either refer to existing component (if *componentType* is set) or to N+1 component otherwise. In the latter case a new component of given ASN.1 type gets instantiated and appended to |ASN.1| sequence. Keyword Args ------------ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. verifyConstraints : :class:`bool` If `False`, skip constraints validation matchTags: :class:`bool` If `False`, skip component tags matching matchConstraints: :class:`bool` If `False`, skip component constraints matching Returns ------- self """ componentType = self.componentType componentTypeLen = self._componentTypeLen try: currentValue = self._componentValues[idx] except IndexError: currentValue = noValue if componentTypeLen: if componentTypeLen < idx: raise error.PyAsn1Error('component index out of range') self._componentValues = [noValue] * componentTypeLen if value is noValue: if componentTypeLen: value = componentType.getTypeByPosition(idx).clone() elif currentValue is noValue: raise error.PyAsn1Error('Component type not defined') elif not isinstance(value, base.Asn1Item): if componentTypeLen: subComponentType = componentType.getTypeByPosition(idx) if isinstance(subComponentType, base.AbstractSimpleAsn1Item): value = subComponentType.clone(value=value) else: raise error.PyAsn1Error('%s can cast only scalar values' % componentType.__class__.__name__) elif currentValue is not noValue and isinstance(currentValue, base.AbstractSimpleAsn1Item): value = currentValue.clone(value=value) else: raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__) elif (matchTags or matchConstraints) and componentTypeLen: subComponentType = componentType.getTypeByPosition(idx) if subComponentType is not noValue: if self.strictConstraints: if not subComponentType.isSameTypeWith(value, matchTags, matchConstraints): raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) else: if not subComponentType.isSuperTypeOf(value, matchTags, matchConstraints): raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) if verifyConstraints and value.isValue: try: self.subtypeSpec(value, idx) except error.PyAsn1Error: exType, exValue, exTb = sys.exc_info() raise exType('%s at %s' % (exValue, self.__class__.__name__)) if componentTypeLen or idx in self._dynamicNames: self._componentValues[idx] = value elif len(self._componentValues) == idx: self._componentValues.append(value) self._dynamicNames.addField(idx) else: raise error.PyAsn1Error('Component index out of range') return self @property def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. If *isValue* is `False` then this object represents just ASN.1 schema. If *isValue* is `True` then, in addition to its ASN.1 schema features, this object can also be used like a Python built-in object (e.g. `int`, `str`, `dict` etc.). Returns ------- : :class:`bool` :class:`False` if object represents just ASN.1 schema. :class:`True` if object represents ASN.1 schema and can be used as a normal value. Note ---- There is an important distinction between PyASN1 schema and value objects. The PyASN1 schema objects can only participate in ASN.1 schema-related operations (e.g. defining or testing the structure of the data). Most obvious uses of ASN.1 schema is to guide serialization codecs whilst encoding/decoding serialised ASN.1 contents. The PyASN1 value objects can **additionally** participate in many operations involving regular Python objects (e.g. arithmetic, comprehension etc). """ componentType = self.componentType if componentType: for idx, subComponentType in enumerate(componentType.namedTypes): if subComponentType.isDefaulted or subComponentType.isOptional: continue if (not self._componentValues or not self._componentValues[idx].isValue): return False else: for componentValue in self._componentValues: if not componentValue.isValue: return False return True def prettyPrint(self, scope=0): """Return an object representation string. Returns ------- : :class:`str` Human-friendly object representation. """ scope += 1 representation = self.__class__.__name__ + ':\n' for idx, componentValue in enumerate(self._componentValues): if componentValue is not noValue: representation += ' ' * scope if self.componentType: representation += self.componentType.getNameByPosition(idx) else: representation += self._dynamicNames.getNameByPosition(idx) representation = '%s=%s\n' % ( representation, componentValue.prettyPrint(scope) ) return representation def prettyPrintType(self, scope=0): scope += 1 representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__) for idx, componentType in enumerate(self.componentType.values() or self._componentValues): representation += ' ' * scope if self.componentType: representation += '"%s"' % self.componentType.getNameByPosition(idx) else: representation += '"%s"' % self._dynamicNames.getNameByPosition(idx) representation = '%s = %s\n' % ( representation, componentType.prettyPrintType(scope) ) return representation + '\n' + ' ' * (scope - 1) + '}' # backward compatibility def setDefaultComponents(self): return self def getComponentType(self): if self._componentTypeLen: return self.componentType def getNameByPosition(self, idx): if self._componentTypeLen: return self.componentType[idx].name class Sequence(SequenceAndSetBase): __doc__ = SequenceAndSetBase.__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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) ) #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` #: object imposing constraints on |ASN.1| objects sizeSpec = constraint.ConstraintsIntersection() #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) #: object imposing size constraint on |ASN.1| objects componentType = namedtype.NamedTypes() # Disambiguation ASN.1 types identification typeId = SequenceAndSetBase.getTypeId() # backward compatibility def getComponentTagMapNearPosition(self, idx): if self.componentType: return self.componentType.getTagMapNearPosition(idx) def getComponentPositionNearType(self, tagSet, idx): if self.componentType: return self.componentType.getPositionNearType(tagSet, idx) else: return idx class Set(SequenceAndSetBase): __doc__ = SequenceAndSetBase.__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 = tag.initTagSet( tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) ) #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) #: object representing ASN.1 type allowed within |ASN.1| type componentType = namedtype.NamedTypes() #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` #: object imposing constraints on |ASN.1| objects sizeSpec = constraint.ConstraintsIntersection() # Disambiguation ASN.1 types identification typeId = SequenceAndSetBase.getTypeId() def getComponent(self, innerFlag=False): return self def getComponentByType(self, tagSet, instantiate=True, innerFlag=False): """Returns |ASN.1| type component by ASN.1 tag. Parameters ---------- tagSet : :py:class:`~pyasn1.type.tag.TagSet` Object representing ASN.1 tags to identify one of |ASN.1| object component Keyword Args ------------ instantiate: :class:`bool` If `True` (default), inner component will be automatically instantiated. If 'False' either existing component or the `noValue` object will be returned. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` a pyasn1 object """ component = self.getComponentByPosition( self.componentType.getPositionByType(tagSet), instantiate ) if innerFlag and isinstance(component, Set): # get inner component by inner tagSet return component.getComponent(innerFlag=True) else: # get outer component by inner tagSet return component def setComponentByType(self, tagSet, value=noValue, verifyConstraints=True, matchTags=True, matchConstraints=True, innerFlag=False): """Assign |ASN.1| type component by ASN.1 tag. Parameters ---------- tagSet : :py:class:`~pyasn1.type.tag.TagSet` Object representing ASN.1 tags to identify one of |ASN.1| object component Keyword Args ------------ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. verifyConstraints : :class:`bool` If `False`, skip constraints validation matchTags: :class:`bool` If `False`, skip component tags matching matchConstraints: :class:`bool` If `False`, skip component constraints matching innerFlag: :class:`bool` If `True`, search for matching *tagSet* recursively. Returns ------- self """ idx = self.componentType.getPositionByType(tagSet) if innerFlag: # set inner component by inner tagSet componentType = self.componentType.getTypeByPosition(idx) if componentType.tagSet: return self.setComponentByPosition( idx, value, verifyConstraints, matchTags, matchConstraints ) else: componentType = self.getComponentByPosition(idx) return componentType.setComponentByType( tagSet, value, verifyConstraints, matchTags, matchConstraints, innerFlag=innerFlag ) else: # set outer component by inner tagSet return self.setComponentByPosition( idx, value, verifyConstraints, matchTags, matchConstraints ) @property def componentTagMap(self): if self.componentType: return self.componentType.tagMapUnique class Choice(Set): __doc__ = Set.__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 = tag.TagSet() # untagged #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) #: object representing ASN.1 type allowed within |ASN.1| type componentType = namedtype.NamedTypes() #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` #: object imposing size constraint on |ASN.1| objects sizeSpec = constraint.ConstraintsIntersection( constraint.ValueSizeConstraint(1, 1) ) # Disambiguation ASN.1 types identification typeId = Set.getTypeId() _currentIdx = None def __eq__(self, other): if self._componentValues: return self._componentValues[self._currentIdx] == other return NotImplemented def __ne__(self, other): if self._componentValues: return self._componentValues[self._currentIdx] != other return NotImplemented def __lt__(self, other): if self._componentValues: return self._componentValues[self._currentIdx] < other return NotImplemented def __le__(self, other): if self._componentValues: return self._componentValues[self._currentIdx] <= other return NotImplemented def __gt__(self, other): if self._componentValues: return self._componentValues[self._currentIdx] > other return NotImplemented def __ge__(self, other): if self._componentValues: return self._componentValues[self._currentIdx] >= other return NotImplemented if sys.version_info[0] <= 2: def __nonzero__(self): return self._componentValues and True or False else: def __bool__(self): return self._componentValues and True or False def __len__(self): return self._currentIdx is not None and 1 or 0 def __contains__(self, key): if self._currentIdx is None: return False return key == self.componentType[self._currentIdx].getName() def __iter__(self): if self._currentIdx is None: raise StopIteration yield self.componentType[self._currentIdx].getName() # Python dict protocol def values(self): if self._currentIdx is not None: yield self._componentValues[self._currentIdx] def keys(self): if self._currentIdx is not None: yield self.componentType[self._currentIdx].getName() def items(self): if self._currentIdx is not None: yield self.componentType[self._currentIdx].getName(), self[self._currentIdx] def verifySizeSpec(self): if self._currentIdx is None: raise error.PyAsn1Error('Component not chosen') def _cloneComponentValues(self, myClone, cloneValueFlag): try: component = self.getComponent() except error.PyAsn1Error: pass else: if isinstance(component, Choice): tagSet = component.effectiveTagSet else: tagSet = component.tagSet if isinstance(component, base.AbstractConstructedAsn1Item): myClone.setComponentByType( tagSet, component.clone(cloneValueFlag=cloneValueFlag) ) else: myClone.setComponentByType(tagSet, component.clone()) def getComponentByPosition(self, idx, instantiate=True): __doc__ = Set.__doc__ if self._currentIdx is None or self._currentIdx != idx: return Set.getComponentByPosition(self, idx, instantiate) return self._componentValues[idx] def setComponentByPosition(self, idx, value=noValue, verifyConstraints=True, matchTags=True, matchConstraints=True): """Assign |ASN.1| type component by position. Equivalent to Python sequence item assignment operation (e.g. `[]`). Parameters ---------- idx: :class:`int` Component index (zero-based). Must either refer to existing component or to N+1 component. In the latter case a new component type gets instantiated (if *componentType* is set, or given ASN.1 object is taken otherwise) and appended to the |ASN.1| sequence. Keyword Args ------------ value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative A Python value to initialize |ASN.1| component with (if *componentType* is set) or ASN.1 value object to assign to |ASN.1| component. Once a new value is set to *idx* component, previous value is dropped. verifyConstraints : :class:`bool` If `False`, skip constraints validation matchTags: :class:`bool` If `False`, skip component tags matching matchConstraints: :class:`bool` If `False`, skip component constraints matching Returns ------- self """ oldIdx = self._currentIdx Set.setComponentByPosition(self, idx, value, verifyConstraints, matchTags, matchConstraints) self._currentIdx = idx if oldIdx is not None and oldIdx != idx: self._componentValues[oldIdx] = None return self @property def effectiveTagSet(self): """Return a :class:`~pyasn1.type.tag.TagSet` object of the currently initialized component or self (if |ASN.1| is tagged).""" if self.tagSet: return self.tagSet else: component = self.getComponent() return component.effectiveTagSet @property def tagMap(self): """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects contained within callee. """ if self.tagSet: return Set.tagMap.fget(self) else: return self.componentType.tagMapUnique def getComponent(self, innerFlag=False): """Return currently assigned component of the |ASN.1| object. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` a PyASN1 object """ if self._currentIdx is None: raise error.PyAsn1Error('Component not chosen') else: c = self._componentValues[self._currentIdx] if innerFlag and isinstance(c, Choice): return c.getComponent(innerFlag) else: return c def getName(self, innerFlag=False): """Return the name of currently assigned component of the |ASN.1| object. Returns ------- : :py:class:`str` |ASN.1| component name """ if self._currentIdx is None: raise error.PyAsn1Error('Component not chosen') else: if innerFlag: c = self._componentValues[self._currentIdx] if isinstance(c, Choice): return c.getName(innerFlag) return self.componentType.getNameByPosition(self._currentIdx) @property def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. If *isValue* is `False` then this object represents just ASN.1 schema. If *isValue* is `True` then, in addition to its ASN.1 schema features, this object can also be used like a Python built-in object (e.g. `int`, `str`, `dict` etc.). Returns ------- : :class:`bool` :class:`False` if object represents just ASN.1 schema. :class:`True` if object represents ASN.1 schema and can be used as a normal value. Note ---- There is an important distinction between PyASN1 schema and value objects. The PyASN1 schema objects can only participate in ASN.1 schema-related operations (e.g. defining or testing the structure of the data). Most obvious uses of ASN.1 schema is to guide serialization codecs whilst encoding/decoding serialised ASN.1 contents. The PyASN1 value objects can **additionally** participate in many operations involving regular Python objects (e.g. arithmetic, comprehension etc). """ if self._currentIdx is None: return False return self._componentValues[self._currentIdx].isValue def clear(self): self._currentIdx = None Set.clear(self) # compatibility stubs def getMinTagSet(self): return self.minTagSet class Any(OctetString): __doc__ = OctetString.__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 = tag.TagSet() # untagged #: Set (on class, not on instance) or return a #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object #: imposing constraints on |ASN.1| type initialization values. subtypeSpec = constraint.ConstraintsIntersection() # Disambiguation ASN.1 types identification typeId = OctetString.getTypeId() @property def tagMap(self): """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects contained within callee. """ try: return self._tagMap except AttributeError: self._tagMap = tagmap.TagMap( {self.tagSet: self}, {eoo.endOfOctets.tagSet: eoo.endOfOctets}, self ) return self._tagMap # XXX # coercion rules?