File: DecodingASN1.md

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (304 lines) | stat: -rw-r--r-- 13,220 bytes parent folder | download
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# Encoding and Decoding DER

Serialize and deserialize objects from DER format.

## Overview

When working with your own ASN.1 types, the biggest problem you'll have is writing the code to serialize and deserialize them from DER format.
This module does not provide an ASN.1 compiler which can automatically synthesise this code for you. Instead, you have to write it yourself.
This is some unfortunate boilerplate, but the good news is that it's straightforward enough that you'll get it done in no time.

By way of a worked example, we can build up a few types in increasing complexity.

## ECDSA Signature

Our starting point is the ECDSA signature type, defined like this:

```
ECDSASignature ::= SEQUENCE {
  r INTEGER,
  s INTEGER }
```

This defines `ECDSASignature` as a `SEQUENCE` of two `INTEGER` values.

In ASN.1, `SEQUENCE` is often used to define a record type. This is much like a Swift `struct`: an ordered series of named fields, often
of different types. That makes a `struct` the natural representation for our `ECDSASignature` type:

```swift
struct ECDSASignature {

}
```

### Storing Fields

Our next problem is to work out the fields we want to store in our type. A natural choice might be `Int`, but some more careful reading will
clue us in to the fact that an ASN.1 `INTEGER` type is of arbitrary size. In the case of ECDSA signatures, these can be very large: 32 bytes
large or more! These won't fit into `Int`.

To address this issue, ``SwiftASN1`` defines two helpers. The first is a protocol, ``ASN1IntegerRepresentable``. This allows users that have
access to a preferred arbitrary-precision integer type to decode directly to that type. The second is a conformance of `ArraySlice<UInt8>` to
``ASN1IntegerRepresentable``. This conformance allows us to just store the raw big-endian bytes of the INTEGER fields without needing to
define an arbitrary-precision integer ourselves. As we mostly care about these numbers as arbitrary byte sequences, we'll use this
escape hatch.

```swift
struct ECDSASignature {
    var r: ArraySlice<UInt8>

    var s: ArraySlice<UInt8>

    init(r: ArraySlice<UInt8>, s: ArraySlice<UInt8>) {
        self.r = r
        self.s = s
    }
}
```

### Decoding from DER

To decode from DER, we'll need to implement ``DERParseable``. As this is a `SEQUENCE` type, however, we can safely implement
``DERImplicitlyTaggable``. Most types can do so, especially anything that is a `SEQUENCE` type.

To conform we need to implement the protocol requirement ``DERImplicitlyTaggable/init(derEncoded:withIdentifier:)-7e88k``. This function
will pass us a pair of arguments. The first is the ``ASN1Node`` representing the root of the ASN.1 object tree we've been asked to parse
the `ECDSASignature` from. The second is the ``ASN1Identifier`` we expect to have this node to have. In many cases this will be
``ASN1Identifier/sequence``, but we will happily use whatever we're told to.

The first trick is to unwrap the `SEQUENCE`. To do that, we can use ``DER/sequence(_:identifier:_:)``. This checks whether the ``ASN1Node``
we pass to it has the right ``ASN1Identifier`` and that it is a constructed node. Assuming all those checks pass, our builder function
will be invoked with an ``ASN1NodeCollection/Iterator`` that contains the nodes within the `SEQUENCE`.

This initial scaffolding will look like this:

```swift
extension ECDSASignature: DERImplicitlyTaggable {
    init(derEncoded rootNode: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
        self = try DER.sequence(rootNode, identifier: identifier) { nodes in
            // TODO
        }
    }
}
```

Next, we need to decode the constituent objects. This is where the recursive nature of ASN.1 works in our favour. These sub-objects
(in this case of ASN.1 type `INTEGER`) are themselves described as trees of ASN.1 nodes. For `INTEGER` specifically this tree will
only have a root node, but in general all ASN.1 objects can be parsed from a single root node.

Fortunately for us, this is exactly the interface that ``DERParseable`` provides us. So we can safely recurse into the child objects using
the helper method ``DERParseable/init(derEncoded:)-4h4ny``. This handy method will even take care of consuming the node from the iterator for us.
The result is a method body that looks like this:

