1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
<html>
<title>
Tagging in PyASN1
</title>
<head>
</head>
<body>
<center>
<table width=60%>
<tr>
<td>
<a name="1.2"></a>
<h4>
1.2 Tagging in PyASN1
</h4>
<p>
In order to continue with the Constructed ASN.1 types, we will first have
to introduce the concept of tagging (and its pyasn1 implementation), as
some of the Constructed types rely upon the tagging feature.
</p>
<p>
When a value is coming into an ASN.1-based system (received from a network
or read from some storage), the receiving entity has to determine the
type of the value to interpret and verify it accordingly.
</p>
<p>
Historically, the first data serialization protocol introduced in
ASN.1 was BER (Basic Encoding Rules). According to BER, any serialized
value is packed into a triplet of (Type, Length, Value) where Type is a
code that identifies the value (which is called <i>tag</i> in ASN.1),
length is the number of bytes occupied by the value in its serialized form
and value is ASN.1 value in a form suitable for serial transmission or storage.
</p>
<p>
For that reason almost every ASN.1 type has a tag (which is actually a
BER type) associated with it by default.
</p>
<p>
An ASN.1 tag could be viewed as a tuple of three numbers:
(Class, Format, Number). While Number identifies a tag, Class component
is used to create scopes for Numbers. Four scopes are currently defined:
UNIVERSAL, context-specific, APPLICATION and PRIVATE. The Format component
is actually a one-bit flag - zero for tags associated with scalar types,
and one for constructed types (will be discussed later on).
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
MyIntegerType ::= [12] INTEGER
MyOctetString ::= [APPLICATION 0] OCTET STRING
</pre>
</td></tr></table>
<p>
In pyasn1, tags are implemented as immutable, tuple-like objects:
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
>>> from pyasn1.type import tag
>>> myTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
>>> myTag
Tag(tagClass=128, tagFormat=0, tagId=10)
>>> tuple(myTag)
(128, 0, 10)
>>> myTag[2]
10
>>> myTag == tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 10)
False
>>>
</pre>
</td></tr></table>
<p>
Default tag, associated with any ASN.1 type, could be extended or replaced
to make new type distinguishable from its ancestor. The standard provides
two modes of tag mangling - IMPLICIT and EXPLICIT.
</p>
<p>
EXPLICIT mode works by appending new tag to the existing ones thus creating
an ordered set of tags. This set will be considered as a whole for type
identification and encoding purposes. Important property of EXPLICIT tagging
mode is that it preserves base type information in encoding what makes it
possible to completely recover type information from encoding.
</p>
<p>
When tagging in IMPLICIT mode, the outermost existing tag is dropped and
replaced with a new one.
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
MyIntegerType ::= [12] IMPLICIT INTEGER
MyOctetString ::= [APPLICATION 0] EXPLICIT OCTET STRING
</pre>
</td></tr></table>
<p>
To model both modes of tagging, a specialized container TagSet object (holding
zero, one or more Tag objects) is used in pyasn1.
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
>>> from pyasn1.type import tag
>>> tagSet = tag.TagSet(
... # base tag
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10),
... # effective tag
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
... )
>>> tagSet
TagSet(Tag(tagClass=128, tagFormat=0, tagId=10))
>>> tagSet.getBaseTag()
Tag(tagClass=128, tagFormat=0, tagId=10)
>>> tagSet = tagSet.tagExplicitly(
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20)
... )
>>> tagSet
TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
Tag(tagClass=128, tagFormat=32, tagId=20))
>>> tagSet = tagSet.tagExplicitly(
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 30)
... )
>>> tagSet
TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
Tag(tagClass=128, tagFormat=32, tagId=20),
Tag(tagClass=128, tagFormat=32, tagId=30))
>>> tagSet = tagSet.tagImplicitly(
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
... )
>>> tagSet
TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
Tag(tagClass=128, tagFormat=32, tagId=20),
Tag(tagClass=128, tagFormat=32, tagId=40))
>>>
</pre>
</td></tr></table>
<p>
As a side note: the "base tag" concept (accessible through the getBaseTag()
method) is specific to pyasn1 -- the base tag is used to identify the original
ASN.1 type of an object in question. Base tag is never occurs in encoding
and is mostly used internally by pyasn1 for choosing type-specific data
processing algorithms. The "effective tag" is the one that always appears in
encoding and is used on tagSets comparation.
</p>
<p>
Any two TagSet objects could be compared to see if one is a derivative
of the other. Figuring this out is also useful in cases when a type-specific
data processing algorithms are to be chosen.
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
>>> from pyasn1.type import tag
>>> tagSet1 = tag.TagSet(
... # base tag
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
... # effective tag
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
... )
>>> tagSet2 = tagSet1.tagExplicitly(
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20)
... )
>>> tagSet1.isSuperTagSetOf(tagSet2)
True
>>> tagSet2.isSuperTagSetOf(tagSet1)
False
>>>
</pre>
</td></tr></table>
<p>
We will complete this discussion on tagging with a real-world example. The
following ASN.1 tagged type:
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
MyIntegerType ::= [12] EXPLICIT INTEGER
</pre>
</td></tr></table>
<p>
could be expressed in pyasn1 like this:
</p>
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
<pre>
>>> from pyasn1.type import univ, tag
>>> class MyIntegerType(univ.Integer):
... tagSet = univ.Integer.tagSet.tagExplicitly(
... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 12)
... )
>>> myInteger = MyIntegerType(12345)
>>> myInteger.getTagSet()
TagSet(Tag(tagClass=0, tagFormat=0, tagId=2),
Tag(tagClass=128, tagFormat=32, tagId=12))
>>>
</pre>
</td></tr></table>
<p>
Referring to the above code, the tagSet class attribute is a property of any
pyasn1 type object that assigns default tagSet to a pyasn1 value object. This
default tagSet specification can be ignored and effectively replaced by some
other tagSet value passed on object instantiation.
</p>
<p>
It's important to understand that the tag set property of pyasn1 type/value
object can never be modifed in place. In other words, a pyasn1 type/value
object can never change its tags. The only way is to create a new pyasn1
type/value object and associate different tag set with it.
</p>
<hr>
</td>
</tr>
</table>
</center>
</body>
</html>
|