1# 2# This file is part of pyasn1 software. 3# 4# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com> 5# License: http://pyasn1.sf.net/license.html 6# 7import sys 8from pyasn1.type import constraint, tagmap, tag 9from pyasn1.compat import calling 10from pyasn1 import error 11 12__all__ = ['Asn1Item', 'Asn1ItemBase', 'AbstractSimpleAsn1Item', 'AbstractConstructedAsn1Item'] 13 14 15class Asn1Item(object): 16 @classmethod 17 def getTypeId(cls, increment=1): 18 try: 19 Asn1Item._typeCounter += increment 20 except AttributeError: 21 Asn1Item._typeCounter = increment 22 return Asn1Item._typeCounter 23 24 25class Asn1ItemBase(Asn1Item): 26 #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing 27 #: ASN.1 tag(s) associated with |ASN.1| type. 28 tagSet = tag.TagSet() 29 30 #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 31 #: object imposing constraints on initialization values. 32 subtypeSpec = constraint.ConstraintsIntersection() 33 34 # Disambiguation ASN.1 types identification 35 typeId = None 36 37 def __init__(self, **kwargs): 38 readOnly = { 39 'tagSet': self.tagSet, 40 'subtypeSpec': self.subtypeSpec 41 } 42 43 readOnly.update(kwargs) 44 45 self.__dict__.update(readOnly) 46 47 self._readOnly = readOnly 48 49 def __setattr__(self, name, value): 50 if name[0] != '_' and name in self._readOnly: 51 raise error.PyAsn1Error('read-only instance attribute "%s"' % name) 52 53 self.__dict__[name] = value 54 55 @property 56 def readOnly(self): 57 return self._readOnly 58 59 @property 60 def effectiveTagSet(self): 61 """For |ASN.1| type is equivalent to *tagSet* 62 """ 63 return self.tagSet # used by untagged types 64 65 @property 66 def tagMap(self): 67 """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object. 68 """ 69 return tagmap.TagMap({self.tagSet: self}) 70 71 def isSameTypeWith(self, other, matchTags=True, matchConstraints=True): 72 """Examine |ASN.1| type for equality with other ASN.1 type. 73 74 ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints 75 (:py:mod:`~pyasn1.type.constraint`) are examined when carrying 76 out ASN.1 types comparison. 77 78 No Python inheritance relationship between PyASN1 objects is considered. 79 80 Parameters 81 ---------- 82 other: a pyasn1 type object 83 Class instance representing ASN.1 type. 84 85 Returns 86 ------- 87 : :class:`bool` 88 :class:`True` if *other* is |ASN.1| type, 89 :class:`False` otherwise. 90 """ 91 return (self is other or 92 (not matchTags or self.tagSet == other.tagSet) and 93 (not matchConstraints or self.subtypeSpec == other.subtypeSpec)) 94 95 def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True): 96 """Examine |ASN.1| type for subtype relationship with other ASN.1 type. 97 98 ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints 99 (:py:mod:`~pyasn1.type.constraint`) are examined when carrying 100 out ASN.1 types comparison. 101 102 No Python inheritance relationship between PyASN1 objects is considered. 103 104 105 Parameters 106 ---------- 107 other: a pyasn1 type object 108 Class instance representing ASN.1 type. 109 110 Returns 111 ------- 112 : :class:`bool` 113 :class:`True` if *other* is a subtype of |ASN.1| type, 114 :class:`False` otherwise. 115 """ 116 return (not matchTags or 117 (self.tagSet.isSuperTagSetOf(other.tagSet)) and 118 (not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec))) 119 120 @staticmethod 121 def isNoValue(*values): 122 for value in values: 123 if value is not None and value is not noValue: 124 return False 125 return True 126 127 # backward compatibility 128 129 def getTagSet(self): 130 return self.tagSet 131 132 def getEffectiveTagSet(self): 133 return self.effectiveTagSet 134 135 def getTagMap(self): 136 return self.tagMap 137 138 def getSubtypeSpec(self): 139 return self.subtypeSpec 140 141 def hasValue(self): 142 return self.isValue 143 144 145class NoValue(object): 146 """Create a singleton instance of NoValue class. 147 148 NoValue object can be used as an initializer on PyASN1 type class 149 instantiation to represent ASN.1 type rather than ASN.1 data value. 150 151 No operations other than type comparison can be performed on 152 a PyASN1 type object. 153 """ 154 skipMethods = ('__getattribute__', '__getattr__', '__setattr__', '__delattr__', 155 '__class__', '__init__', '__del__', '__new__', '__repr__', 156 '__qualname__', '__objclass__', 'im_class', '__sizeof__') 157 158 _instance = None 159 160 def __new__(cls): 161 if cls._instance is None: 162 def getPlug(name): 163 def plug(self, *args, **kw): 164 raise error.PyAsn1Error('Uninitialized ASN.1 value ("%s" attribute looked up)' % name) 165 return plug 166 167 op_names = [name 168 for typ in (str, int, list, dict) 169 for name in dir(typ) 170 if (name not in cls.skipMethods and 171 name.startswith('__') and 172 name.endswith('__') and 173 calling.callable(getattr(typ, name)))] 174 175 for name in set(op_names): 176 setattr(cls, name, getPlug(name)) 177 178 cls._instance = object.__new__(cls) 179 180 return cls._instance 181 182 def __getattr__(self, attr): 183 if attr in self.skipMethods: 184 raise AttributeError('attribute %s not present' % attr) 185 raise error.PyAsn1Error('No value for "%s"' % attr) 186 187 def __repr__(self): 188 return '%s()' % self.__class__.__name__ 189 190noValue = NoValue() 191 192 193# Base class for "simple" ASN.1 objects. These are immutable. 194class AbstractSimpleAsn1Item(Asn1ItemBase): 195 #: Default payload value 196 defaultValue = noValue 197 198 def __init__(self, value=noValue, **kwargs): 199 Asn1ItemBase.__init__(self, **kwargs) 200 if value is noValue or value is None: 201 value = self.defaultValue 202 else: 203 value = self.prettyIn(value) 204 try: 205 self.subtypeSpec(value) 206 207 except error.PyAsn1Error: 208 exType, exValue, exTb = sys.exc_info() 209 raise exType('%s at %s' % (exValue, self.__class__.__name__)) 210 211 self._value = value 212 213 def __repr__(self): 214 representation = [] 215 if self._value is not self.defaultValue: 216 representation.append(self.prettyOut(self._value)) 217 if self.tagSet is not self.__class__.tagSet: 218 representation.append('tagSet=%r' % (self.tagSet,)) 219 if self.subtypeSpec is not self.__class__.subtypeSpec: 220 representation.append('subtypeSpec=%r' % (self.subtypeSpec,)) 221 return '%s(%s)' % (self.__class__.__name__, ', '.join(representation)) 222 223 def __str__(self): 224 return str(self._value) 225 226 def __eq__(self, other): 227 return self is other and True or self._value == other 228 229 def __ne__(self, other): 230 return self._value != other 231 232 def __lt__(self, other): 233 return self._value < other 234 235 def __le__(self, other): 236 return self._value <= other 237 238 def __gt__(self, other): 239 return self._value > other 240 241 def __ge__(self, other): 242 return self._value >= other 243 244 if sys.version_info[0] <= 2: 245 def __nonzero__(self): 246 return self._value and True or False 247 else: 248 def __bool__(self): 249 return self._value and True or False 250 251 def __hash__(self): 252 return hash(self._value) 253 254 @property 255 def isValue(self): 256 """Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value. 257 258 In other words, if *isValue* is `True`, then the ASN.1 object is 259 initialized. 260 261 Returns 262 ------- 263 : :class:`bool` 264 :class:`True` if object represents ASN.1 value and type, 265 :class:`False` if object represents just ASN.1 type. 266 267 Note 268 ---- 269 There is an important distinction between PyASN1 type and value objects. 270 The PyASN1 type objects can only participate in ASN.1 type 271 operations (subtyping, comparison etc) and serve as a 272 blueprint for serialization codecs to resolve ambiguous types. 273 274 The PyASN1 value objects can additionally participate in most 275 of built-in Python operations. 276 """ 277 return self._value is not noValue 278 279 def clone(self, value=noValue, **kwargs): 280 """Create a copy of a |ASN.1| type or object. 281 282 Any parameters to the *clone()* method will replace corresponding 283 properties of the |ASN.1| object. 284 285 Parameters 286 ---------- 287 value: :class:`tuple`, :class:`str` or |ASN.1| object 288 Initialization value to pass to new ASN.1 object instead of 289 inheriting one from the caller. 290 291 tagSet: :py:class:`~pyasn1.type.tag.TagSet` 292 Object representing ASN.1 tag(s) to use in new object instead of inheriting from the caller 293 294 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 295 Object representing ASN.1 subtype constraint(s) to use in new object instead of inheriting from the caller 296 297 Returns 298 ------- 299 : 300 new instance of |ASN.1| type/value 301 """ 302 if value is noValue or value is None: 303 if not kwargs: 304 return self 305 306 value = self._value 307 308 initilaizers = self.readOnly.copy() 309 initilaizers.update(kwargs) 310 311 return self.__class__(value, **initilaizers) 312 313 def subtype(self, value=noValue, **kwargs): 314 """Create a copy of a |ASN.1| type or object. 315 316 Any parameters to the *subtype()* method will be added to the corresponding 317 properties of the |ASN.1| object. 318 319 Parameters 320 ---------- 321 value: :class:`tuple`, :class:`str` or |ASN.1| object 322 Initialization value to pass to new ASN.1 object instead of 323 inheriting one from the caller. 324 325 implicitTag: :py:class:`~pyasn1.type.tag.Tag` 326 Implicitly apply given ASN.1 tag object to caller's 327 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as 328 new object's ASN.1 tag(s). 329 330 explicitTag: :py:class:`~pyasn1.type.tag.Tag` 331 Explicitly apply given ASN.1 tag object to caller's 332 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as 333 new object's ASN.1 tag(s). 334 335 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 336 Add ASN.1 constraints object to one of the caller, then 337 use the result as new object's ASN.1 constraints. 338 339 Returns 340 ------- 341 : 342 new instance of |ASN.1| type/value 343 """ 344 if value is noValue or value is None: 345 if not kwargs: 346 return self 347 348 value = self._value 349 350 initializers = self.readOnly.copy() 351 352 implicitTag = kwargs.pop('implicitTag', None) 353 if implicitTag is not None: 354 initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag) 355 356 explicitTag = kwargs.pop('explicitTag', None) 357 if explicitTag is not None: 358 initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag) 359 360 for arg, option in kwargs.items(): 361 initializers[arg] += option 362 363 return self.__class__(value, **initializers) 364 365 def prettyIn(self, value): 366 return value 367 368 def prettyOut(self, value): 369 return str(value) 370 371 def prettyPrint(self, scope=0): 372 """Provide human-friendly printable object representation. 373 374 Returns 375 ------- 376 : :class:`str` 377 human-friendly type and/or value representation. 378 """ 379 if self.isValue: 380 return self.prettyOut(self._value) 381 else: 382 return '<no value>' 383 384 # XXX Compatibility stub 385 def prettyPrinter(self, scope=0): 386 return self.prettyPrint(scope) 387 388 # noinspection PyUnusedLocal 389 def prettyPrintType(self, scope=0): 390 return '%s -> %s' % (self.tagSet, self.__class__.__name__) 391 392# 393# Constructed types: 394# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice 395# * ASN1 types and values are represened by Python class instances 396# * Value initialization is made for defaulted components only 397# * Primary method of component addressing is by-position. Data model for base 398# type is Python sequence. Additional type-specific addressing methods 399# may be implemented for particular types. 400# * SequenceOf and SetOf types do not implement any additional methods 401# * Sequence, Set and Choice types also implement by-identifier addressing 402# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing 403# * Sequence and Set types may include optional and defaulted 404# components 405# * Constructed types hold a reference to component types used for value 406# verification and ordering. 407# * Component type is a scalar type for SequenceOf/SetOf types and a list 408# of types for Sequence/Set/Choice. 409# 410 411def setupComponent(): 412 """Returns a sentinel value. 413 414 Indicates to a constructed type to set up its inner component so that it 415 can be referred to. This is useful in situation when you want to populate 416 descendants of a constructed type what requires being able to refer to 417 their parent types along the way. 418 419 Example 420 ------- 421 422 >>> constructed['record'] = setupComponent() 423 >>> constructed['record']['scalar'] = 42 424 """ 425 return noValue 426 427 428class AbstractConstructedAsn1Item(Asn1ItemBase): 429 430 #: If `True`, requires exact component type matching, 431 #: otherwise subtype relation is only enforced 432 strictConstraints = False 433 434 componentType = None 435 sizeSpec = None 436 437 def __init__(self, **kwargs): 438 readOnly = { 439 'componentType': self.componentType, 440 'sizeSpec': self.sizeSpec 441 } 442 readOnly.update(kwargs) 443 444 Asn1ItemBase.__init__(self, **readOnly) 445 446 self._componentValues = [] 447 448 def __repr__(self): 449 representation = [] 450 if self.componentType is not self.__class__.componentType: 451 representation.append('componentType=%r' % (self.componentType,)) 452 if self.tagSet is not self.__class__.tagSet: 453 representation.append('tagSet=%r' % (self.tagSet,)) 454 if self.subtypeSpec is not self.__class__.subtypeSpec: 455 representation.append('subtypeSpec=%r' % (self.subtypeSpec,)) 456 representation = '%s(%s)' % (self.__class__.__name__, ', '.join(representation)) 457 if self._componentValues: 458 for idx, component in enumerate(self._componentValues): 459 if component is None or component is noValue: 460 continue 461 representation += '.setComponentByPosition(%d, %s)' % (idx, repr(component)) 462 return representation 463 464 def __eq__(self, other): 465 return self is other and True or self._componentValues == other 466 467 def __ne__(self, other): 468 return self._componentValues != other 469 470 def __lt__(self, other): 471 return self._componentValues < other 472 473 def __le__(self, other): 474 return self._componentValues <= other 475 476 def __gt__(self, other): 477 return self._componentValues > other 478 479 def __ge__(self, other): 480 return self._componentValues >= other 481 482 if sys.version_info[0] <= 2: 483 def __nonzero__(self): 484 return self._componentValues and True or False 485 else: 486 def __bool__(self): 487 return self._componentValues and True or False 488 489 def _cloneComponentValues(self, myClone, cloneValueFlag): 490 pass 491 492 def clone(self, **kwargs): 493 """Create a copy of a |ASN.1| type or object. 494 495 Any parameters to the *clone()* method will replace corresponding 496 properties of the |ASN.1| object. 497 498 Parameters 499 ---------- 500 tagSet: :py:class:`~pyasn1.type.tag.TagSet` 501 Object representing non-default ASN.1 tag(s) 502 503 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 504 Object representing non-default ASN.1 subtype constraint(s) 505 506 sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 507 Object representing non-default ASN.1 size constraint(s) 508 509 Returns 510 ------- 511 : 512 new instance of |ASN.1| type/value 513 514 """ 515 cloneValueFlag = kwargs.pop('cloneValueFlag', False) 516 517 initilaizers = self.readOnly.copy() 518 initilaizers.update(kwargs) 519 520 clone = self.__class__(**initilaizers) 521 522 if cloneValueFlag: 523 self._cloneComponentValues(clone, cloneValueFlag) 524 525 return clone 526 527 def subtype(self, **kwargs): 528 """Create a copy of a |ASN.1| type or object. 529 530 Any parameters to the *subtype()* method will be added to the corresponding 531 properties of the |ASN.1| object. 532 533 Parameters 534 ---------- 535 tagSet: :py:class:`~pyasn1.type.tag.TagSet` 536 Object representing non-default ASN.1 tag(s) 537 538 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 539 Object representing non-default ASN.1 subtype constraint(s) 540 541 sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` 542 Object representing non-default ASN.1 size constraint(s) 543 544 Returns 545 ------- 546 : 547 new instance of |ASN.1| type/value 548 549 """ 550 551 initializers = self.readOnly.copy() 552 553 cloneValueFlag = kwargs.pop('cloneValueFlag', False) 554 555 implicitTag = kwargs.pop('implicitTag', None) 556 if implicitTag is not None: 557 initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag) 558 559 explicitTag = kwargs.pop('explicitTag', None) 560 if explicitTag is not None: 561 initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag) 562 563 for arg, option in kwargs.items(): 564 initializers[arg] += option 565 566 clone = self.__class__(**initializers) 567 568 if cloneValueFlag: 569 self._cloneComponentValues(clone, cloneValueFlag) 570 571 return clone 572 573 def verifySizeSpec(self): 574 self.sizeSpec(self) 575 576 def getComponentByPosition(self, idx): 577 raise error.PyAsn1Error('Method not implemented') 578 579 def setComponentByPosition(self, idx, value, verifyConstraints=True): 580 raise error.PyAsn1Error('Method not implemented') 581 582 def setComponents(self, *args, **kwargs): 583 for idx, value in enumerate(args): 584 self[idx] = value 585 for k in kwargs: 586 self[k] = kwargs[k] 587 return self 588 589 def __len__(self): 590 return len(self._componentValues) 591 592 def clear(self): 593 self._componentValues = [] 594 595 # backward compatibility 596 597 def setDefaultComponents(self): 598 pass 599 600 def getComponentType(self): 601 return self.componentType 602