```swift
init(derEncoded rootNode: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
    self = try DER.sequence(rootNode, identifier: identifier) { nodes in
        let r = try ArraySlice<UInt8>(derEncoded: &nodes)
        let s = try ArraySlice<UInt8>(derEncoded: &nodes)

        return ECDSASignature(r: r, s: s)
    }
}
```

Note that we don't need to confirm the exact length of the ``ASN1NodeCollection/Iterator`` `nodes`. The helper method
``DERParseable/init(derEncoded:)-4h4ny`` will automatically throw an error if there is no node available to decode an object from, and
``DER/sequence(_:identifier:_:)`` will confirm that all the nodes were consumed from the child iterator.

This composition approach scales to more complex types. Any ASN.1 composite object can be implemented using ``DER/sequence(_:identifier:_:)``
and recursive initialization of ever-smaller ASN.1 objects.

### Encoding to DER

Next we need to implement the inverse method, ``DERImplicitlyTaggable/serialize(into:withIdentifier:)``. This method is implemented in much the
same was as our parsing method, but in reverse.

Importantly, here we don't have a `sequence` helper. Instead we use the more general operation, ``DER/Serializer/appendConstructedNode(identifier:_:)``.
This doesn't have any semantic implications: it applies a general purpose ASN.1 constructed node with a given identifier.

```swift
func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
    try coder.appendConstructedNode(identifier: identifier) { coder in
        try coder.serialize(self.r)
        try coder.serialize(self.s)
    }
}
```

Note that we have shadowed our `coder` in this block. This is important: we're passed our original ``DER/Serializer`` `coder` as an `inout`
variable, and ``DER/Serializer/appendConstructedNode(identifier:_:)`` is a `mutating` operation. The closure it takes will receive the same
serializer as an `inout` again, allowing us to subsequently serialize new objects.

We can then recurse down the tree. ``DER/Serializer/serialize(_:)-1gxeg`` is a handy wrapper that will ultimately invoke ``DERSerializable/serialize(into:)`` on
the node in question.

### Final Result

