1 <html> 2 <title> 3 PyASN1 Constructed types 4 </title> 5 <head> 6 </head> 7 <body> 8 <center> 9 <table width=60%> 10 <tr> 11 <td> 12 13 <h4> 14 1.3 PyASN1 Constructed types 15 </h4> 16 17 <p> 18 Besides scalar types, ASN.1 specifies so-called constructed ones - these 19 are capable of holding one or more values of other types, both scalar 20 and constructed. 21 </p> 22 23 <p> 24 In pyasn1 implementation, constructed ASN.1 types behave like 25 Python sequences, and also support additional component addressing methods, 26 specific to particular constructed type. 27 </p> 28 29 <a name="1.3.1"></a> 30 <h4> 31 1.3.1 Sequence and Set types 32 </h4> 33 34 <p> 35 The Sequence and Set types have many similar properties: 36 </p> 37 <ul> 38 <li>they can hold any number of inner components of different types 39 <li>every component has a human-friendly identifier 40 <li>any component can have a default value 41 <li>some components can be absent. 42 </ul> 43 44 <p> 45 However, Sequence type guarantees the ordering of Sequence value components 46 to match their declaration order. By contrast, components of the 47 Set type can be ordered to best suite application's needs. 48 <p> 49 50 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 51 <pre> 52 Record ::= SEQUENCE { 53 id INTEGER, 54 room [0] INTEGER OPTIONAL, 55 house [1] INTEGER DEFAULT 0 56 } 57 </pre> 58 </td></tr></table> 59 60 <p> 61 Up to this moment, the only method we used for creating new pyasn1 types 62 is Python sub-classing. With this method, a new, named Python class is created 63 what mimics type derivation in ASN.1 grammar. However, ASN.1 also allows for 64 defining anonymous subtypes (room and house components in the example above). 65 To support anonymous subtyping in pyasn1, a cloning operation on an existing 66 pyasn1 type object can be invoked what creates a new instance of original 67 object with possibly modified properties. 68 </p> 69 70 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 71 <pre> 72 >>> from pyasn1.type import univ, namedtype, tag 73 >>> class Record(univ.Sequence): 74 ... componentType = namedtype.NamedTypes( 75 ... namedtype.NamedType('id', univ.Integer()), 76 ... namedtype.OptionalNamedType( 77 ... 'room', 78 ... univ.Integer().subtype( 79 ... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0) 80 ... ) 81 ... ), 82 ... namedtype.DefaultedNamedType( 83 ... 'house', 84 ... univ.Integer(0).subtype( 85 ... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1) 86 ... ) 87 ... ) 88 ... ) 89 >>> 90 </pre> 91 </td></tr></table> 92 93 <p> 94 All pyasn1 constructed type classes have a class attribute <b>componentType</b> 95 that represent default type specification. Its value is a NamedTypes object. 96 </p> 97 98 <p> 99 The NamedTypes class instance holds a sequence of NameType, OptionalNamedType 100 or DefaultedNamedType objects which, in turn, refer to pyasn1 type objects that 101 represent inner SEQUENCE components specification. 102 </p> 103 104 <p> 105 Finally, invocation of a subtype() method of pyasn1 type objects in the code 106 above returns an implicitly tagged copy of original object. 107 </p> 108 109 <p> 110 Once a SEQUENCE or SET type is decleared with pyasn1, it can be instantiated 111 and initialized (continuing the above code): 112 </p> 113 114 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 115 <pre> 116 >>> record = Record() 117 >>> record.setComponentByName('id', 123) 118 >>> print(record.prettyPrint()) 119 Record: 120 id=123 121 >>> 122 >>> record.setComponentByPosition(1, 321) 123 >>> print(record.prettyPrint()) 124 Record: 125 id=123 126 room=321 127 >>> 128 >>> record.setDefaultComponents() 129 >>> print(record.prettyPrint()) 130 Record: 131 id=123 132 room=321 133 house=0 134 </pre> 135 </td></tr></table> 136 137 <p> 138 Inner components of pyasn1 Sequence/Set objects could be accessed using the 139 following methods: 140 </p> 141 142 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 143 <pre> 144 >>> record.getComponentByName('id') 145 Integer(123) 146 >>> record.getComponentByPosition(1) 147 Integer(321) 148 >>> record[2] 149 Integer(0) 150 >>> for idx in range(len(record)): 151 ... print(record.getNameByPosition(idx), record.getComponentByPosition(idx)) 152 id 123 153 room 321 154 house 0 155 >>> 156 </pre> 157 </td></tr></table> 158 159 <p> 160 The Set type share all the properties of Sequence type, and additionally 161 support by-tag component addressing (as all Set components have distinct 162 types). 163 </p> 164 165 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 166 <pre> 167 >>> from pyasn1.type import univ, namedtype, tag 168 >>> class Gamer(univ.Set): 169 ... componentType = namedtype.NamedTypes( 170 ... namedtype.NamedType('score', univ.Integer()), 171 ... namedtype.NamedType('player', univ.OctetString()), 172 ... namedtype.NamedType('id', univ.ObjectIdentifier()) 173 ... ) 174 >>> gamer = Gamer() 175 >>> gamer.setComponentByType(univ.Integer().getTagSet(), 121343) 176 >>> gamer.setComponentByType(univ.OctetString().getTagSet(), 'Pascal') 177 >>> gamer.setComponentByType(univ.ObjectIdentifier().getTagSet(), (1,3,7,2)) 178 >>> print(gamer.prettyPrint()) 179 Gamer: 180 score=121343 181 player=b'Pascal' 182 id=1.3.7.2 183 >>> 184 </pre> 185 </td></tr></table> 186 187 <a name="1.3.2"></a> 188 <h4> 189 1.3.2 SequenceOf and SetOf types 190 </h4> 191 192 <p> 193 Both, SequenceOf and SetOf types resemble an unlimited size list of components. 194 All the components must be of the same type. 195 </p> 196 197 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 198 <pre> 199 Progression ::= SEQUENCE OF INTEGER 200 201 arithmeticProgression Progression ::= { 1, 3, 5, 7 } 202 </pre> 203 </td></tr></table> 204 205 <p> 206 SequenceOf and SetOf types are expressed by the very similar pyasn1 type 207 objects. Their components can only be addressed by position and they 208 both have a property of automatic resize. 209 </p> 210 211 <p> 212 To specify inner component type, the <b>componentType</b> class attribute 213 should refer to another pyasn1 type object. 214 </p> 215 216 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 217 <pre> 218 >>> from pyasn1.type import univ 219 >>> class Progression(univ.SequenceOf): 220 ... componentType = univ.Integer() 221 >>> arithmeticProgression = Progression() 222 >>> arithmeticProgression.setComponentByPosition(1, 111) 223 >>> print(arithmeticProgression.prettyPrint()) 224 Progression: 225 -empty- 111 226 >>> arithmeticProgression.setComponentByPosition(0, 100) 227 >>> print(arithmeticProgression.prettyPrint()) 228 Progression: 229 100 111 230 >>> 231 >>> for idx in range(len(arithmeticProgression)): 232 ... arithmeticProgression.getComponentByPosition(idx) 233 Integer(100) 234 Integer(111) 235 >>> 236 </pre> 237 </td></tr></table> 238 239 <p> 240 Any scalar or constructed pyasn1 type object can serve as an inner component. 241 Missing components are prohibited in SequenceOf/SetOf value objects. 242 </p> 243 244 <a name="1.3.3"></a> 245 <h4> 246 1.3.3 Choice type 247 </h4> 248 249 <p> 250 Values of ASN.1 CHOICE type can contain only a single value of a type from a 251 list of possible alternatives. Alternatives must be ASN.1 types with 252 distinct tags for the whole structure to remain unambiguous. Unlike most 253 other types, CHOICE is an untagged one, e.g. it has no base tag of its own. 254 </p> 255 256 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 257 <pre> 258 CodeOrMessage ::= CHOICE { 259 code INTEGER, 260 message OCTET STRING 261 } 262 </pre> 263 </td></tr></table> 264 265 <p> 266 In pyasn1 implementation, Choice object behaves like Set but accepts only 267 a single inner component at a time. It also offers a few additional methods 268 specific to its behaviour. 269 </p> 270 271 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 272 <pre> 273 >>> from pyasn1.type import univ, namedtype 274 >>> class CodeOrMessage(univ.Choice): 275 ... componentType = namedtype.NamedTypes( 276 ... namedtype.NamedType('code', univ.Integer()), 277 ... namedtype.NamedType('message', univ.OctetString()) 278 ... ) 279 >>> 280 >>> codeOrMessage = CodeOrMessage() 281 >>> print(codeOrMessage.prettyPrint()) 282 CodeOrMessage: 283 >>> codeOrMessage.setComponentByName('code', 123) 284 >>> print(codeOrMessage.prettyPrint()) 285 CodeOrMessage: 286 code=123 287 >>> codeOrMessage.setComponentByName('message', 'my string value') 288 >>> print(codeOrMessage.prettyPrint()) 289 CodeOrMessage: 290 message=b'my string value' 291 >>> 292 </pre> 293 </td></tr></table> 294 295 <p> 296 Since there could be only a single inner component value in the pyasn1 Choice 297 value object, either of the following methods could be used for fetching it 298 (continuing previous code): 299 </p> 300 301 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 302 <pre> 303 >>> codeOrMessage.getName() 304 'message' 305 >>> codeOrMessage.getComponent() 306 OctetString(b'my string value') 307 >>> 308 </pre> 309 </td></tr></table> 310 311 <a name="1.3.4"></a> 312 <h4> 313 1.3.4 Any type 314 </h4> 315 316 <p> 317 The ASN.1 ANY type is a kind of wildcard or placeholder that matches 318 any other type without knowing it in advance. Like CHOICE type, ANY 319 has no base tag. 320 </p> 321 322 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 323 <pre> 324 Error ::= SEQUENCE { 325 code INTEGER, 326 parameter ANY DEFINED BY code 327 } 328 </pre> 329 </td></tr></table> 330 331 <p> 332 The ANY type is frequently used in specifications, where exact type is not 333 yet agreed upon between communicating parties or the number of possible 334 alternatives of a type is infinite. 335 Sometimes an auxiliary selector is kept around to help parties indicate 336 the kind of ANY payload in effect ("code" in the example above). 337 </p> 338 339 <p> 340 Values of the ANY type contain serialized ASN.1 value(s) in form of 341 an octet string. Therefore pyasn1 Any value object share the properties of 342 pyasn1 OctetString object. 343 </p> 344 345 <table bgcolor="lightgray" border=0 width=100%><TR><TD> 346 <pre> 347 >>> from pyasn1.type import univ 348 >>> someValue = univ.Any(b'\x02\x01\x01') 349 >>> someValue 350 Any(b'\x02\x01\x01') 351 >>> str(someValue) 352 '\x02\x01\x01' 353 >>> bytes(someValue) 354 b'\x02\x01\x01' 355 >>> 356 </pre> 357 </td></tr></table> 358 359 <p> 360 Receiving application is supposed to explicitly deserialize the content of Any 361 value object, possibly using auxiliary selector for figuring out its ASN.1 362 type to pick appropriate decoder. 363 </p> 364 365 <p> 366 There will be some more talk and code snippets covering Any type in the codecs 367 chapters that follow. 368 </p> 369 370 <hr> 371 372 </td> 373 </tr> 374 </table> 375 </center> 376 </body> 377 </html> 378