Grammar fix
[pyderasn.git] / doc / examples.rst
1 Examples
2 ========
3
4 .. contents::
5
6 Schema definition
7 -----------------
8
9 Let's try to parse X.509 certificate. We have to define our structures
10 based on ASN.1 schema descriptions.
11
12 .. list-table::
13    :header-rows: 1
14
15    * - ASN.1 specification
16      - pyderasn's code
17    * - ::
18
19             Certificate  ::=  SEQUENCE  {
20                 tbsCertificate       TBSCertificate,
21                 signatureAlgorithm   AlgorithmIdentifier,
22                 signatureValue       BIT STRING  }
23      - ::
24
25             class Certificate(Sequence):
26                 schema = (
27                     ("tbsCertificate", TBSCertificate()),
28                     ("signatureAlgorithm", AlgorithmIdentifier()),
29                     ("signatureValue", BitString()),
30                 )
31    * - ::
32
33             AlgorithmIdentifier  ::=  SEQUENCE  {
34                 algorithm    OBJECT IDENTIFIER,
35                 parameters   ANY DEFINED BY algorithm OPTIONAL  }
36      - ::
37
38             class AlgorithmIdentifier(Sequence):
39                 schema = (
40                     ("algorithm", ObjectIdentifier()),
41                     ("parameters", Any(optional=True)),
42                 )
43    * - ::
44
45             TBSCertificate  ::=  SEQUENCE  {
46                 version         [0]  EXPLICIT Version DEFAULT v1,
47                 serialNumber         CertificateSerialNumber,
48                 signature            AlgorithmIdentifier,
49                 issuer               Name,
50                 validity             Validity,
51                 subject              Name,
52                 subjectPublicKeyInfo SubjectPublicKeyInfo,
53                 issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
54                 subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
55                 extensions      [3]  EXPLICIT Extensions OPTIONAL  }
56      - ::
57
58             class TBSCertificate(Sequence):
59                 schema = (
60                     ("version", Version(expl=tag_ctxc(0), default="v1")),
61                     ("serialNumber", CertificateSerialNumber()),
62                     ("signature", AlgorithmIdentifier()),
63                     ("issuer", Name()),
64                     ("validity", Validity()),
65                     ("subject", Name()),
66                     ("subjectPublicKeyInfo", SubjectPublicKeyInfo()),
67                     ("issuerUniqueID", UniqueIdentifier(impl=tag_ctxp(1), optional=True)),
68                     ("subjectUniqueID", UniqueIdentifier(impl=tag_ctxp(2), optional=True)),
69                     ("extensions", Extensions(expl=tag_ctxc(3), optional=True)),
70                 )
71    * - ::
72
73             Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
74      - ::
75
76             class Version(Integer):
77                 schema = (("v1", 0), ("v2", 1), ("v3", 2))
78    * - ::
79
80             CertificateSerialNumber  ::=  INTEGER
81      - ::
82
83             class CertificateSerialNumber(Integer):
84                 pass
85    * - ::
86
87             Validity ::= SEQUENCE {
88                 notBefore      Time,
89                 notAfter       Time }
90             Time ::= CHOICE {
91                 utcTime        UTCTime,
92                 generalTime    GeneralizedTime }
93      - ::
94
95             class Validity(Sequence):
96                 schema = (
97                     ("notBefore", Time()),
98                     ("notAfter", Time()),
99                 )
100             class Time(Choice):
101                 schema = (
102                     ("utcTime", UTCTime()),
103                     ("generalTime", GeneralizedTime()),
104                 )
105    * - ::
106
107             SubjectPublicKeyInfo  ::=  SEQUENCE  {
108                 algorithm            AlgorithmIdentifier,
109                 subjectPublicKey     BIT STRING  }
110      - ::
111
112             class SubjectPublicKeyInfo(Sequence):
113                 schema = (
114                     ("algorithm", AlgorithmIdentifier()),
115                     ("subjectPublicKey", BitString()),
116                 )
117    * - ::
118
119             UniqueIdentifier  ::=  BIT STRING
120      - ::
121
122             class UniqueIdentifier(BitString):
123                 pass
124    * - ::
125
126             Name ::= CHOICE { rdnSequence  RDNSequence }
127
128             RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
129
130             RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
131
132             AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue }
133
134             AttributeType ::= OBJECT IDENTIFIER
135
136             AttributeValue ::= ANY -- DEFINED BY AttributeType
137      - ::
138
139             class Name(Choice):
140                 schema = (("rdnSequence", RDNSequence()),)
141             class RDNSequence(SequenceOf):
142                 schema = RelativeDistinguishedName()
143             class RelativeDistinguishedName(SetOf):
144                 schema = AttributeTypeAndValue()
145                 bounds = (1, float("+inf"))
146             class AttributeTypeAndValue(Sequence):
147                 schema = (
148                     ("type", AttributeType()),
149                     ("value", AttributeValue()),
150                 )
151             class AttributeType(ObjectIdentifier):
152                 pass
153             class AttributeValue(Any):
154                 pass
155    * - ::
156
157             Extensions ::=  SEQUENCE SIZE (1..MAX) OF Extension
158
159             Extension  ::=  SEQUENCE  {
160                 extnID      OBJECT IDENTIFIER,
161                 critical    BOOLEAN DEFAULT FALSE,
162                 extnValue   OCTET STRING
163                 }
164      - ::
165
166             class Extensions(SequenceOf):
167                 schema = Extension()
168                 bounds = (1, float("+inf"))
169             class Extension(Sequence):
170                 schema = (
171                     ("extnID", ObjectIdentifier()),
172                     ("critical", Boolean(default=False)),
173                     ("extnValue", OctetString()),
174                 )
175
176 We are ready to decode PayPal's certificate from Go `encoding/asn1
177 <https://golang.org/pkg/encoding/asn1/>`__ test suite (assuming that
178 it's DER encoded representation is already in ``raw`` variable)::
179
180     >>> crt, tail = Certificate().decode(raw)
181     >>> crt
182     Certificate SEQUENCE[TBSCertificate SEQUENCE[[0] EXPLICIT Version
183         INTEGER v3 OPTIONAL, CertificateSerialNumber INTEGER 61595,
184         AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.840.113549.1.1.5...
185
186 Pretty printing
187 ---------------
188
189 There is huge output. Let's pretty print it::
190
191     >>> print(pprint(crt))
192         0   [1,3,1604] Certificate SEQUENCE
193         4   [1,3,1453]  . tbsCertificate: TBSCertificate SEQUENCE
194        10-2 [1,1,   1]  . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
195        13   [1,1,   3]  . . serialNumber: CertificateSerialNumber INTEGER 61595
196        18   [1,1,  13]  . . signature: AlgorithmIdentifier SEQUENCE
197        20   [1,1,   9]  . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
198        31   [0,0,   2]  . . . parameters: [UNIV 5] ANY OPTIONAL
199                         . . . . 05:00
200        33   [0,0, 278]  . . issuer: Name CHOICE rdnSequence
201        33   [1,3, 274]  . . . rdnSequence: RDNSequence SEQUENCE OF
202        37   [1,1,  11]  . . . . 0: RelativeDistinguishedName SET OF
203        39   [1,1,   9]  . . . . . 0: AttributeTypeAndValue SEQUENCE
204        41   [1,1,   3]  . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
205        46   [0,0,   4]  . . . . . . value: [UNIV 19] AttributeValue ANY
206                         . . . . . . . 13:02:45:53
207     [...]
208      1461   [1,1,  13]  . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
209      1463   [1,1,   9]  . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
210      1474   [0,0,   2]  . . parameters: [UNIV 5] ANY OPTIONAL
211                         . . . 05:00
212      1476   [1,2, 129]  . signatureValue: BIT STRING 1024 bits
213                         . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
214                         . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
215      [...]
216
217     Trailing data: 0a
218
219 Let's parse that output, human::
220
221        10-2 [1,1,   1]  . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
222        ^  ^  ^ ^    ^   ^   ^        ^            ^       ^       ^  ^
223        0  1  2 3    4   5   6        7            8       9       10 11
224
225 ::
226
227        20   [1,1,   9]  . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
228        ^     ^ ^    ^   ^     ^          ^                 ^
229        0     2 3    4   5     6          9                 10
230
231 ::
232
233        33   [0,0, 278]  . . issuer: Name CHOICE rdnSequence
234        ^     ^ ^    ^   ^   ^       ^    ^      ^
235        0     2 3    4   5   6       8    9      10
236
237 :0:
238  Offset of the object, where its DER encoding begins.
239  Pay attention that it does **not** include explicit tag.
240 :1:
241  If explicit tag exists, then this is its length (tag + encoded length).
242 :2:
243  Length of object's tag. For example CHOICE does not have its own tag,
244  so it is zero.
245 :3:
246  Length of encoded length.
247 :4:
248  Length of encoded value.
249 :5:
250  Visual indentation to show the depth of object in the hierarchy.
251 :6:
252  Object's name inside SEQUENCE/CHOICE.
253 :7:
254  If either IMPLICIT or EXPLICIT tag is set, then it will be shown
255  here. "IMPLICIT" is omitted.
256 :8:
257  Object's class name, if set. Omitted if it is just an ordinary simple
258  value (like with ``algorithm`` in example above).
259 :9:
260  Object's ASN.1 type.
261 :10:
262  Object's value, if set. Can consist of multiple words (like OCTET/BIT
263  STRINGs above). We see ``v3`` value in Version, because it is named.
264  ``rdnSequence`` is the choice of CHOICE type.
265 :11:
266  Possible other flags like OPTIONAL and DEFAULT, if value equals to the
267  default one, specified in the schema.
268
269 As command line utility
270 -----------------------
271
272 You can decode DER files using command line abilities and get the same
273 picture as above by executing::
274
275     % python -mpyderasn --schema tests.test_crts:Certificate path/to/file
276
277 If there is no schema for you file, then you can try parsing it without,
278 but of course IMPLICIT tags will often make it impossible. But result is
279 good enough for the certificate above::
280
281     % python -mpyderasn path/to/file
282         0   [1,3,1604]  . >: SEQUENCE OF
283         4   [1,3,1453]  . . >: SEQUENCE OF
284         8   [0,0,   5]  . . . . >: [0] ANY
285                         . . . . . A0:03:02:01:02
286        13   [1,1,   3]  . . . . >: INTEGER 61595
287        18   [1,1,  13]  . . . . >: SEQUENCE OF
288        20   [1,1,   9]  . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
289        31   [1,1,   0]  . . . . . . >: NULL
290        33   [1,3, 274]  . . . . >: SEQUENCE OF
291        37   [1,1,  11]  . . . . . . >: SET OF
292        39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
293        41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
294        46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
295     [...]
296      1409   [1,1,  50]  . . . . . . >: SEQUENCE OF
297      1411   [1,1,   8]  . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
298      1421   [1,1,  38]  . . . . . . . . >: OCTET STRING 38 bytes
299                         . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
300                         . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
301                         . . . . . . . . . 61:2E:63:6F:6D:2F
302      1461   [1,1,  13]  . . >: SEQUENCE OF
303      1463   [1,1,   9]  . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
304      1474   [1,1,   0]  . . . . >: NULL
305      1476   [1,2, 129]  . . >: BIT STRING 1024 bits
306                         . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
307                         . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
308     [...]
309
310 If you have got dictionaries with ObjectIdentifiers, like example one
311 from ``tests/test_crts.py``::
312
313     some_oids = {
314         "1.2.840.113549.1.1.1": "id-rsaEncryption",
315         "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
316         [...]
317         "2.5.4.10": "id-at-organizationName",
318         "2.5.4.11": "id-at-organizationalUnitName",
319     }
320
321 then you can pass it to pretty printer to see human readable OIDs::
322
323     % python -mpyderasn --oids tests.test_crts:some_oids path/to/file
324     [...]
325        37   [1,1,  11]  . . . . . . >: SET OF
326        39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
327        41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
328        46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
329        50   [1,1,  18]  . . . . . . >: SET OF
330        52   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
331        54   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
332        59   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
333        70   [1,1,  18]  . . . . . . >: SET OF
334        72   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
335        74   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
336        79   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
337     [...]
338
339 Descriptive errors
340 ------------------
341
342 If you have bad DER, then errors will show you where error occurred::
343
344     % python -mpyderasn --schema tests.test_crts:Certificate path/to/bad/file
345     Traceback (most recent call last):
346     [...]
347     pyderasn.DecodeError: UTCTime (tbsCertificate.validity.notAfter.utcTime) (at 328) invalid UTCTime format
348
349 ::
350
351     % python -mpyderasn path/to/bad/file
352     [...]
353     pyderasn.DecodeError: UTCTime (0.SequenceOf.4.SequenceOf.1.UTCTime) (at 328) invalid UTCTime format
354
355 You can see, so called, decode path inside the structures:
356 ``tbsCertificate`` -> ``validity`` -> ``notAfter`` -> ``utcTime`` and
357 that object at byte 328 is invalid.
358
359 X.509 certificate creation
360 --------------------------
361
362 Let's create some simple self-signed X.509 certificate from the ground::
363
364     tbs = TBSCertificate()
365     tbs["serialNumber"] = CertificateSerialNumber(10143011886257155224)
366
367     sign_algo_id = AlgorithmIdentifier()
368     sign_algo_id["algorithm"] = ObjectIdentifier("1.2.840.113549.1.1.5")
369     sign_algo_id["parameters"] = Any(Null())
370     tbs["signature"] = sign_algo_id
371
372     rdnSeq = RDNSequence()
373     for oid, klass, text in (
374             ("2.5.4.6", PrintableString, "XX"),
375             ("2.5.4.8", PrintableString, "Some-State"),
376             ("2.5.4.7", PrintableString, "City"),
377             ("2.5.4.10", PrintableString, "Internet Widgits Pty Ltd"),
378             ("2.5.4.3", PrintableString, "false.example.com"),
379             ("1.2.840.113549.1.9.1", IA5String, "false@example.com"),
380     ):
381         attr = AttributeTypeAndValue()
382         attr["type"] = AttributeType(oid)
383         attr["value"] = AttributeValue(klass(text))
384         rdn = RelativeDistinguishedName()
385         rdn.append(attr)
386         rdnSeq.append(rdn)
387     issuer = Name()
388     issuer["rdnSequence"] = rdnSeq
389     tbs["issuer"] = issuer
390     tbs["subject"] = issuer
391
392     validity = Validity()
393     validity["notBefore"] = Time(("utcTime", UTCTime(datetime(2009, 10, 8, 0, 25, 53))))
394     validity["notAfter"] = Time(("utcTime", UTCTime(datetime(2010, 10, 8, 0, 25, 53))))
395     tbs["validity"] = validity
396
397     spki = SubjectPublicKeyInfo()
398     spki_algo_id = sign_algo_id.copy()
399     spki_algo_id["algorithm"] = ObjectIdentifier("1.2.840.113549.1.1.1")
400     spki["algorithm"] = spki_algo_id
401     spki["subjectPublicKey"] = BitString(hexdec("".join((
402         "3048024100cdb7639c3278f006aa277f6eaf42902b592d8cbcbe38a1c92ba4695",
403         "a331b1deadeadd8e9a5c27e8c4c2fd0a8889657722a4f2af7589cf2c77045dc8f",
404         "deec357d0203010001",
405     ))))
406     tbs["subjectPublicKeyInfo"] = spki
407
408     crt = Certificate()
409     crt["tbsCertificate"] = tbs
410     crt["signatureAlgorithm"] = sign_algo_id
411     crt["signatureValue"] = BitString(hexdec("".join((
412         "a67b06ec5ece92772ca413cba3ca12568fdc6c7b4511cd40a7f659980402df2b",
413         "998bb9a4a8cbeb34c0f0a78cf8d91ede14a5ed76bf116fe360aafa8821490435",
414     ))))
415     crt.encode()
416
417 And we will get the same certificate used in Go's library tests.