We're done! Ultimately we've ended up with the following type. Don't worry too much about our use of ``DERImplicitlyTaggable/defaultIdentifier`` here: we'll cover it
in [Implicit Tagging](#Implicit-Tagging).

```swift
struct ECDSASignature: DERImplicitlyTaggable {
    static var defaultIdentifier: ASN1Identifier {
        .sequence
    }

    var r: ArraySlice<UInt8>
    var s: ArraySlice<UInt8>

    init(r: ArraySlice<UInt8>, s: ArraySlice<UInt8>) {
        self.r = r
        self.s = s
    }

    init(derEncoded rootNode: ASN1Node, withIdentifier identifier: ASN1Identifier) throws {
        self = try DER.sequence(rootNode, identifier: identifier) { nodes in
            let r = try ArraySlice<UInt8>(derEncoded: &nodes)
            let s = try ArraySlice<UInt8>(derEncoded: &nodes)

            return ECDSASignature(r: r, s: s)
        }
    }

    func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws {
        try coder.appendConstructedNode(identifier: identifier) { coder in
            try coder.serialize(self.r)
            try coder.serialize(self.s)
        }
    }
}
```

## Alternative Tagging

ASN.1 supports overriding tags in two ways: implicit and explicit tagging. ``SwiftASN1`` supports both.

It's often difficult to work out from an isolated snippet of ASN.1 definition whether a custom tag is explicit
or implicit. For clarity, this document will attempt to express on each line whether a tag is explicit or
implicit.

### Implicit Tagging

Implicit tagging replaces the usual tag for an object with a new tag. This makes it impossible to know the type
of the object without having the ASN.1 definition. It can also limit some extensibility.

The principal advantage of implicit tagging is that there is no need to add the extra tag that explicit tagging requires.
This will save at least 2 bytes on each object, but may save more.

An object that can be implicitly tagged should conform to ``DERImplicitlyTaggable``. This will make a number of extra
methods available to users of that type, as well as forcing users to implement
``DERImplicitlyTaggable/init(derEncoded:withIdentifier:)-7e88k`` and ``DERImplicitlyTaggable/serialize(into:withIdentifier:)``.
These two methods allow the caller to tell the type what its implicit tag is. The implementer is required to use that tag
for the top-level object (usually a constructed node).

> Note: A few objects should not conform to ``DERImplicitlyTaggable``. These objects are ones which, if they lost their tag,
> would not be able to correctly decode themselves.
>
> An example of such a type is a CHOICE type. For example:
>
> ```
> CannotBeImplicitlyTagged ::= CHOICE {
>   integerVal    INTEGER
>   boolVal       BOOLEAN
>   stringVal     UTF8String }
> ```
>
> As CHOICE types encode as whatever of the choice values is chosen, the tag is necessary to know which of these cases to decode.
> As a result, we cannot implicitly tag a CHOICE object: it'll be impossible to decode. This is true even if the various CHOICE
> fields are implicitly or explicitly tagged with custom tags: that tag will be obliterated if the CHOICE itself is implicitly
> tagged.

Users can then decode an implicitly tagged object using one of the helper methods. For example, consider
this simple ASN.1 type:

```
ContainsImplicitTag ::= SEQUENCE {
  value    [0]  IMPLICIT INTEGER }
```

We can decode it like so:

```swift
init(derEncoded rootNode: ASN1Node) throws {
    self = try DER.sequence(rootNode, identifier: .sequence) { nodes in
        let value = try Int64(derEncoded: &nodes, withIdentifier: .init(tagWithNumber: 0, tagClass: .contextSpecific))

        return ContainsImplicitTag(value: value)
    }
}
```

We can also encode it similarly:

```swift
func serialize(into coder: inout DER.Serializer) throws {
    try coder.appendConstructedNode(identifier: .sequence) { coder in
        try self.value.serialize(into: &coder, withIdentifier: .init(tagWithNumber: 0, tagClass: .contextSpecific))
    }
}
```

Note that we need to construct the ``ASN1Identifier`` directly in this case. This is common whenever we have an implicit
tag that needs to be passed to an object that is unconditionally present. It's often a wise idea to store this tag in a `static let`
for ease of access.

There are a few helper functions for circumstances when an object has an implicit tag and is also either `OPTIONAL` or `DEFAULT`. For parsing,
those are:

- ``DER/optionalImplicitlyTagged(_:tag:)``
- ``DER/decodeDefault(_:identifier:defaultValue:)``
- ``DER/decodeDefault(_:identifier:defaultValue:_:)``

For serializing, those are:

- ``DER/Serializer/serializeOptionalImplicitlyTagged(_:)``
- ``DER/Serializer/serializeOptionalImplicitlyTagged(_:withIdentifier:)``

Note that in a few cases you will see an object that is `OPTIONAL` or `DEFAULT` without a note of a tag. In this case, the implicit tag is
identical to the normal tag for this object.

### Explicit Tagging

Explicit tagging wraps an object in a new constructed object whose tag is equal to the explicit tag defined in the ASN.1 definition. This allows
the explicitly tagged object to still retain its original tag, helping the encoding to remain self-describing. Additionally, unlike with implicit
tagging, all nodes can be explicitly tagged, as the underlying representation of that node is not affected.

As a worked example of explicit tagging, consider the following object:

```
ContainsExplicitTag ::= SEQUENCE {
  value    [0]  EXPLICIT INTEGER }
```

We can decode it like so:

```swift
init(derEncoded rootNode: ASN1Node) throws {
    self = try DER.sequence(rootNode, identifier: .sequence) { nodes in
        let value = try ASN1.explicitlyTagged(&nodes, tagNumber: 0, tagClass: .contextSpecific) {
            try Int64(derEncoded: $0)
        }

        return ContainsExplicitTag(value: value)
    }
}
```

We can also encode it similarly:

```swift
func serialize(into coder: inout DER.Serializer) throws {
    try coder.appendConstructedNode(identifier: .sequence) { coder in
        try coder.serialize(self.value, explicitlyTaggedWithTagNumber: 0, tagClass: .contextSpecific)
    }
}
```

As with the implicitly tagged case, most explicit tags are used in cases where the object is also either `OPTIONAL` or `DEFAULT`. We have a number
helper methods for those. For parsing:

- ``DER/optionalExplicitlyTagged(_:tagNumber:tagClass:_:)``
- ``DER/decodeDefaultExplicitlyTagged(_:tagNumber:tagClass:defaultValue:)``
- ``DER/decodeDefaultExplicitlyTagged(_:tagNumber:tagClass:defaultValue:_:)``

For serializing, users are expected to implement the DEFAULT/OPTIONAL logic themselves.