This specification describes how to make der_walk()
traverse the path in DER
binaries that you intend it to take.
The walk path is described as a sequence of values that ends in DER_WALK_END
:
#include <quick-der/api.h>
derwalk path_demo [] = {
...,
DER_WALK_END
}
which is then used to move a dercursor crs
with
int prsok;
prsok = der_walk (&crs, path_demo);
The output is -1 for hard errors or 0 for success. If it fails to parse the path
at some point, the return value is a positive integer, indicating how much of
path_demo
was left unprocessed before DER_WALK_END
.
The crs
value is updated by this call to point to the end of the path_demo
walk.
There are two basic actions that der_walk()
takes at each position along the
path; it may either enter or skip a DER element. This is defined in the path
with DER_WALK_ENTER
and DER_WALK_SKIP
, respectively, as in
derwalk path_demo [] = {
DER_WALK_ENTER | ...,
DER_WALK_SKIP | ...,
...,
DER_WALK_END
}
The tag found in the DER code must be matched, or otherwise a validation error
is raised (and a positive integer returned from der_walk()
to indicate where
the problem was encountered).
Tags are quite simply matched by mentioning them after the enter-or-skip choice, as in
derwalk path_demo [] = {
DER_WALK_ENTER | DER_TAG_SEQUENCE,
DER_WALK_SKIP | DER_TAG_CONTEXT (0),
DER_WALK_ENTER | DER_TAG_OCTETSTRING,
DER_WALK_END
}
The two statements shown could be used to get to the OCTET STRING
in
demoStruct ::= SEQUENCE {
demoCounter [0] INTEGER,
demoName OCTET STRING
}
This relates to the DER sequence for this structure; let's say the INTEGER
value is 7
and the OCTET STRING
is Quick DER
, then the encoding would be
30 16 -- tag SEQUENCE, length 16
a0 03 -- tag a0 for [0], length 3
02 01 07 -- tag INTEGER, length 1, value 7
04 09 51 75 69 63 6b 20 44 45 52 -- tag 4, length 9, "Quick DER"
From the start of this structure, we need to:
- Enter the
SEQUENCE
- Skip the
[0]
rather then entering it - Enter the
OCTET STRING
- Stop processing
This is precisely what the path walk describes. Although some understanding of the mapping to DER is helpful, you can generally derive the path to walk directly from the ASN.1 structure.
When done, der_walk()
returns the pointer to the string "Quick DER" with a
length of 9, and you can continue to process it:
printf ("Found \"%.*s\"\n", crs.derlen, crs.derptr);
There is a possibility in ASN.1 to specify an element as OPTIONAL
, perhaps
even having a DEFAULT
value (which is ignored by Quick DER). To mark an
entry as optional, precede it with DER_WALK_OPTIONAL
.
Choices are barely interesting during a walk; in fact, the only purpose they
serve is as something to skip over (since we obviously have no idea how to get
into a structure if we don't know yet what that structure is like). So,
specify DER_WALK_SKIP | DER_WALK_CHOICE
to skip an arbitrary element;
there will be no validation of that particular tag.
The forms ANY
and ANY DEFINED BY
receive the same treatment as CHOICE
,
but can be declare with a separate symbol DER_WALK_ANY
.
Also have a look at the individual steps that can be taken with
der_enter()
and der_skip()
. And take a look at
der_iterate_first()
and der_iterate_next()
if you need iterators.
Where der_walk()
is ideally suited to retrieve a single bit of information
from the repository, the der_unpack()
routine can unpack a complete DER
structure (only deferring dynamically-sized parts to later calls). The latter
also has a reverse routine der_pack()
. You will want to read the
PACK SYNTAX for the walking paths used with those routines.