# # This file is part of pyasn1 software. # # Copyright (c) 2005-2020, Ilya Etingof # License: http://snmplabs.com/pyasn1/license.html # import math import sys from pyasn1 import error from pyasn1.codec.ber import eoo from pyasn1.compat import integer from pyasn1.compat import octets from pyasn1.type import base from pyasn1.type import constraint from pyasn1.type import namedtype from pyasn1.type import namedval from pyasn1.type import tag from pyasn1.type import tagmap 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.SimpleAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object Python :class:`int` or :class:`str` literal or |ASN.1| class instance. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class ErrorCode(Integer): ''' ASN.1 specification: ErrorCode ::= INTEGER { disk-full(1), no-disk(-1), disk-not-formatted(2) } error ErrorCode ::= disk-full ''' namedValues = NamedValues( ('disk-full', 1), ('no-disk', -1), ('disk-not-formatted', 2) ) error = ErrorCode('disk-full') """ #: 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.SimpleAsn1Type.getTypeId() def __init__(self, value=noValue, **kwargs): if 'namedValues' not in kwargs: kwargs['namedValues'] = self.namedValues base.SimpleAsn1Type.__init__(self, value, **kwargs) 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.SimpleAsn1Type.__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) 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 str(self.namedValues[value]) except KeyError: return str(value) # backward compatibility def getNamedValues(self): return self.namedValues class Boolean(Integer): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object Python :class:`int` or :class:`str` literal or |ASN.1| class instance. If `value` is not given, schema object will be created. 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).Constraints verification for |ASN.1| type occurs automatically on object instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class RoundResult(Boolean): ''' ASN.1 specification: RoundResult ::= BOOLEAN ok RoundResult ::= TRUE ko RoundResult ::= FALSE ''' ok = RoundResult(True) ko = RoundResult(False) """ #: 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() 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 class BitString(base.SimpleAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its 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 :class:`int` or :class:`str` literal representing binary or hexadecimal number or sequence of integer bits or |ASN.1| object. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. 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 ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class Rights(BitString): ''' ASN.1 specification: Rights ::= BIT STRING { user-read(0), user-write(1), group-read(2), group-write(3), other-read(4), other-write(5) } group1 Rights ::= { group-read, group-write } group2 Rights ::= '0011'B group3 Rights ::= '3'H ''' namedValues = NamedValues( ('user-read', 0), ('user-write', 1), ('group-read', 2), ('group-write', 3), ('other-read', 4), ('other-write', 5) ) group1 = Rights(('group-read', 'group-write')) group2 = Rights('0011') group3 = Rights(0x3) """ #: 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.SimpleAsn1Type.getTypeId() defaultBinValue = defaultHexValue = noValue 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.SimpleAsn1Type.__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(SizedInteger(self._value << len(value) | value).setBitLength(len(self._value) + len(value))) def __radd__(self, value): value = self.prettyIn(value) return self.clone(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(SizedInteger(self._value << count).setBitLength(len(self._value) + count)) def __rshift__(self, count): return self.clone(SizedInteger(self._value >> count).setBitLength(max(0, len(self._value) - count))) def __int__(self): return int(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 = 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 = 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 = SizedInteger( (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 = 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 = SizedInteger( (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 = SizedInteger(integer.from_bytes(value) >> padding).setBitLength(len(value) * 8 - padding) if prepend is not None: value = SizedInteger( (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, SizedInteger): return value elif octets.isStringType(value): if not value: return 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 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 SizedInteger(value).setBitLength(len(value)) elif isinstance(value, intTypes): return SizedInteger(value) else: raise error.PyAsn1Error( 'Bad BitString initializer type \'%s\'' % (value,) ) class OctetString(base.SimpleAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its 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|" serialisation. Keyword Args ------------ value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object class:`str` (Python 2) or :class:`bytes` (Python 3), alternatively class:`unicode` object (Python 2) or :class:`str` (Python 3) representing character string to be serialised into octets (note `encoding` parameter) or |ASN.1| object. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. 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 ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class Icon(OctetString): ''' ASN.1 specification: Icon ::= OCTET STRING icon1 Icon ::= '001100010011001000110011'B icon2 Icon ::= '313233'H ''' icon1 = Icon.fromBinaryString('001100010011001000110011') icon2 = Icon.fromHexString('313233') """ #: 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.SimpleAsn1Type.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.SimpleAsn1Type.__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): exc = sys.exc_info()[1] raise error.PyAsn1UnicodeEncodeError( "Can't encode string '%s' with codec " "%s" % (value, self.encoding), exc ) 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: exc = sys.exc_info()[1] raise error.PyAsn1UnicodeDecodeError( "Can't decode string '%s' with codec " "%s" % (self._value, self.encoding), exc ) 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: exc = sys.exc_info()[1] raise error.PyAsn1UnicodeEncodeError( "Can't encode string '%s' with '%s' " "codec" % (value, self.encoding), exc ) elif isinstance(value, OctetString): # a shortcut, bytes() would work the same way return value.asOctets() elif isinstance(value, base.SimpleAsn1Type): # 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: exc = sys.exc_info()[1] raise error.PyAsn1UnicodeDecodeError( "Can't decode string '%s' with '%s' codec at " "'%s'" % (self._value, self.encoding, self.__class__.__name__), exc ) def __bytes__(self): return bytes(self._value) def asOctets(self): return bytes(self._value) def asNumbers(self): return tuple(self._value) # # Normally, `.prettyPrint()` is called from `__str__()`. Historically, # OctetString.prettyPrint() used to return hexified payload # representation in cases when non-printable content is present. At the # same time `str()` used to produce either octet-stream (Py2) or # text (Py3) representations. # # Therefore `OctetString.__str__()` -> `.prettyPrint()` call chain is # reversed to preserve the original behaviour. # # Eventually we should deprecate `.prettyPrint()` / `.prettyOut()` harness # and end up with just `__str__()` producing hexified representation while # both text and octet-stream representation should only be requested via # the `.asOctets()` method. # # Note: ASN.1 OCTET STRING is never mean to contain text! # def prettyOut(self, value): return value def prettyPrint(self, scope=0): # first see if subclass has its own .prettyOut() value = self.prettyOut(self._value) if value is not self._value: return value numbers = self.asNumbers() for x in numbers: # hexify if needed if x < 32 or x > 126: return '0x' + ''.join(('%.2x' % x for x in numbers)) else: # this prevents infinite recursion return OctetString.__str__(self) @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) # 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| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its objects are immutable and duck-type Python :class:`str` objects (always empty). Keyword Args ------------ value: :class:`str` or |ASN.1| object Python empty :class:`str` literal or any object that evaluates to :obj:`False` If `value` is not given, schema object will be created. tagSet: :py:class:`~pyasn1.type.tag.TagSet` Object representing non-default ASN.1 tag(s) Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class Ack(Null): ''' ASN.1 specification: Ack ::= NULL ''' ack = Ack('') """ #: 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.SimpleAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its 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 :class:`str` literal or |ASN.1| object. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class ID(ObjectIdentifier): ''' ASN.1 specification: ID ::= OBJECT IDENTIFIER id-edims ID ::= { joint-iso-itu-t mhs-motif(6) edims(7) } id-bp ID ::= { id-edims 11 } ''' id_edims = ID('2.6.7') id_bp = id_edims + (11,) """ #: 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.SimpleAsn1Type.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 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` :obj:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object or :obj:`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.SimpleAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its 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 :class:`float` instance or |ASN.1| object. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class Pi(Real): ''' ASN.1 specification: Pi ::= REAL pi Pi ::= { mantissa 314159, base 10, exponent -5 } ''' pi = Pi((314159, 10, -5)) """ 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.SimpleAsn1Type.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 prettyPrint(self, scope=0): try: return self.prettyOut(float(self)) except OverflowError: return '' @property def isPlusInf(self): """Indicate PLUS-INFINITY object value Returns ------- : :class:`bool` :obj:`True` if calling object represents plus infinity or :obj:`False` otherwise. """ return self._value == self._plusInf @property def isMinusInf(self): """Indicate MINUS-INFINITY object value Returns ------- : :class:`bool` :obj:`True` if calling object represents minus infinity or :obj:`False` otherwise. """ return self._value == self._minusInf @property def isInf(self): return self._value in self._inf 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))) 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.SimpleAsn1Type.__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): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its objects are immutable and duck-type Python :class:`int` objects. Keyword Args ------------ value: :class:`int`, :class:`str` or |ASN.1| object Python :class:`int` or :class:`str` literal or |ASN.1| object. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` Object representing non-default symbolic aliases for numbers Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class RadioButton(Enumerated): ''' ASN.1 specification: RadioButton ::= ENUMERATED { button1(0), button2(1), button3(2) } selected-by-default RadioButton ::= button1 ''' namedValues = NamedValues( ('button1', 0), ('button2', 1), ('button3', 2) ) selected_by_default = RadioButton('button1') """ #: 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.ConstructedAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, its 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). Constraints verification for |ASN.1| type can only occur on explicit `.isInconsistent` call. Examples -------- .. code-block:: python class LotteryDraw(SequenceOf): # SetOf is similar ''' ASN.1 specification: LotteryDraw ::= SEQUENCE OF INTEGER ''' componentType = Integer() lotteryDraw = LotteryDraw() lotteryDraw.extend([123, 456, 789]) """ def __init__(self, *args, **kwargs): # support positional params for backward compatibility if args: for key, value in zip(('componentType', 'tagSet', 'subtypeSpec'), args): if key in kwargs: raise error.PyAsn1Error('Conflicting positional and keyword params!') kwargs['componentType'] = value self._componentValues = noValue base.ConstructedAsn1Type.__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 append(self, value): if self._componentValues is noValue: pos = 0 else: pos = len(self._componentValues) self[pos] = value def count(self, value): return list(self._componentValues.values()).count(value) def extend(self, values): for value in values: self.append(value) if self._componentValues is noValue: self._componentValues = {} def index(self, value, start=0, stop=None): if stop is None: stop = len(self) indices, values = zip(*self._componentValues.items()) # TODO: remove when Py2.5 support is gone values = list(values) try: return indices[values.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 = dict( enumerate(sorted(self._componentValues.values(), key=key, reverse=reverse))) def __len__(self): if self._componentValues is noValue or not self._componentValues: return 0 return max(self._componentValues) + 1 def __iter__(self): for idx in range(0, len(self)): yield self.getComponentByPosition(idx) def _cloneComponentValues(self, myClone, cloneValueFlag): for idx, componentValue in self._componentValues.items(): if componentValue is not noValue: if isinstance(componentValue, base.ConstructedAsn1Type): myClone.setComponentByPosition( idx, componentValue.clone(cloneValueFlag=cloneValueFlag) ) else: myClone.setComponentByPosition(idx, componentValue.clone()) def getComponentByPosition(self, idx, default=noValue, 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 ------------ default: :class:`object` If set and requested component is a schema object, return the `default` object instead of the requested component. instantiate: :class:`bool` If :obj:`True` (default), inner component will be automatically instantiated. If :obj:`False` either existing component or the :class:`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 component #0 with `.isValue` property False s.getComponentByPosition(0) # returns None s.getComponentByPosition(0, default=None) s.clear() # 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) """ if isinstance(idx, slice): indices = tuple(range(len(self))) return [self.getComponentByPosition(subidx, default, instantiate) for subidx in indices[idx]] if idx < 0: idx = len(self) + idx if idx < 0: raise error.PyAsn1Error( 'SequenceOf/SetOf index is out of range') try: componentValue = self._componentValues[idx] except (KeyError, error.PyAsn1Error): if not instantiate: return default self.setComponentByPosition(idx) componentValue = self._componentValues[idx] if default is noValue or componentValue.isValue: return componentValue else: return default 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. If `value` is not given, schema object will be set as a component. verifyConstraints: :class:`bool` If :obj:`False`, skip constraints validation matchTags: :class:`bool` If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` If :obj:`False`, skip component constraints matching Returns ------- self Raises ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer IndexError When idx > len(self) """ if isinstance(idx, slice): indices = tuple(range(len(self))) startIdx = indices and indices[idx][0] or 0 for subIdx, subValue in enumerate(value): self.setComponentByPosition( startIdx + subIdx, subValue, verifyConstraints, matchTags, matchConstraints) return self if idx < 0: idx = len(self) + idx if idx < 0: raise error.PyAsn1Error( 'SequenceOf/SetOf index is out of range') componentType = self.componentType if self._componentValues is noValue: componentValues = {} else: componentValues = self._componentValues currentValue = componentValues.get(idx, noValue) 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.SimpleAsn1Type)): value = componentType.clone(value=value) elif (currentValue is not noValue and isinstance(currentValue, base.SimpleAsn1Type)): 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 and (matchTags or matchConstraints): subtypeChecker = ( self.strictConstraints and componentType.isSameTypeWith or componentType.isSuperTypeOf) if not subtypeChecker(value, verifyConstraints and matchTags, verifyConstraints and matchConstraints): # TODO: we should wrap componentType with UnnamedType to carry # additional properties associated with componentType if componentType.typeId != Any.typeId: raise error.PyAsn1Error( 'Component value is tag-incompatible: %r vs ' '%r' % (value, componentType)) componentValues[idx] = value self._componentValues = componentValues return self @property def componentTagMap(self): if self.componentType is not None: return self.componentType.tagMap @property def components(self): return [self._componentValues[idx] for idx in sorted(self._componentValues)] def clear(self): """Remove all components and become an empty |ASN.1| value object. Has the same effect on |ASN.1| object as it does on :class:`list` built-in. """ self._componentValues = {} return self def reset(self): """Remove all components and become a |ASN.1| schema object. See :meth:`isValue` property for more information on the distinction between value and schema objects. """ self._componentValues = noValue return self def prettyPrint(self, scope=0): scope += 1 representation = self.__class__.__name__ + ':\n' if not self.isValue: return representation for idx, componentValue in enumerate(self): 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 :obj:`False` then this object represents just ASN.1 schema. If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, this object can also be used like a Python built-in object (e.g. :class:`int`, :class:`str`, :class:`dict` etc.). Returns ------- : :class:`bool` :obj:`False` if object represents just ASN.1 schema. :obj:`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 serialisation 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._componentValues is noValue: return False if len(self._componentValues) != len(self): return False for componentValue in self._componentValues.values(): if componentValue is noValue or not componentValue.isValue: return False return True @property def isInconsistent(self): """Run necessary checks to ensure |ASN.1| object consistency. Default action is to verify |ASN.1| object against constraints imposed by `subtypeSpec`. Raises ------ :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found """ if self.componentType is noValue or not self.subtypeSpec: return False if self._componentValues is noValue: return True mapping = {} for idx, value in self._componentValues.items(): # Absent fields are not in the mapping if value is noValue: continue mapping[idx] = value try: # Represent SequenceOf/SetOf as a bare dict to constraints chain self.subtypeSpec(mapping) except error.PyAsn1Error: exc = sys.exc_info()[1] return exc return False 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() # 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() # Disambiguation ASN.1 types identification typeId = SequenceOfAndSetOfBase.getTypeId() class SequenceAndSetBase(base.ConstructedAsn1Type): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, its 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). Constraints verification for |ASN.1| type can only occur on explicit `.isInconsistent` call. Examples -------- .. code-block:: python class Description(Sequence): # Set is similar ''' ASN.1 specification: Description ::= SEQUENCE { surname IA5String, first-name IA5String OPTIONAL, age INTEGER DEFAULT 40 } ''' componentType = NamedTypes( NamedType('surname', IA5String()), OptionalNamedType('first-name', IA5String()), DefaultedNamedType('age', Integer(40)) ) descr = Description() descr['surname'] = 'Smith' descr['first-name'] = 'John' """ #: 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.ConstructedAsn1Type.__init__(self, **kwargs) self._componentTypeLen = len(self.componentType) if self._componentTypeLen: self._componentValues = [] else: self._componentValues = noValue 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 __len__(self): return len(self._componentValues) 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): """Remove all components and become an empty |ASN.1| value object. Has the same effect on |ASN.1| object as it does on :class:`dict` built-in. """ self._componentValues = [] self._dynamicNames = self.DynamicNames() return self def reset(self): """Remove all components and become a |ASN.1| schema object. See :meth:`isValue` property for more information on the distinction between value and schema objects. """ self._componentValues = noValue self._dynamicNames = self.DynamicNames() return self @property def components(self): return self._componentValues def _cloneComponentValues(self, myClone, cloneValueFlag): if self._componentValues is noValue: return for idx, componentValue in enumerate(self._componentValues): if componentValue is not noValue: if isinstance(componentValue, base.ConstructedAsn1Type): myClone.setComponentByPosition( idx, componentValue.clone(cloneValueFlag=cloneValueFlag) ) else: myClone.setComponentByPosition(idx, componentValue.clone()) def getComponentByName(self, name, default=noValue, 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 ------------ default: :class:`object` If set and requested component is a schema object, return the `default` object instead of the requested component. instantiate: :class:`bool` If :obj:`True` (default), inner component will be automatically instantiated. If :obj:`False` either existing component or the :class:`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, default=default, instantiate=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. If `value` is not given, schema object will be set as a component. verifyConstraints: :class:`bool` If :obj:`False`, skip constraints validation matchTags: :class:`bool` If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` If :obj:`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, default=noValue, 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 ------------ default: :class:`object` If set and requested component is a schema object, return the `default` object instead of the requested component. instantiate: :class:`bool` If :obj:`True` (default), inner component will be automatically instantiated. If :obj:`False` either existing component or the :class:`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 component #0 with `.isValue` property False s.getComponentByPosition(0) # returns None s.getComponentByPosition(0, default=None) s.clear() # 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: if self._componentValues is noValue: componentValue = noValue else: componentValue = self._componentValues[idx] except IndexError: componentValue = noValue if not instantiate: if componentValue is noValue or not componentValue.isValue: return default else: return componentValue if componentValue is noValue: self.setComponentByPosition(idx) componentValue = self._componentValues[idx] if default is noValue or componentValue.isValue: return componentValue else: return default 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. If `value` is not given, schema object will be set as a component. verifyConstraints : :class:`bool` If :obj:`False`, skip constraints validation matchTags: :class:`bool` If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` If :obj:`False`, skip component constraints matching Returns ------- self """ componentType = self.componentType componentTypeLen = self._componentTypeLen if self._componentValues is noValue: componentValues = [] else: componentValues = self._componentValues try: currentValue = componentValues[idx] except IndexError: currentValue = noValue if componentTypeLen: if componentTypeLen < idx: raise error.PyAsn1Error('component index out of range') componentValues = [noValue] * componentTypeLen if value is noValue: if componentTypeLen: value = componentType.getTypeByPosition(idx) if isinstance(value, base.ConstructedAsn1Type): value = value.clone(cloneValueFlag=componentType[idx].isDefaulted) 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.SimpleAsn1Type): 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.SimpleAsn1Type): value = currentValue.clone(value=value) else: raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__) elif ((verifyConstraints or matchTags or matchConstraints) and componentTypeLen): subComponentType = componentType.getTypeByPosition(idx) if subComponentType is not noValue: subtypeChecker = (self.strictConstraints and subComponentType.isSameTypeWith or subComponentType.isSuperTypeOf) if not subtypeChecker(value, verifyConstraints and matchTags, verifyConstraints and matchConstraints): if not componentType[idx].openType: raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) if componentTypeLen or idx in self._dynamicNames: componentValues[idx] = value elif len(componentValues) == idx: componentValues.append(value) self._dynamicNames.addField(idx) else: raise error.PyAsn1Error('Component index out of range') self._componentValues = componentValues return self @property def isValue(self): """Indicate that |ASN.1| object represents ASN.1 value. If *isValue* is :obj:`False` then this object represents just ASN.1 schema. If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, this object can also be used like a Python built-in object (e.g. :class:`int`, :class:`str`, :class:`dict` etc.). Returns ------- : :class:`bool` :obj:`False` if object represents just ASN.1 schema. :obj:`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 serialisation 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). It is sufficient for |ASN.1| objects to have all non-optional and non-defaulted components being value objects to be considered as a value objects as a whole. In other words, even having one or more optional components not turned into value objects, |ASN.1| object is still considered as a value object. Defaulted components are normally value objects by default. """ if self._componentValues is noValue: return False componentType = self.componentType if componentType: for idx, subComponentType in enumerate(componentType.namedTypes): if subComponentType.isDefaulted or subComponentType.isOptional: continue if not self._componentValues: return False componentValue = self._componentValues[idx] if componentValue is noValue or not componentValue.isValue: return False else: for componentValue in self._componentValues: if componentValue is noValue or not componentValue.isValue: return False return True @property def isInconsistent(self): """Run necessary checks to ensure |ASN.1| object consistency. Default action is to verify |ASN.1| object against constraints imposed by `subtypeSpec`. Raises ------ :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found """ if self.componentType is noValue or not self.subtypeSpec: return False if self._componentValues is noValue: return True mapping = {} for idx, value in enumerate(self._componentValues): # Absent fields are not in the mapping if value is noValue: continue name = self.componentType.getNameByPosition(idx) mapping[name] = value try: # Represent Sequence/Set as a bare dict to constraints chain self.subtypeSpec(mapping) except error.PyAsn1Error: exc = sys.exc_info()[1] return exc return False 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 and componentValue.isValue: 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 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() # Disambiguation ASN.1 types identification typeId = SequenceAndSetBase.getTypeId() def getComponent(self, innerFlag=False): return self def getComponentByType(self, tagSet, default=noValue, 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 ------------ default: :class:`object` If set and requested component is a schema object, return the `default` object instead of the requested component. instantiate: :class:`bool` If :obj:`True` (default), inner component will be automatically instantiated. If :obj:`False` either existing component or the :class:`noValue` object will be returned. Returns ------- : :py:class:`~pyasn1.type.base.PyAsn1Item` a pyasn1 object """ componentValue = self.getComponentByPosition( self.componentType.getPositionByType(tagSet), default=default, instantiate=instantiate ) if innerFlag and isinstance(componentValue, Set): # get inner component by inner tagSet return componentValue.getComponent(innerFlag=True) else: # get outer component by inner tagSet return componentValue 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. If `value` is not given, schema object will be set as a component. verifyConstraints : :class:`bool` If :obj:`False`, skip constraints validation matchTags: :class:`bool` If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` If :obj:`False`, skip component constraints matching innerFlag: :class:`bool` If :obj:`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): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, its objects are mutable and duck-type Python :class:`list` 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). Constraints verification for |ASN.1| type can only occur on explicit `.isInconsistent` call. Examples -------- .. code-block:: python class Afters(Choice): ''' ASN.1 specification: Afters ::= CHOICE { cheese [0] IA5String, dessert [1] IA5String } ''' componentType = NamedTypes( NamedType('cheese', IA5String().subtype( implicitTag=Tag(tagClassContext, tagFormatSimple, 0) ), NamedType('dessert', IA5String().subtype( implicitTag=Tag(tagClassContext, tagFormatSimple, 1) ) ) afters = Afters() afters['cheese'] = 'Mascarpone' """ #: 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( 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 checkConsistency(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.ConstructedAsn1Type): myClone.setComponentByType( tagSet, component.clone(cloneValueFlag=cloneValueFlag) ) else: myClone.setComponentByType(tagSet, component.clone()) def getComponentByPosition(self, idx, default=noValue, instantiate=True): __doc__ = Set.__doc__ if self._currentIdx is None or self._currentIdx != idx: return Set.getComponentByPosition(self, idx, default=default, instantiate=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. If `value` is not given, schema object will be set as a component. verifyConstraints : :class:`bool` If :obj:`False`, skip constraints validation matchTags: :class:`bool` If :obj:`False`, skip component tags matching matchConstraints: :class:`bool` If :obj:`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] = noValue 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 :obj:`False` then this object represents just ASN.1 schema. If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, this object can also be used like a Python built-in object (e.g. :class:`int`, :class:`str`, :class:`dict` etc.). Returns ------- : :class:`bool` :obj:`False` if object represents just ASN.1 schema. :obj:`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 serialisation 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 componentValue = self._componentValues[self._currentIdx] return componentValue is not noValue and componentValue.isValue def clear(self): self._currentIdx = None return Set.clear(self) # compatibility stubs def getMinTagSet(self): return self.minTagSet class Any(OctetString): """Create |ASN.1| schema or value object. |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its 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|" serialisation. Keyword Args ------------ value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object :class:`str` (Python 2) or :class:`bytes` (Python 3), alternatively :class:`unicode` object (Python 2) or :class:`str` (Python 3) representing character string to be serialised into octets (note `encoding` parameter) or |ASN.1| object. If `value` is not given, schema object will be created. 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). Constraints verification for |ASN.1| type occurs automatically on object instantiation. 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 ------ ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error On constraint violation or bad initializer. Examples -------- .. code-block:: python class Error(Sequence): ''' ASN.1 specification: Error ::= SEQUENCE { code INTEGER, parameter ANY DEFINED BY code -- Either INTEGER or REAL } ''' componentType=NamedTypes( NamedType('code', Integer()), NamedType('parameter', Any(), openType=OpenType('code', {1: Integer(), 2: Real()})) ) error = Error() error['code'] = 1 error['parameter'] = Integer(1234) """ #: 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?