--- /dev/null
+[submodule "tool/hexio"]
+ path = tool/hexio
+ url = https://github.com/vanrein/hexio
--- /dev/null
+# Installing Quick DER
+
+ ./configure
+ make
+ make install
+
+Other build targets include `clean`, `uninstall`.
--- /dev/null
+# Quick DER licensing terms
+
+Copyright (c) 2016, Rick van Rein, OpenFortress.nl
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
--- /dev/null
+# SUBDIRS = lib asn2qder rfc test
+SUBDIRS = lib tool test
+
+all:
+ for d in $(SUBDIRS); do make -C "$$d" all ; done
+
+install:
+ for d in $(SUBDIRS); do make -C "$$d" install ; done
+
+uninstall:
+ for d in $(SUBDIRS); do make -C "$$d" uninstall ; done
+
+clean:
+ for d in $(SUBDIRS); do make -C "$$d" clean ; done
--- /dev/null
+# Quick (and Easy) DER, a Library for parsing ASN.1
+
+![Quick DER logo](quick-der-logo.png)
+
+> *Quick DER, or if you like, "Quick and Easy DER", is a library for handling
+> DER, which is a widely used binary representation of ASN.1 syntax in binary
+> formats. The library describes ASN.1 syntax in a parser table that can be
+> fed into library routines, resulting in pointer/length descriptors for the
+> individual data fragments.*
+
+## Basic Usage
+
+The basic approach of using this library is translating the ASN.1 syntax into
+a parser table. This is done when building your software, as a phase preceding
+the compilation of your Quick DER using program. The translation from ASN.1
+to C code can be done manually and **TODO** will in the future be automated with
+an ASN.1 parser.
+
+The resulting *path walks* are used in calls like `der_unpack()` to transform
+DER into C-style structures, and `der_pack()` to make the opposite transformation.
+The C-style structures are derived from the ASN.1 syntax, and permits access to
+the information with no further need of understanding the depths of processing
+DER.
+
+Since you are handling binary data (rather than character strings), all data
+is described in so-called `dercursor` structures; each containing a pointer
+and a size of the data pointed at.
+
+
+## Basic Code Structure
+
+The output from mapping ASN.1 to a parser table is an include file for the
+C programming language. This defines the various *path walks* that can be
+used to unpack and pack DER data. The output and input of these routines
+takes the form of an array of `dercursor` values.
+
+To simplify use of the unpacked data, there are overlay structures for the
+`dercursor` array. These overlay structures use the same labels that are
+used in the ASN.1 syntax, so it is possible to walk around in the structures.
+
+Some parts of the syntax indicate `OPTIONAL` elements. Such elements result
+in the respective `dercursor` variables to be NULL values; specifically, the
+function `der_isnull()` returns a true value for these elements.
+
+
+## Extra Code Facilities
+
+There are routines `der_iterate_first()` and `der_iterate_next()` routines
+to manually iterate over a DER structure's components. This can be used to
+analyse structures that have not been unpacked (yet). The `der_countelements()`
+routine can be used to predict the number of iterations.
+
+There are also routines to manually walk through packaged DER structures,
+namely `der_enter()` and `der_skip()` to enter into a nested structure and to
+find the next element in a concatenation of such elements.
+
+A much more advanced form of such walks through a DER structure exists in the
+form of `der_walk()`, which is fed another kind of walk, a sequence of enter/skip
+statements with tags that will be validated.
+
+
+## Validation of DER
+
+To validate the structures written in DER, both `der_unpack()` and `der_walk()`
+can be used. Most other routines are coded for flexibility and should not be
+assumed to validate DER in more detail than strictly required.
+
+The `der_unpack()` routine runs through the entire structure, and validates
+the tags it runs by, as well as the complete nesting structure it encounters.
+It is a complete validation of the structures.
+
+The `der_walk()` routine performs *lazy validation*, meaning that it will
+carefully check tags and nesting inasfar as it is needed to get from its
+starting point to its end point. Anything but the paths explored will be
+accepted without question.
+
+### Relation to BER
+
+*This is for ASN.1 experts; others can safely skip this subsection.*
+
+The Quick DER library is designed to process DER, although it will also accept
+some of the more general BER format. If a length or value is written in more
+than the minimal space, the library is still likely to accept it. Note that
+the `BIT STRING` type is somewhat likely to run into overflow problems, so
+there the full restrictiveness of DER is applied.
+
+
+## No Memory Allocation
+
+The entire library has been designed to operate without dynamic memory allocation.
+This means that there will never be a memory leak as a result of using Quick DER.
+
+When DER information is unpacked, it is assumed to be loaded into a memory buffer
+by the calling program, and the `dercursor` structures point to portions of that
+buffer. The data is stored in `dercursor` arrays which the user program may
+overlay with meaningful, ASN.1-labelled structures. In many applications, such
+structures can be allocated on the stack.
+
+Some portions of the data may be dynamically sized, notably the `SEQUENCE OF`
+and `SET OF` structures, which indicate that the structural description following
+it may be repeated in the binary data. Such data portions will be stored and
+not yet unpacked by `der_unpack()`. Based on the stored DER data in a `dercursor`,
+the calling application can choose to use iterators, `der_walk()` and so on to
+avoid actually unpacking it; or it may allocate memory dynamically, and use that
+to repeatedly call `der_unpack()` to find the individual entries.
+
+In short, the Quick DER library *never* needs to perform memory allocation, and
+it provides the calling program with a lot of control to avoid it too. This is
+ideal for embedded applications, but is also beneficial for a secure programming
+style.
+
+
+## Future Plans
+
+There are a few things that this library can use:
+
+ * **Proper tests.** The current `test` directory is far too small; we can take a PKIX certificate apart and re-compose it, so we're definately doing something good, but this is nowhere near thorough testing. If you run into a problem case, then *please* share it so we can solve it and extend our test base.
+ * **ASN.1 compilation.** The compiler for [libtasn1](http://git.savannah.gnu.org/cgit/libtasn1.git/tree/lib) may be an interesting piece of software to modify to create the `derwalk[]` structures that we currently create by hand, together with the overlay structures with syntax-derived labels.
+ * **RFC Library.** A collection of most/all standards that use DER today, in a pre-compiled form that can simply be included as `<quick-der/rfc5280.h>` and then permits processing of the structures defined in it (in this case, PKIX Certificates).
+
+And of course, there are many useful things we may do with this library:
+
+ * **Kerberos in PKIX.** [Certificates wrapping Kerberos Tickets](http://github.com/arpa2/kerberos2pkix) for use with [TLS-KDH](https://tools.ietf.org/html/draft-vanrein-tls-kdh)
+ * **Miniature LDAP services.** These can help you centralise your data storage under own control; for instance, your PGP key ring or your vCard collection are good canidadates for sharing locally.
+
--- /dev/null
+# Quick DER parsing support
+
+> *Quick DER parsing aims to get you started with the parsing of DER (and most
+> BER) encoded ASN.1 data really quickly. It also aims to makes quick parsers,
+> by using shared data structures whenever possible.*
+
+**Note: This is a preview of features to come; for now, it is no promise, but merely a rough sketch of what is to come.**
+
+## Outline of using this library
+
+Working with the quick DER library is really quick to learn.
+
+### Preparing your build environment
+
+The first thing you do, is parse an ASN.1 specification that you may have gotten
+from any source -- for example, from an RFC. You map it to a header file and a
+parser specification file:
+
+ qder_from_asn1 myspec.asn1 myderparser.c myderparser.h
+
+Your source code dealing with DER should read the entire block, and pass it to
+the DER parser. Initially, it would include:
+
+ #include <der/qparse.h>
+ #include "myderparser.h"
+
+and builing should include:
+
+ gcc -o myparser.o myparser.c
+ gcc ... myderparser.o -lqder
+
+You may be lucky, and find your favourite spec precompiled. In that case, you
+can limit yourself to things like:
+
+ #define RFC5280_PREFIX pkix_
+ #include <der/qparse.h>
+ #include <der/rfc5280.h>
+
+and link with simply:
+
+ gcc ... -lqder
+
+
+### Parsing DER structures
+
+Before you get to parse DER-encoded structures that match the ASN.1 syntax,
+you should read the entire data into memory. The parser output will not
+clone bits and pieces of data, but instead point into it with cursors; these
+are little structures with a pointer and a length. Note that this means that
+strings are not NUL-terminated; printing them may be different than what you
+are accustomed to:
+
+ printf ("%s\n", derelem->ptr); // Unbounded string
+ printf ("%.*s\n", derelem->len, derelem->ptr); // Perfect printing
+
+Now, to invoke the parser, you setup a cursor describing the entire content,
+
+ dercursor_t thelot;
+ thelot.derptr = ...pointer-to-data...;
+ thelot.derlen = ...length-of-data...;
+
+then you invoke the parser, providing it with storage space and the
+precompiled structure to follow while parsing:
+
+ struct pkix_Certificate crt;
+ int prsok = der_unpack (&thelot, asn1_pkix_Certificate, &crt, 1);
+
+This will parse the DER-encoded data in `thelot` and store the various fields
+in `crt`, so it becomes available as individual cursor structures such as
+`crt.tbsCertificate.validity.notAfter`. This follows the structure of the
+ASN.1 syntax, and uses field names defined in it, to gradually move into
+the structure. The header file defines those names as part of the
+`asn1_pkix_Certificate`.
+
+Something else that can now be done, is switch behaviour based on the the
+various fields that contain an `OBJECT IDENTIFIER` for that purposes. These
+can usually be treated as binary settings to be compared as binaries. The
+`dercmp()` utility does this by looking at the length as well as binary
+contents of such fields, as in
+
+ if (dercmp (&crt.signatureAlgorith.algorithm, RSA_WITH_SHA1) == 0) {
+ ...the OIDs matched...
+ } else ...other cases...
+
+
+## Iterating over repeating structures
+
+Many structures in ASN.1 are variable in the sizes of primitive data types, but
+have a fixed composition structure. And `OPTIONAL` parts can be parsed and their
+respective structure fields set to NULL when they are absent. This also happens
+to values setup with a `DEFAULT` value in ASN.1 (note that their default value
+is not filled in by the parser).
+
+But some structures are not parsed immediately, because they might have a
+repeated structure, and thus won't fit into a structure; not if the assumption
+is that dynamic allocation should not be done by the parser. For such
+repeating structure, there are two options, namely iterating over their
+contents or allocating memory and having them filled by the parser.
+
+To iterate over a repeated structure, simply use the two routines setup for
+that in quick DER, as in:
+
+ if (der_iterate_first (&crt.tbsCertificate.extensions.packed, &iter)) do {
+ ...treat extension pointed to by iter...
+ } while (der_iterate_next (&crt.tbsCertificate.extensions.packed, &iter));
+
+This requires no dynamic allocation, and simply handles each of the extensions
+in a certificate one by one.
+
+## Allocating space for a repeating structure
+
+The structures that repeat are limited to the ASN.1 constructs
+`SEQUENCE OF` and `SET OF`. When these occur, the parser will not unfold
+the contained structure, but simply store the whole structure. We will
+refer to that as "packed" representation, meaning the binary DER format.
+
+It is possible to replace packed notation by unpacked, by assigning to it
+an array of suitable size to contain the required number of elements,
+and then unfold the repeated structure into it:
+
+ size_t count = der_countelements (&crt.tbsCertificate.extensions.packed);
+ pkix_Extensions *exts = calloc (count, sizeof (pkcix_Extensions));
+ if (exts === NULL) {
+ ...handle error...
+ }
+ prsok = der_unpack (&crt.tbsCertificate.extensions.packed, asn1_pkix_Extension, exts, count);
+
+This will unpack `count` times the structure described by `asn1_pkix_Extension` and place the output in `count` structures in the array `exts`; note that the earlier
+call the `der_unpack` had a parameter `1` in the position of `count`.
+
+When successful, the `der_unpack()` routine replaces the `extensions.packed`
+structure, which is a plain `dercursor_t`, with an unpacked structure
+`unpacked` which has elements `derray` pointing to an array of cursors and
+an element `dercnt` with the number of cursors in that array. When this
+is setup, the `.packed` version of the data is destroyed; the `.packed` and
+`.unpacked` versions are in fact labels of a union.
+
+Note that structures such as `crt` may hold a lot of useful naming, but they
+are just a cleverly constructed overlay form for an array of `dercursor_t` fields,
+which is exactly how `der_unpack` treats them. The ASN.1 parsing instructions
+are matched to the structures so that no data will be sticking out of these
+array-like structures.
+
+## Composing DER output
+
+The composition of DER output uses the same ASN.1 structural descriptions as
+the unpacking process. It is possible to use `.packed` structures, but once
+they are unpacked it becomes necessary to prepare repeating structures for
+repackaging. This uses the `der_prepack()` function:
+
+ int prsok = der_prepack (TODO);
+
+This sets up a third flavour of the repeated structure, namely `.prepacked`.
+In this form, the `derlen` value has been set to the eventual length of
+the to-be-formed DER structure, but the `derray` value still points to the
+array of `dercursor_t` holding the to-be-filled data. This `derlen` field
+can subsequently be used during the future packing process.
+
+TODO: How to distinguish packed, unpacked and prepacked lengths? Tag or size bits?
+
--- /dev/null
+# Quick DER sizes: Sometimes small is bettar
+
+> *This is a decription of the sizes of structures used by Quick DER. As this
+> demonstrates, the library is quite useful for embedded purposes.*
+
+Using Quick DER, an include file is derived from an ASN.1 syntax specification.
+The include files contain fixed parsers for the various structure that can be
+assigned to constant byte arrays, and then applied to a dowloaded sequence of
+bytes in DER format.
+
+Data is generally stored in the form of a dercursor. This is a combination
+of a pointer to DER data with a size of the data from that point. We measure
+memory consumption for data storage in such structures; they point the the
+already-allocated memory that holds to-be-analysed DER input. The length of
+the actual data handled is therefore not included below, since it can vary
+virtually without bounds.
+
+
+## Code size
+
+The following data stems from analysis of the `.text` segment of version TODO
+of Quick DER:
+
+ * 261 bytes for `der_header()`
+ * 079 bytes for `der_iterate_first()`, `der_iterate_next()` and `der_iterate_next()`
+ * 141 bytes for `der_skip()` and `der_enter()`
+ * 761 bytes for `der_unpack()`
+ * 008 bytes for `der_prepack()`
+ * 788 bytes for `der_pack()`
+ * 376 bytes for `der_walk()`
+
+Note that `der_header()` is the only dependency for most other modules.
+Some modules will write into `errno` as well.
+
+The total size of all these modules is 2405 bytes. Note that this includes
+alternatives; it is possible to use just `der_walk()` to walk into structures,
+or one might do it with `der_unpack()`. The choice is there but there is no
+need to mix the styles if you are pressed for space.
+
+
+## Data storage
+
+This is expressed in the number of dercursor structures needed to address
+portions of already-allocated DER code space. Note that Quick DER never
+allocates dynamic memory for itself, so you are free()d from memory management
+for these routines.
+
+Moreover, the routines help you to avoid allocating dynamic memory too. The
+walker construct, together with basic movements inside DER structures, makes
+it easy to setup a cursor and have it traverse paths. And there are iterators
+to walk through SEQUENCE OF and SET OF structures.
+
+The only reason you might have to allocate memory is when you intend to fully
+der_unpack() into SEQUENCE OF and SET OF structures. In this case, the output
+in the form of an dercursor array can have any size due to the variable size
+of the repeated structure. You may be forced to use this if your aim is to
+der_pack() the structure later, because it may otherwise end up as a Primitive
+structure, which may not be what you intended to deliver. But even then, it
+is a matter of dercursor structures, not the full-blown packaged data size.
+After taking ample size precautiouns, this might easily be done on a stack.
+
+The other components of variability do not require explicit allocation, because
+the space for all their variants can be unfolded and separately stored.
+This applies to OPTIONAL / DEFAULT as well as to CHOICE. Entries that were
+not parsed are simply set to NULL dercursor values, so it is detectable that
+they were not present in the input (and should not appear in the output).
+The ANY element is parsed by providing the entire structure at its position,
+including its header (and tag). You can use der_enter() or der_header() to
+skip or analyse the header, and/or you could start a new der_unpack() or
+der_walk() to get into the structure, based on the applicable syntax.
+
+The number of dercursor structures created depends on the parser prescription.
+Here is the count for der_unpack()ing a few standard structures:
+
+ * 16 dercursor values for a PKIX Certificate
+ * 03 dercursor values for a PKIX Certificate Extension
+ * 07 dercursor values for a Kerberos5 Ticket's unencrypted part
+ * 14 dercursor values for a Kerberos5 Ticket's EncTicketPart
+
+Quite humble, isn't it?
+
+
+## Stack size
+
+The storage needed on the stack depends solely on routine nesting depth.
+The nesting depth depends on the ASN.1 syntax descriptions being processed;
+since it does not depend on the data, there is no risk of a stack overflow
+caused by too large data. (You should of course still be careful when you
+allocate dynamically sized, that is, input-determined, data on your stack.)
+
+With der_walk() there is never any nesting. For der_pack() and der_unpack()
+the nesting is determined by the number of ENTER / LEAVE pairs.
+In addition, der_pack() will nest for components prepared with der_prepack()
+which might also be prepared in a nested fashion. In addition, der_unpack()
+will nest for CHOICE sequences.
+
+The following indications provide some information on the maximum reached
+nesting depth while running der_unpack() over some standard structures:
+
+ * 4 nesting levels for a PKIX Certificate
+ * 1 nesting levels for a PKIX Certificate Extension
+ * 4 nesting levels for a Kerberos5 Ticket's unencrypted part
+ * 5 nesting levels for a Kerberos5 Ticket's EncTicketPart
+
+The reason that Kerberos is using a few more nesting levels is that it uses
+explicit tagging very liberally. It's a modest cost for such a bright use
+of ASN.1 and especially to achieve extensibility in the data formats.
+
+
+## Benchmarking Environment
+
+The aforementioned figures were obtained using:
+
+ * `gcc` 4.7.2
+ * `CFLAGS=-Os -ggdb3 -c`
+ * `objdump -h -j .text *.o`
+ * The first GIT checkin of the source code (may be updated later on)
+
+The choice for `-Os` was based on the intention to demonstrate suitability
+for embedded environments. It saved about 42% code size compared to `-O0`
+(no optimisation) and about 25% compared to `-O2` (often used in projects as
+their default optimisation).
+
--- /dev/null
+#!/bin/sh
+
+echo
+echo My apologies\; I have not enjoyed my previous encounter with autotools.
+echo
+echo For a package supposed to overcome platform differences, it had too many
+echo version dependencies of its own. Also, I failed to grasp the logic or
+echo structure of the autotools, making it seem like an endless series of
+echo fixes without clarity of why any of them was needed, and where.
+echo
+echo Please accept my humble offering of Makefiles instead.
+echo
--- /dev/null
+# Photo license
+
+Description
+ Català: Xarranca
+ Čeština: Nebe, peklo, ráj / Skákání panáka
+ Deutsch: Hickelkasten
+ English: Hopscotch
+ Español: Rayuela
+ Français : Marelle
+ Nederlands: Hinkelbaan
+ Português: Amarelinha
+ Русский: Классики
+ Svenska: Hoppa hage
+
+Souce
+ https://commons.wikimedia.org/wiki/File:Metropole_Zlicin_-_skakaci_panak_2.jpg
+
+Date
+ 22 September 2008
+
+Source
+ Own work
+
+Author
+ Matěj Baťha
+
+Permission
+ https://commons.wikimedia.org/wiki/Commons:Reusing_content_outside_Wikimedia
+ Creative Commons Attribution ShareAlike 3.0
+ https://creativecommons.org/licenses/by-sa/3.0/deed.en
+
--- /dev/null
+
+#ifndef QUICK_DER_H
+#define QUICK_DER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+
+#ifdef DEBUG
+# include <stdio.h>
+# define DPRINTF printf
+#else
+# define DPRINTF
+#endif
+
+
+/* Most of BER is included with these routines as well, but not the
+ * indefinate-length method. Also, there is no support for application,
+ * contextual and private tags [31] and up. What this means in practice,
+ * is that you should be able to process PKIX certificates, in spite of
+ * their outer layer being BER and only the tbsCertificate being DER.
+ */
+
+
+/* Cursors describe the ASN.1 buffer in DER coding, and can be walked into
+ * structures when they follow a path. It is possible to make copies of this
+ * structure between partial traversals, so as to efficiently run into a
+ * structure.
+ */
+typedef struct dercursor {
+ uint8_t *derptr;
+ size_t derlen;
+} dercursor;
+
+
+/* Unpacked DER structures take the same shape as a dercursor, and usually
+ * overlay the same space; it is assumed that the programmer is aware of
+ * the phase his program is in, and addresses the right variation. Most
+ * often, this type will not be used, but a properly typed structure with
+ * the same names. The derray points to an array of dercursors, which is
+ * then overlayed with a structure with dercursors named after fields in
+ * the ASN.1 syntax; the dercnt value indicates how many elements dercursors
+ * exist in the DER data.
+ *
+ * This variation is useful for dynamically-sized data, notably from unpacking
+ * SEQUENCE OF and SET OF substructures.
+ */
+
+typedef struct derarray {
+ dercursor *derray;
+ size_t dercnt;
+} derarray;
+
+
+/* Prepacked DER structures finally, also overlay the dercursor and/or the
+ * derarray structures, to cover yet another phase of the process. In this
+ * case, there still is a pointer to a derarray, but it has been marked with
+ * a variation on the length, namely with the most significant bit set. This
+ * makes the variation recognisable during the packing process, without
+ * really affecting the potential of an in-memory stored data structure.
+ */
+
+#define DER_DERLEN_FLAG_CONSTRUCTED ((~(size_t)0)^((~(size_t)0)>>1))
+#define DER_DERLEN_ERROR ((~(size_t)0)>>1)
+
+typedef struct derprep {
+ dercursor *derray;
+ size_t derlen_msb;
+} derprep;
+
+
+/* The above structures may be overlaid in a union, but since the derray is
+ * usually replaced with another component, a variation is used by generated
+ * code. Especially the "info" variation is then usually replaced by an
+ * overlay structure with names taken from the ASN.1 syntax. For home-brewn
+ * parsers however, we do include a generic union below.
+ *
+ * The variation "wire" refers to the format on the wire, so plain DER.
+ * The variation "info" refers to an unpacked array of dercursors.
+ * The variation "prep" refers to a form that is prepared for sending.
+ */
+typedef union dernode {
+ dercursor wire;
+ derarray info;
+ derarray prep;
+} dernode;
+
+
+
+/* WRITING GOOD DER TARVERSAL PATHS.
+ *
+ * A path constitutes a “lazy” parser going through a DER structure. It will
+ * trigger faults when it runs into them, but will otherwise be quite permissive.
+ * The lazy thing is that it simply advances a cursor to move through the ASN.1
+ * specification.
+ *
+ * To write an ASN.1 path expression, know the place where your cursor starts,
+ * which usually is the entire structure you stored in a buffer, and know where
+ * it should end. To get to the end point, you will need to do a series of
+ * actions, namely skipping entries and entering other entries. A path is
+ * basically a series of such steps.
+ *
+ * The ASN.1 syntax assures parseability with only one tag of lookahead. This
+ * means that at any choice point in the syntax, you can safely skip ahead to the
+ * one you meant to find. So, ignore a CHOICE and skip any OPTIONAL bits
+ * unless they are actually part of your path.
+ *
+ * The path traversal mechanism is a lazy syntax check, meaning it will check
+ * precisely those parts that it needs to traverse the path, but accept anything
+ * else without giving it a second look. In other words, the code is sufficiently
+ * secure to not accept badly formatted data, but only inasfar as you actually
+ * need that data. To this end, an OPTIONAL tag may be skipped by prefixing
+ * it with a special tag, and a CHOICE may be skipped regardless of the actual
+ * choice it made (if you want to enter either form, you should simply indicate
+ * the desired tag and the parser may return that it stopped being able to parse
+ * the structure from that point in your code, which tells you to try another path
+ * from the cursor position). And yes, you can have an optional choice by
+ * first using DER_WALK_OPTIONAL, DER_WALK_CHOICE, DER_TAG_…
+ *
+ * Path components are specified by their tags, for which definitions follow; in
+ * addition, a flag DER_WALK_SKIP or DER_WALK_ENTER
+ * should be added to signal skip or enter. Note that the
+ * first component is not being matched; you should compare its tag by
+ * looking what the cursor points at. You should first ensure that you did not
+ * end up in an empty data structure though. Paths are stored in derwalk[]
+ * that end in DER_WALK_END.
+ *
+ * There is no support for the long-form tag; that is, tags 31 with continued
+ * tag bytes; it is assumed that a tag always fits in one byte and otherwise
+ * ENOTIMPL is reported.
+ *
+ * An example path, with made-up ASN.1 markup in comments, is:
+ *
+ * derwalk [] = {
+ * DER_WALK_ENTER | DER_TAG_SEQUENCE, // SEQUENCE OF
+ * DER_WALK_ENTER | DER_TAG_CONTEXT (0), // [0]
+ * DER_WALK_OPTIONAL,
+ * DER_WALK_SKIP | DER_TAG_BOOLEAN, // amActive BOOLEAN OPTIONAL
+ * DER_WALK_CHOICE , // myMood CHOICE { …. }
+ * DER_WALK_SKIP | DER_TAG_OCTET_STRING, // myNAME OCTET STRING
+ * DER_WALK_ENTER | DER_TAG_SEQUENCE, // options SEQUENCE OF
+ * DER_WALK_END
+ * };
+ *
+ * Having found the sequence we’re interested in, we could iterate over its
+ * elements.
+ *
+ * There certainly is room for a developer’s tool that creates such paths from a
+ * textual description and the ASN.1 syntax description. Perhaps the formats
+ * used by libtasn1 could help doing this. Compared to libtasn1, the advantage
+ * of this approach is that the code is simpler, memory management is simplified
+ * by using shared data, and that ought to improve efficiency rather dramatically.
+ */
+
+typedef const uint8_t derwalk;
+
+/* Special markers for instructions on a walking path */
+#define DER_WALK_END 0x00
+#define DER_WALK_OPTIONAL 0x3f
+#define DER_WALK_CHOICE 0x1f
+#define DER_WALK_ANY 0x1f
+
+/* Special markers for instructions for (un)packing syntax */
+#define DER_PACK_LEAVE 0x00
+#define DER_PACK_END 0x00
+#define DER_PACK_OPTIONAL 0x3f
+#define DER_PACK_CHOICE_BEGIN 0x1f
+#define DER_PACK_CHOICE_END 0x1f
+#define DER_PACK_ANY 0xdf
+
+/* Flags to add to tags to indicate entering or skipping them */
+#define DER_WALK_ENTER 0x20
+#define DER_WALK_SKIP 0x00
+#define DER_WALK_MATCHBITS (~(DER_WALK_ENTER | DER_WALK_SKIP))
+
+/* Flags to add to tags to indicate entering or storing them while (un)packing */
+#define DER_PACK_ENTER 0x20
+#define DER_PACK_STORE 0x00
+#define DER_PACK_MATCHBITS (~(DER_PACK_ENTER | DER_PACK_STORE))
+
+/* Universal tags and macros for application, contextual, private tags */
+#define DER_TAG_BOOLEAN 0x01
+#define DER_TAG_INTEGER 0x02
+#define DER_TAG_BITSTRING 0x03
+#define DER_TAG_BIT_STRING 0x03
+#define DER_TAG_OCTETSTRING 0x04
+#define DER_TAG_OCTET_STRING 0x04
+#define DER_TAG_NULL 0x05
+#define DER_TAG_OBJECTIDENTIFIER 0x06
+#define DER_TAG_OBJECT_IDENTIFIER 0x06
+#define DER_TAG_OID 0x06
+#define DER_TAG_OBJECT_DESCRIPTOR 0x07
+#define DER_TAG_EXTERNAL 0x08
+#define DER_TAG_REAL 0x09
+#define DER_TAG_ENUMERATED 0x0a
+#define DER_TAG_EMBEDDEDPDV 0x0b
+#define DER_TAG_EMBEDDED_PDV 0x0b
+#define DER_TAG_UTF8STRING 0x0c
+#define DER_TAG_RELATIVEOID 0x0d
+#define DER_TAG_RELATIVE_OID 0x0d
+#define DER_TAG_SEQUENCE 0x10
+#define DER_TAG_SEQUENCEOF 0x10
+#define DER_TAG_SEQUENCE_OF 0x10
+#define DER_TAG_SET 0x11
+#define DER_TAG_SETOF 0x11
+#define DER_TAG_SET_OF 0x11
+#define DER_TAG_NUMERICSTRING 0x12
+#define DER_TAG_PRINTABLESTRING 0x13
+#define DER_TAG_T61STRING 0x14
+#define DER_TAG_VIDEOTEXSTRING 0x15
+#define DER_TAG_IA5SRING 0x16
+#define DER_TAG_UTCTIME 0x17
+#define DER_TAG_GENERALIZEDTIME 0x18
+#define DER_TAG_GRAPHICSTRING 0x19
+#define DER_TAG_VISIBLESTRING 0x1a
+#define DER_TAG_GENERALSTRING 0x1b
+#define DER_TAG_UNIVERSALSTRING 0x1c
+#define DER_TAG_CHARACTERSTRING 0x1d
+#define DER_TAG_CHARACTER_STRING 0x1d
+#define DER_TAG_BMPSTRING 0x1e
+
+#define DER_TAG_APPLICATION(n) (0x40 | (n))
+#define DER_TAG_CONTEXT(n) (0x80 | (n))
+#define DER_TAG_PRIVATE(n) (0xc0 | (n))
+
+
+/* PARSING AN ENTIRE STRUCTURE
+ *
+ * Although it is useful to be able to construct paths that walk through
+ * DER-encoded ASN.1 data, it is often more useful to parse a structure
+ * and put its values into a general data structure. This can be done
+ * by describing the entire syntax, in much the same fashion as a walking
+ * path that meanders through the entire structure. Ideally of course,
+ * this is automatically derived from an ASN.1 syntax description.
+ *
+ * The idea of parsing an entire structure is like a depth-first walk
+ * through the entire structure, and store all the individual components
+ * into a cursor of their own. The combined cursors can be overlayed
+ * by a structure type that has field names to match the ASN.1 syntax,
+ * so as to simplify locating entries to use in a program.
+ *
+ * Where we used symbols starting with DER_WALK_ to describe walking paths,
+ * we will use symbols starting with DER_PACK_ to describe the syntax
+ * descriptions, and we will use them both for packing and unpacking
+ * DER syntax.
+ *
+ * The flags DER_PACK_ENTER and DER_PACK_STORE indicate what to do with
+ * the current item being walked past; _ENTER unfolds a structures and
+ * dives into it, with the intention of backing out later using a
+ * DER_PACK_LEAVE. It is normal for the first instruction to have a
+ * DER_PACK_ENTER flag. The alternative flag is DER_PACK_STORE, which
+ * indicates that the present tag should not be processed, but instead
+ * be stored as a dercursor in the provided output array.
+ *
+ * Entries immediately following DER_PACK_OPTIONAL (including those
+ * structures marked in ASN.1 with a DEFAULT value, which by the way is
+ * not installed while unpacking a DER structure) will be skipped when
+ * they do not match; in case of DER_PACK_STORE flags, the respective
+ * dercursor structures will be set to derptr NULL and derlen 0.
+ *
+ * The ASN.1 pattern of a CHOICE between alternative substructures is
+ * marked between DER_PACK_CHOICE_BEGIN and DER_PACK_CHOICE_END, and may
+ * be surrounded by DER_PACK_OPTIONAL_ markers. Each of the possible
+ * substructures is tried on the upcoming DER element, and all that do
+ * not match fill a dercursor structure with derptr NULL and derlen 0.
+ * As soon as one has matched, it is handled as specified and the
+ * remaining choice options are all set to derptr NULL and derlen 0.
+ * When encountering DER_PACK_CHOICE_END, it is tested whether one of
+ * the choices was fulfilled, except when DER_PACK_OPTIONAL_ markers
+ * surround the choice section.
+ *
+ * Not all elements have a predictable number of dercursors, in which
+ * case they must be parses them in a special manner. This is true
+ * for the SEQUENCE OF and SET OF constructs; their total content needs
+ * to be _STOREd in a first pass, and separately parsed into an array
+ * of dercursors (or an overlayed structure type). The caller allocates
+ * the space where it wants --on the stack or form a heap-- and also
+ * has the option of running through the contents with iterators based
+ * in the cursor that was _STOREd.
+ *
+ * Having done all this, we now have a structure that holds the various
+ * pieces of our DER structure, with field names that derive from the
+ * ASN.1 syntax. In fact, using the same structural descriptions that
+ * also derive from the ASN syntax, we should be able to reproduce the
+ * DER information from the parser output structures.
+ * (NOTE: Variations in Primitive-or-Constructed?
+ */
+
+
+/* Test if the cursor points to a Constructed type. Return 1 for yes, 0 for no.
+ * Note that too-short structures return 0, so this is not quite the inverse
+ * of der_isptimitive().
+ */
+static inline int der_isconstructed (const dercursor *crs) {
+ return (crs->derlen >= 2)? (((*crs->derptr) >> 5) & 0x01): 0;
+}
+
+/* Test if the cursor points to a Primitive type. Return 1 for yes, 0 for no.
+ * Note that too-short structures return 0, so this is not quite the inverse
+ * of der_isconstructed().
+ */
+static inline int der_isprimitive (const dercursor *crs) {
+ return (crs->derlen >= 2)? (((~ *crs->derptr) >> 5) & 0x01) : 0;
+}
+
+
+/* Ensure that the length available to the cursor is non-empty. This is sort of
+ * a DER-equivalent to a NULL pointer; it normally occurs only during
+ * iteration, where it can be used to test whether more data is available.
+ * Note that this does not actually read the DER-data, but instead the cursor.
+ * This functions return 1 for non-empty, or 0 for empty cursor lengths.
+ */
+static inline int der_isnonempty (const dercursor *crs) {
+ return (crs->derlen == 0)? 0: 1;
+}
+
+
+/* Test whether a cursor is set to a NULL value; this is the case when the
+ * data pointed to is actually NULL.
+ */
+static inline int der_isnull (const dercursor *crs) {
+ return (crs->derptr == NULL);
+}
+
+
+/* Analyse the header of a DER structure. Pass back its tag, len and the
+ * total header length. Analysis starts at crs, which will move past the
+ * header by updating both its derptr and derlen components. This function
+ * returns 0 on success, or -1 on error (in which case it sets errno).
+ *
+ * For BIT STRINGS, this routine validates that remainder bits are cleared.
+ * Note that this is a difference between BER and DER; DER requires that
+ * the bits are 0 whereas BER welcomes arbitrary values. In the interest
+ * of security (bit buffer overflows) and reproducability of signatures on
+ * data, this routine rejects non-zero remainder bits with an error. For
+ * your program, this may mean that the number of remainder bits do not
+ * need to be checked if zero bits are acceptable without overflow risk.
+ */
+int der_header (dercursor *crs, uint8_t *tagp, size_t *lenp, uint8_t *hlenp);
+
+
+/* Update a cursor expression by walking into a DER-encoded ASN.1 structure.
+ * The return value is -1 on error, and errno will be set accordinly, and the
+ * cursor will not have been updated. Otherwise, the return value is the number
+ * of unprocessed bytes on the path, so 0 when the entire path was processed.
+ * The count as non-error returns, so the cursor is updated. Values higher than
+ * 0 indicate where in the path a tag could not be found; this may be helpful in
+ * learning about the structure that was being parsed, for example that an
+ * OPTIONAL or CHOICE part was absent from the DER bytes.
+ *
+ * Paths are sequence of one-byte choices to be made. These choices are tags,
+ * because these are used by ASN.1 to decide on parsing choices to be made.
+ * The one difference is the interpretation of the Primitive/Constructed bit: when
+ * this is set to Primitive, the value will be skipped (even if it is Constructed)
+ * and when set to Constructed, the value will be entered and interpreted as ASN.1
+ * (even when it is setup as Primitive).
+ *
+ * In all the places where ASN.1 defines choices, such as CHOICE or OPTIONAL,
+ * it enforces distinct tags from the various choices. This can be used in a path
+ * to skip such unknown parts in the encoding.
+ *
+ * When entering a BIT STRING, special treatment is implemented; the remaining
+ * bits will have to be zero, and these are then skipped while entering the
+ * remainder. Note that this ensures that the byte-aligned DER structures are
+ * properly packed into a bit-aligned BIT STRING container.
+ */
+int der_walk (dercursor *crs, const derwalk *path);
+
+
+/* Skip the current value under the cursor. Return an empty cursor value
+ * if nothing more is to be had.
+ * This function returns -1 on error and sets errno; 0 on success.
+ */
+int der_skip (dercursor *crs);
+
+
+/* Enter the current value under the cursor. Return an empty cursor value
+ * if nothing more is to be had. Some special handling is done for BIT STRING
+ * entrance; for them, the number of remainder bits is required to be 0 and
+ * that initial byte is skipped.
+ *
+ * This function returns -1 on error and sets errno; 0 on success.
+ */
+int der_enter (dercursor *crs);
+
+
+/* Unpack a structure, or possibly a sequence of structures. The output
+ * is stored in subsequent entries of outarray, whose size should be
+ * precomputed to sufficient length. The outarray will often be an
+ * overlay for a structure composed of dercursor elements with labels
+ * and nesting derived from ASN.1 syntax, and matching an (un)packing walk.
+ *
+ * The syntax is supplied without a length; proper formatting of the syntax
+ * is assumed, that is the number of DER_PACK_ENTER bits should be followed
+ * by an equal amount of DER_PACK_LEAVE instructions, and the choice
+ * markers DER_PACK_CHOICE_BEGIN ... DER_PACK_CHOICE_END must be properly
+ * nested with those instructions and with each other. There is no
+ * protection for foolish specifications (and they will often be generated
+ * anyway). This method additionally requires the first element in the
+ * syntax to be flagged with DER_PACK_ENTER. (TODO: Permit non-looped use?)
+ *
+ * The cursor will be updated by this call to point to the position
+ * where unpacking stopped. Refer to the return value to see if this is
+ * an error position. The function returns 0 on success and -1 on failure.
+ * Upon failure, errno is also set, namely to EBADMSG for syntax problems
+ * or ERANGE for lengths or tags that are out of the supported range of
+ * this implementation.
+ */
+int der_unpack (dercursor *crs, const derwalk *syntax,
+ dercursor *outarray, int repeats);
+
+
+/* Given an iterator, setup a second iterator to run over its contained
+ * components. While iterating, the initial iterator must continue to be
+ * supplied, without modification to it.
+ *
+ * This function returns 1 upon success. In case of failure, it
+ * returns 0; in addition, it sets the nested iterator for zero
+ * iterations. A special case of error is when the container cursor is
+ * not pointing to a Constructed element; in this case an error is returned
+ * but the cursor will run over the contained elements when using the iterator.
+ *
+ * To be sensitive to errors, use this as follows:
+ *
+ * if (der_iterate_first (cnt, &iter)) do {
+ * ...process entry...
+ * } while (der_iterate_next (&iter));
+ *
+ */
+int der_iterate_first (const dercursor *container, dercursor *iterator);
+
+/* Step forward with an iterator. This assumes an iterator that was
+ * setup by der_iterate_first() and has since then not been modified.
+ */
+int der_iterate_next (dercursor *iterator);
+
+
+/* Count the number of elements available after entering the component
+ * under the cursor. This is useful to know how many elements exist inside
+ * a SEQUENCE OF or SET OF, but may be used for other purposes as well.
+ */
+int der_countelements (dercursor *container);
+
+
+/* COMPOSING DER STRUCTURES FOR TRANSMISSION
+ *
+ * While working with DER data, the various dercursor structures can be passed
+ * around freely, because they are mere <pointer,length> tuples that reference
+ * independently management memory -- usually an input buffer. As long as the
+ * input buffer is not cleared, the tuples can be carried around and copied
+ * and destroyed at will.
+ *
+ * Just like overlay structures are filled with dercursor fields labelled
+ * according to the ASN.1 syntax during der_unpack(), it is also possible to
+ * build up such structures for output with der_pack(). These structures
+ * must once more refer to independently allocated memory, which may be any
+ * mixture of sources: input buffers, stack portions, heap structures. As
+ * long as these memory regions are kept around during der_pack(). After
+ * der_pack(), a new output area has been filled and the fragments can be
+ * cleared, deallocated and so on.
+ *
+ * The der_pack() routine is based on the same DER_PACK_ syntax descriptions
+ * used by der_unpack(), so it is straightforward to unpack a structure, make
+ * some modifications and pack its new incarnation.
+ *
+ * One point of concern is the DER_STORE_ structure. This can both be used
+ * for Primitive types and for Constructed types such as SEQUENCE OF and
+ * SET OF that either need an additional call to der_unpack() to be parsed,
+ * or iteration, or der_walk(). Reproducing those pieces literally is not
+ * a problem, and leads to the original data as a Primitive type. But when
+ * the data is in fact Constructed, or perhaps modified, something else is
+ * required. To accommodate that, you may need to der_prepack() such partial
+ * structures that are to be dealt with a Constructed types; most commonly
+ * however, is that this is necessary for SEQUENCE OF and SET OF, which never
+ * occur as Primitive types, and are therefore always treated correctly.
+ *
+ * The general idea of der_prepack() is that you supply a dercursor array
+ * and set it up in the original structure stored by DER_STORE_. This
+ * leads to a different representation that will be recognised by
+ * der_pack() as a reference to such an array, which it will insert as
+ * a Constructed subtype, with the tag that it originally had during
+ * parsing. Specifically, the highest bit of derlen, identified by the
+ * symbol DER_DERLEN_FLAG_CONSTRUCTED, will be set to indicate that the
+ * remainder of the derlen is to be interpreted as a number of dercursors,
+ * to be found at the derptr. This differs from the usual meaning, which
+ * is a literal series of bytes pointed to by derptr and whose length is
+ * stored in derlen. TODO: Do we really want to do it this way?!?
+ *
+ * Note that the special ASN.1 constructs "ANY" and "ANY DEFINED BY" are
+ * not a problem. They are stored with the tag and length included, and
+ * are therefore trivial to reproduce. Also, the CHOICE and OPTIONAL
+ * versions are trivially handled by having NULL dercursors. Do avoid
+ * calling der_pack() with multiple options in one CHOICE set though.
+ *
+ * Based on all this, the der_pack() routine can scan over the syntax,
+ * look at the data at hand and determine what needs to go where. It
+ * is often good to know the storage space before writing, and
+ * der_packlen() can be used to determine that.
+ *
+ * Since der_pack() assumes it is provided with the properly sized
+ * memory buffer to write to, it will start at the end and work its
+ * way back. This means that lengths are known at their time of
+ * insertion into the buffer. The idea stems from MIT Kerberos5,
+ * but unlike that implementation we do use a "forward" description
+ * and merely traverse it from the end to the beginning. Note that
+ * the DER_PACK_ descriptions lend themselves well for doing this.
+ */
+
+
+/* Pack a memory buffer following the indicated syntax, and using the elements
+ * stored in the derray. Enough memory is assumed to be available _before_
+ * outbuf_end_opt; to find how large this buffer needs to be, it is possible to
+ * call this function with outbuf_end_opt set to NULL.
+ *
+ * The return value is the same, regardless of outbuf_end_opt being NULL or not;
+ * it is the length of the required output buffer. When an error occurs, the
+ * value 0 is returned, but that cannot happen on a second run on the same data
+ * with only the outbuf_end_opt set to non-NULL.
+ *
+ * Please note once more that outbuf_end_opt, when non-NULL, points to the
+ * first byte that is _not_ filled with the output DER data. The value will
+ * be decremented in this function for the bytes written. This is quite
+ * simply a more optimal strategy for DER production than anything else.
+ * And yes, this is funny in an API, but you have the information and we would
+ * otherwise ask you to pass it in, need to check it, you would then need to
+ * check for extra error returns, ... so this is in fact simpler.
+ *
+ * Any parts of this structure that need to be prepacked are assumed to have
+ * been prepared with der_prepack(). If your packaged structures show up as
+ * Primitive where they should have been Constructed, then this is where to
+ * look.
+ */
+size_t der_pack (const derwalk *syntax, const dercursor *derray,
+ uint8_t *outbuf_end_opt);
+
+
+/* Pre-package the given DER array into the dercursor that is also provided.
+ * This operation modifies the information stored in the destination field,
+ * in a way that stops it from being interpreted properly in the usualy
+ * manner, but it _does_ prepare it for der_pack() in a way that will include
+ * the array of dercursor as a consecutive sequence, without any additional
+ * headers, markers or other frills.
+ */
+void der_prepack (dercursor *derray, size_t arraycount, derarray *target);
+
+
+#endif /* QUICK_DER_H */
--- /dev/null
+OBJS = der_pack.o der_prepack.o der_unpack.o der_iterate.o der_walk.o der_skipenter.o der_header.o
+
+TARGETS = libquickder.a libquickder.so
+
+PREFIX = /usr/local
+
+all: $(TARGETS)
+
+install: $(TARGETS)
+ install $(TARGETS) "$(PREFIX)/lib"
+ mkdir -p "$(PREFIX)/include/quick-der"
+ install ../include/quick-der/api.h "$(PREFIX)/include/quick-der"
+
+uninstall:
+ for f in $(TARGETS); do rm -f "$(PREFIX)/lib/$$f" ; done
+ rm -f "$(PREFIX)/include/quick-der/api.h"
+ rmdir --ignore-fail-on-non-empty "$(PREFIX)/include/quick-der"
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
+
+.c.o:
+ gcc -fPIC -Os -c -I../include -o "$@" "$<"
+
+libquickder.a: $(OBJS)
+ rm -rf "$@"
+ ar rc "$@" $(OBJS)
+
+libquickder.so: $(OBJS)
+ gcc -I../include --shared -Os -o "$@" $(OBJS)
+
+# test-kxover: test-kxover.c quick-der.c libquickder.a
+# gcc -I../include -ggdb3 -o $@ test-kxover.c libquickder.a -lc
+#
+# test-cert-1: test-cert-1.c quick-der.c libquickder.a
+# gcc -I../include -ggdb3 -o $@ test-cert-1.c libquickder.a -lc
+#
+# test-cert-2: test-cert-2.c quick-der.c libquickder.a
+# gcc -I../include -ggdb3 -DDEBUG -o $@ test-cert-2.c libquickder.a -lc
+#
+# test-cert-3: test-cert-3.c quick-der.c libquickder.a
+# gcc -I../include -ggdb3 -o $@ test-cert-3.c libquickder.a -lc
+
+stats: $(OBJS)
+ @for o in *.o ; do objdump -h -j .text $$o | sed -e '/\.text/!d' -e 's/^.*\.text[ \t]*\([^ \t]*\).*/\1'" $${o%.o}/" ;done | while read sz fun ; do printf '%5d %s\n' 0x$$sz $$fun ; done
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Analyse the header of a DER structure. Pass back its tag, len and the
+ * total header length. Analysis starts at crs, which will move past the
+ * header by updating both its derptr and derlen components. This function
+ * returns 0 on success, or -1 on error (in which case it sets errno).
+ *
+ * For BIT STRINGS, this routine validates that remainder bits are cleared.
+ * Note that this is a difference between BER and DER; DER requires that
+ * the bits are 0 whereas BER welcomes arbitrary values. In the interest
+ * of security (bit buffer overflows) and reproducability of signatures on
+ * data, this routine rejects non-zero remainder bits with an error. For
+ * your program, this may mean that the number of remainder bits do not
+ * need to be checked if zero bits are acceptable without overflow risk.
+ */
+int der_header (dercursor *crs, uint8_t *tagp, size_t *lenp, uint8_t *hlenp) {
+ uint8_t tag;
+ uint8_t lenlen;
+ uint8_t rembits;
+ uint8_t rembyte;
+ size_t len;
+ *tagp = tag = *crs->derptr++;
+ crs->derlen--;
+ if ((tag & 0x1f) == 0x1f) {
+ // No support for long tags
+ errno = ERANGE;
+ return -1;
+ }
+ len = *crs->derptr++;
+ crs->derlen--;
+ if (len & 0x80) {
+ lenlen = len & 0x7f;
+ *hlenp = 2 + lenlen;
+ if (lenlen > crs->derlen) {
+ errno = EBADMSG;
+ return -1;
+ }
+ if (lenlen > sizeof (size_t)) {
+ // No support for such long sizes
+ errno = ERANGE;
+ return -1;
+ }
+ crs->derlen -= lenlen;
+ len = 0;
+ while (lenlen-- > 0) {
+ len <<= 8;
+ len |= *crs->derptr++;
+ }
+ } else {
+ *hlenp = 2;
+ }
+ if (len & DER_DERLEN_FLAG_CONSTRUCTED) {
+ errno = ERANGE;
+ return -1;
+ }
+ if (len > crs->derlen) {
+ errno = EBADMSG;
+ return -1;
+ }
+ // Special treatment for BIT STRING (one additional header byte)
+ if (tag == DER_TAG_BITSTRING) {
+ rembits = *crs->derptr;
+ rembyte = crs->derptr [len-1] & (0xff >> (8 - rembits));
+ if ((len == 0) || (*crs->derptr > 7) || (rembyte != 0x00)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ }
+ *lenp = len;
+DPRINTF ("DEBUG: Header analysis: tag 0x%02x, hdrlen %d, len %d, rest %d\n", *tagp, *hlenp, *lenp, crs->derlen);
+ return 0;
+}
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Given a dercursor, setup an iterator to run over its contained components.
+ * While iterating, the initial iterator must continue to be supplied, without
+ * modification to it.
+ *
+ * This function returns 1 upon success. In case of failure, it
+ * returns 0; in addition, it sets the nested iterator for zero
+ * iterations. A special case of error is when the container cursor is
+ * not pointing to a Constructed element; in this case an error is returned
+ * but the cursor will run over the contained elements when using the iterator.
+ *
+ * To be sensitive to errors, use this as follows:
+ *
+ * if (der_iterate_first (cnt, &iter)) do {
+ * ...process entry...
+ * } while (der_iterate_next (&iter));
+ *
+ */
+int der_iterate_first (const dercursor *container, dercursor *iterator) {
+#if 0 /* Old code */
+ *iterator = *container;
+ der_enter (iterator);
+ if (!der_isnonempty (iterator)) {
+ return 0;
+ }
+ if (der_isconstructed (container)) {
+ return 1;
+ } else {
+ return 0;
+ }
+#else
+ *iterator = *container;
+#endif
+}
+
+/* Step forward with an iterator. This assumes an iterator that was
+ * setup by der_iterate_first() and has since then not been modified.
+ */
+int der_iterate_next (dercursor *iterator) {
+ der_skip (iterator);
+ return (iterator->derlen >= 2);
+}
+
+
+/* Count the number of elements available after entering the component
+ * under the cursor. This is useful to know how many elements exist inside
+ * a SEQUENCE OF or SET OF, but may be used for other purposes as well.
+ */
+int der_countelements (dercursor *container) {
+ int retval = 0;
+ dercursor iter;
+ if (der_iterate_first (container, &iter)) do {
+ retval++;
+ } while (der_iterate_next (&iter));
+ return retval;
+}
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Backward-insert the bytes for the individual entries of the given derprep
+ * structure; the entry is assumed to be setup with prepack, and so to contain
+ * a derray pointing to an array of dercursor, and the derlen_msb is supposed
+ * to be the element count with the highest bit set for the
+ * DER_DERLEN_FLAG_CONSTRUCTED flagging.
+ *
+ * This function does not return failure under the assumption that a
+ * properly-sized buffer is available for it. The return value is
+ * still the size, because it is needed when processing the data.
+ *
+ * If bufend is NULL, this function can be used to measure the size of the
+ * total insertion. In this case, the function may return DER_DERLEN_ERROR
+ * to indicate an error.
+ */
+size_t der_pack_prepack (const derprep *derp, uint8_t **bufend) {
+ size_t totlen = 0;
+ size_t elmlen;
+ size_t cnt = derp->derlen_msb & ~DER_DERLEN_FLAG_CONSTRUCTED;
+ dercursor *crs = derp->derray;
+ uint8_t *buf;
+ while (cnt-- > 0) {
+ if (crs->derlen & DER_DERLEN_FLAG_CONSTRUCTED) {
+ elmlen = der_pack_prepack ((const derprep *) crs+cnt, bufend);
+ if (elmlen == DER_DERLEN_ERROR) {
+ return DER_DERLEN_ERROR;
+ }
+ } else {
+ if (bufend) {
+ buf = *bufend;
+ buf -= elmlen;
+ memcpy (buf, crs [cnt].derptr, elmlen);
+DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", elmlen, buf);
+ *bufend = buf;
+ }
+ elmlen = crs->derlen;
+ }
+ totlen += elmlen;
+ if ((totlen | elmlen) & DER_DERLEN_FLAG_CONSTRUCTED) {
+ return DER_DERLEN_ERROR;
+ }
+ }
+ return totlen;
+}
+
+
+/* Backward-insert the bytes for der_pack() for the given syntax, using the
+ * DER array for elementary values. Special handling is provided when a
+ * BIT STRING is entered; this encapsulates byte-aligned DER codes into a
+ * bit-aligned BIT STRING, so we can insert the remainder bits set to 0.
+ * Also handled specially is DER_PACK_ANY, which causes the entire structure
+ * to be stored or returned, including its DER header.
+ *
+ * The routine returns 0 if it encounters an error, or otherwise the number
+ * of bytes filled. When it is called with a non-NULL bufend and an output
+ * buffer of the right size, it will not return an error.
+ */
+static size_t der_pack_rec (const derwalk *syntax, int *stxlen,
+ uint8_t **bufend,
+ const dercursor *derray, size_t *offsetp) {
+ size_t totlen = 0;
+ size_t elmlen = 0;
+ size_t tmplen;
+ bool addhdr;
+ bool bitstr;
+ uint8_t cmd;
+ uint8_t tag;
+ uint8_t *buf;
+ uint8_t lenlen;
+ const dercursor *dernext;
+DPRINTF ("DEBUG: Entered recursive call der_pack_rec() with bufend=0x%016llx\n", bufend? *bufend: 0);
+ do {
+ // deref stxend; decrease the stored pointer; deref that pointer:
+ tag = cmd = syntax [-- *stxlen];
+ bitstr = (cmd == (DER_PACK_ENTER | DER_TAG_BITSTRING));;
+DPRINTF ("DEBUG: Command to pack_rec() is 0x%02x, collected length is %zd, offset is %zd\n", cmd, totlen, *offsetp);
+ // Note: DER_PACK_ANY ends up under DER_PACK_STORE below
+ if ((cmd == DER_PACK_CHOICE_BEGIN)
+ || (cmd == DER_PACK_CHOICE_END)
+ || (cmd == DER_PACK_OPTIONAL)) {
+ // Skip, and rely on consistent NULL dercursor entries
+DPRINTF ("DEBUG: Choice|Optional command has no data\n");
+ cmd = 0x00; // Avoid falling out (OPTIONAL & ENTER)
+ continue;
+ } else if (cmd & DER_PACK_ENTER) {
+ // Ends current (recursive) der_pack_rec() for sub-part
+ // Continue below, where the <tag,elmlen> header is added
+ addhdr = 1;
+ elmlen = totlen;
+ totlen = bitstr? 1: 0;
+DPRINTF ("DEBUG: Post-enter element, moved totlen %zd to element length\n", elmlen);
+ } else if (cmd == DER_PACK_LEAVE) {
+ // Make a recursive call for what precedes DER_PACK_LEAVE
+ elmlen = der_pack_rec (syntax, stxlen, bufend, derray, offsetp);
+ if (elmlen == DER_DERLEN_ERROR) {
+ return DER_DERLEN_ERROR;
+ }
+ addhdr = 0;
+DPRINTF ("DEBUG: Recursive element length set to %zd\n", elmlen);
+ } else {
+ // We have hit upon a DER_PACK_STORE value (includes ANY)
+ addhdr = (cmd != DER_PACK_ANY);
+ // Consume one array element, even if it will be NULL
+ (*offsetp)--;
+ dernext = derray + *offsetp; // offset may have changed
+DPRINTF ("DEBUG: Updated offset to %zd, pointer is 0x%016llx\n", *offsetp, dernext);
+ if (der_isnull (dernext)) {
+ // Do not pack this entry, DEFAULT or CHOICE
+ elmlen = 0;
+ addhdr = 0;
+DPRINTF ("DEBUG: Consumed NULL entry at %zd, so elmlen set to 0\n", *offsetp);
+ } else if (derray->derlen & DER_DERLEN_FLAG_CONSTRUCTED) {
+ // Prepacked Constructed, non-NULL entry
+ elmlen = der_pack_prepack ((const derprep *) dernext, bufend);
+ if (elmlen == DER_DERLEN_ERROR) {
+ return DER_DERLEN_ERROR;
+ }
+DPRINTF ("DEBUG: Fetched length from constructed, recursive element\n");
+ } else {
+ // Primitive, non-NULL entry
+ elmlen = dernext->derlen;
+ if ((elmlen > 0) && (bufend != NULL)) {
+ buf = *bufend;
+ buf -= elmlen;
+ memcpy (buf, dernext->derptr, elmlen);
+DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", elmlen, buf);
+ *bufend = buf;
+ }
+ if ((tag == 0x08) || (tag == 0x0b)
+ || (tag == 0x10) || (tag == 0x11)) {
+ tag |= 0x20; // Constructed, even STORED
+ }
+DPRINTF ("DEBUG: Fetched length from primitive element\n");
+ }
+DPRINTF ("DEBUG: Stored element length set to %zd at offset %zd\n", elmlen, *offsetp);
+ }
+ if (addhdr) {
+ if (bufend) {
+ buf = *bufend;
+ if (bitstr) {
+ * --buf = 0x00;
+ }
+ }
+ lenlen = 0;
+ if (elmlen >= 0x80) {
+ tmplen = elmlen;
+ while (tmplen > 0) {
+ if (bufend) {
+ * -- buf = (tmplen & 0xff);
+ }
+ tmplen >>= 8;
+ lenlen++;
+ }
+ }
+ if (bufend) {
+ * -- buf = (elmlen >= 0x80)? (lenlen|0x80): elmlen;
+ * -- buf = tag;
+ * bufend = buf;
+DPRINTF ("DEBUG: Wrote %4d bytes to 0x%016llx\n", 2 + lenlen, buf);
+ }
+ elmlen += 2 + lenlen;
+ }
+DPRINTF ("DEBUG: Adding %zd to length %zd, collected length is %zd\n", elmlen, totlen, elmlen + totlen);
+ totlen += elmlen;
+ if ((elmlen | totlen) & DER_DERLEN_FLAG_CONSTRUCTED) {
+ return DER_DERLEN_ERROR;
+ }
+ // Special cases: DER_PACK_OPTIONAL has had the cmd value reset to 0x00
+ // Note: The while loop terminates *after* processing the ENTER cmd
+ } while (((cmd & DER_PACK_ENTER) == 0x00) && (*stxlen > 0));
+DPRINTF ("DEBUG: Leaving recursive call der_pack_rec() with bufend=0x%016llx\n", bufend? *bufend: 0);
+ return totlen;
+}
+
+
+/* Pack a memory buffer following the indicated syntax, and using the elements
+ * stored in the derray. Enough memory is assumed to be available _before_
+ * outbuf_end_opt; to find how large this buffer needs to be, it is possible to
+ * call this function with outbuf_end_opt set to NULL.
+ *
+ * The return value is the same, regardless of outbuf_end_opt being NULL or not;
+ * it is the length of the required output buffer. When an error occurs, the
+ * value 0 is returned, but that cannot happen on a second run on the same data
+ * with only the outbuf_end_opt set to non-NULL.
+ *
+ * Please note once more that outbuf_end_opt, when non-NULL, points to the
+ * first byte that is _not_ filled with the output DER data. The value will
+ * be decremented in this function for the bytes written. This is quite
+ * simply a more optimal strategy for DER production than anything else.
+ * And yes, this is funny in an API, but you have the information and we would
+ * otherwise ask you to pass it in, need to check it, you would then need to
+ * check for extra error returns, ... so this is in fact simpler.
+ *
+ * Any parts of this structure that need to be prepacked are assumed to have
+ * been prepared with der_prepack(). If your packaged structures show up as
+ * Primitive where they should have been Constructed, then this is where to
+ * look.
+ */
+size_t der_pack (const derwalk *syntax, const dercursor *derray,
+ uint8_t *outbuf_end_opt) {
+ int entered = 0;
+ uint8_t cmd;
+ int stxlen = 0;
+ size_t derraylen = 0;
+ size_t totlen = 0;
+ while (cmd = syntax [stxlen], (entered > 0) || (cmd != DER_PACK_END)) {
+ stxlen++;
+ if (cmd & DER_PACK_ENTER) {
+ if (cmd != DER_PACK_OPTIONAL) {
+ entered++;
+ }
+ } else {
+ if (cmd == DER_PACK_LEAVE) {
+ entered--;
+ } else if ((cmd != DER_PACK_CHOICE_BEGIN) && (cmd != DER_PACK_CHOICE_END)) {
+ // Remaining commands store data (including ANY)
+ derraylen++;
+ }
+ }
+ }
+DPRINTF ("DEBUG: Skipping %d syntax bytes, ending in %02x %02x %02x %02x | %02x\n", stxlen, syntax [-4], syntax [-3], syntax [-2], syntax [-1], syntax [0]);
+ while (stxlen > 0) {
+ totlen += der_pack_rec (syntax, &stxlen,
+ outbuf_end_opt? &outbuf_end_opt: NULL,
+ derray, &derraylen);
+ }
+ // One could assert() on derraylen == NULL, and syntax back to initial
+ return totlen;
+}
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Pre-package the given DER array into the dercursor that is also provided.
+ * This operation modifies the information stored in the destination field,
+ * in a way that stops it from being interpreted properly in the usualy
+ * manner, but it _does_ prepare it for der_pack() in a way that will include
+ * the array of dercursor as a consecutive sequence, without any additional
+ * headers, markers or other frills.
+ */
+void der_prepack (dercursor *derray, size_t arraycount, derarray *target) {
+ target->derray = derray;
+ target->dercnt = arraycount;
+}
+
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Skip the current value under the cursor. Return an empty cursor value
+ * if nothing more is to be had.
+ * This function returns -1 on error and sets errno; 0 on success.
+ */
+int der_skip (dercursor *crs) {
+ uint8_t tag;
+ uint8_t hlen;
+ size_t len;
+ if (der_header (crs, &tag, &len, &hlen)) {
+ crs->derptr = NULL;
+ crs->derlen = 0;
+ return -1;
+ } else {
+ crs->derptr += len;
+ crs->derlen -= len;
+ return 0;
+ }
+}
+
+/* Enter the current value under the cursor. Return an empty cursor value
+ * if nothing more is to be had. Some special handling is done for BIT STRING
+ * entrance; for them, the number of remainder bits is required to be 0 and
+ * that initial byte is skipped.
+ *
+ * This function returns -1 on error and sets errno; 0 on success.
+ */
+int der_enter (dercursor *crs) {
+ uint8_t tag;
+ uint8_t hlen;
+ size_t len;
+ if (der_header (crs, &tag, &len, &hlen)) {
+ crs->derptr = NULL;
+ crs->derlen = 0;
+ return -1;
+ } else {
+ crs->derlen = len;
+ return 0;
+ }
+ if (tag == DER_TAG_BITSTRING) {
+ crs->derlen--;
+ crs->derptr++;
+ }
+}
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Unpack a DER structure based on its ASN.1 description, mapped to DER_PACK_
+ * instructions. This includes handling of OPTIONAL/DEFAULT and CHOICE syntax.
+ * It also includes a DER_PACK_ENTER flag and DER_PACK_LEAVE instruction to
+ * dive into a structure, as well as a DER_PACK_STORE flag to store the outcome
+ * of a construct.
+ *
+ * When an error is encountered, this function returns NULL and sets errno to
+ * a suitable error code. In case of success, the return value gives the
+ * new position from which the walk should continue. It also updates the
+ * number of elements in the output array in outctr.
+ *
+ * This routine takes a dercursor that it will move forward, up to a point
+ * where parsing failed or was simply done. In addition, it uses an
+ * outarray and an outctr that is incremented while entries (NULL or other)
+ * are filled in.
+ *
+ * This recursive routine processes a number of flags that modify its action:
+ * - choice implements a CHOICE of alternatives (possibly OPTIONAL) and
+ * will normally require precisely one of these alternatives to match;
+ * - optional indicates that the first DER element does not need to match
+ * because the syntax marks the *walk as OPTIONAL or having a DEFAULT;
+ * - optout indicates that the entries should be parsed and skipped, but
+ * only produce NULL entries due to CHOICE or OPTIONAL semantics.
+ * Note that optout applies to any length of DER elements. It is used to
+ * support incrementing outctr while setting NULL values where otherwise
+ * there might have been actual values, but the syntax context blocks that.
+ */
+static const derwalk *der_unpack_rec (dercursor *crs, const derwalk *walk,
+ dercursor *outarray,
+ int *outctr,
+ bool choice,
+ bool optional,
+ bool optout) {
+ uint8_t tag;
+ uint8_t hlen;
+ uint8_t terminal;
+ uint8_t cmd;
+ size_t len;
+ dercursor newcrs;
+ dercursor hdrcrs;
+ bool chosen = 0;
+ bool optoutsub = optout;
+DPRINTF ("DEBUG: Entering der_unpack_rec() at 0x%08llx\n", (intptr_t) walk);
+ //
+ // Decide on the terminal code and parse until that value
+ if (choice) {
+ terminal = DER_PACK_CHOICE_END;
+ } else {
+ terminal = DER_PACK_LEAVE;
+ }
+DPRINTF ("DEBUG: Start looping around for 0x%02x\n", terminal);
+ while (*walk != terminal) {
+DPRINTF ("DEBUG: Entering loop with choice=%d, optional=%d, optout=%d\n", choice, optional, optout);
+ //
+ // First detect the more complex structures for CHOICE and OPTIONAL
+ if (*walk == DER_PACK_OPTIONAL) {
+ //
+ // Parse the prefix command for OPTION / DEFAULT
+DPRINTF ("DEBUG: Encountered OPTIONAL\n");
+ if (optional || choice) {
+ // Nested OPTION, that can't be good
+ // OPTION within CHOICE also signifies trouble
+ errno = EBADMSG;
+ return NULL;
+ }
+ optional = 1; // for the one next entry (may be ENTER)
+ walk++;
+ }
+ if (*walk == DER_PACK_CHOICE_BEGIN) {
+ //
+ // Parse for a choice, leaving the OPTIONAL flag as is
+DPRINTF ("DEBUG: Encountered CHOICE\n");
+ if (choice) {
+ // Nested CHOCIE, that can't be good
+ errno = EBADMSG;
+ return NULL;
+ }
+DPRINTF ("DEBUG: Making recursive call because of CHOICE_BEGIN\n");
+ walk = der_unpack_rec (crs, walk + 1,
+ outarray, outctr,
+ 1, optional, optout);
+ if (walk == NULL) {
+ // Pass on inner error
+ return NULL;
+ }
+DPRINTF ("DEBUG: Ended recursive call because of CHOICE_END\n");
+ optional = 0; // any 1 was used up by recursive CHOICE
+DPRINTF ("DEBUG: Next command up is 0x%02x with %d left\n", *walk, crs->derlen);
+ continue;
+ }
+ //
+ // Check if we have anything left to process at the DER cursor
+ if (crs->derlen < 2) {
+ if ((crs->derlen == 0) && optional) {
+ // Empty value is acceptable, skip ahead
+ if (*walk & DER_PACK_STORE) {
+ memset (outarray + (*outctr)++,
+ 0,
+ sizeof (dercursor));
+ walk++;
+ continue;
+ }
+ } else {
+DPRINTF ("ERROR: Message size is only %d\n", crs->derlen);
+ errno = EBADMSG;
+ return NULL;
+ }
+ }
+ //
+ // Pickup the tag and check its sanity
+ hdrcrs = newcrs = *crs;
+ if (der_header (&hdrcrs, &tag, &len, &hlen)) {
+ return NULL;
+ }
+ //
+ // Now decide how to handle the element. If the OPTIONAL flag
+ // is active, then a mismatch in the first attempted match is
+ // accepted, but that will clear the OPTIONAL flag. If the
+ // OPTOUT flag is active, then everything happes as it would
+ // normally happen, except that _STORE always stores NULL
+ // values. If the CHOICE flag is active, then a match is
+ // processed and OPTOUT parsing is applied to all remaining
+ // elements in the CHOICE (and the preceding OPTIONAL flag
+ // applies to the whole CHOICE instead of a single element).
+ // This assumes OPTION cannot occur immediately inside CHOICE.
+ cmd = *walk++;
+DPRINTF ("DEBUG: Instruction 0x%02x decodes 0x%02x size %d of %d\n", cmd, tag, len, hlen + len);
+ if (chosen) {
+DPRINTF ("DEBUG: CHOICE was already made\n");
+ // Already matched CHOICE, so don't try matching anymore;
+ // we chase on with optout && optoutsub
+ optoutsub = 1;
+ } else if ((cmd == DER_PACK_ANY) || ((tag ^ cmd) & DER_PACK_MATCHBITS) == 0x00) {
+DPRINTF ("DEBUG: Found a match\n");
+ // We found a match
+ optoutsub = optout; // Hopefully store the value
+ newcrs.derptr += hlen + len; // Skip over match
+ newcrs.derlen -= hlen + len;
+ if (cmd == (DER_PACK_ENTER | DER_TAG_BITSTRING)) {
+ // Check the remainder bits
+ if (*hdrcrs.derptr != 0x00) {
+ errno = EBADMSG;
+ return NULL;
+ }
+ // Skip the remainder bits
+ hdrcrs.derptr++;
+ hdrcrs.derlen--;
+ }
+ if (choice) {
+ // We matched a choice, so skip other choices
+DPRINTF ("DEBUG: Moreover, found a matching choice\n");
+ optoutsub = optout; // For the current element
+ optout = 1; // For following elements
+ chosen = 1; // Bypass match of following
+ }
+ } else if (choice) {
+DPRINTF ("DEBUG: Found a non-matching choice\n");
+ // No match, but CHOICE flag permits that while choosing
+ optoutsub = 1; // suppress value copy for current elem
+ } else if (optional) {
+DPRINTF ("DEBUG: Mismatch forgiven because we're doing optional recognition\n");
+ // No match, but OPTIONAL flag permits that once
+ optoutsub = 1; // suppress value copy for current elem
+ } else {
+DPRINTF ("ERROR: Mismatch in neither CHOICE nor OPTIONAL decoding parts\n");
+ // No match and nothing helped to make that acceptable
+ errno = EBADMSG;
+ return NULL;
+ }
+ //
+ // Now see if we need to ENTER a substructure. If so, we will
+ // use optoutsub for optout. We never pass CHOICE because we
+ // are past the choosing tag, and we also do not pass OPTIONAL
+ // because that applied to the present tag. The cursor passed
+ // is newcrs, which is then also updated and later copied to crs.
+ if (cmd & DER_PACK_ENTER) {
+ newcrs = hdrcrs;
+ if (cmd == (DER_PACK_ENTER | DER_TAG_BITSTRING)) {
+ if (*newcrs.derptr++ != 0x00) {
+ errno = EBADMSG;
+ return NULL;
+ }
+ newcrs.derlen--;
+ }
+DPRINTF ("DEBUG: Making recursive call because of ENTER bit with rest %d\n", newcrs.derlen);
+ walk = der_unpack_rec (&newcrs, walk,
+ outarray, outctr,
+ 0, 0, optoutsub);
+ if (walk == NULL) {
+ return NULL;
+ }
+DPRINTF ("DEBUG: Ended recursive call because of ENTER bit\n");
+ //
+ // The alternative to _ENTER is to _STORE the current value.
+ // Whether we actually do that, or store a NULL value instead,
+ // is determined by the opt-out choice for the current element,
+ // so by optoutsub.
+ //
+ // Even when we retry the same DER code to another walking step,
+ // then we want to store the walk-guided syntax component.
+ } else if (optoutsub) {
+ // We opt out on this elem, so we store a NULL cursor
+DPRINTF ("DEBUG: Opting out of output value #%d, setting it to NULL cursor\n", *outctr);
+ memset (outarray + (*outctr)++,
+ 0,
+ sizeof (dercursor));
+ } else {
+ // We store the DER value found
+DPRINTF ("DEBUG: Storing output value #%d with %d bytes 0x%02x, 0x%02x, 0x%02x, ...\n", *outctr, crs->derlen, crs->derptr [0], crs->derptr [1], crs->derptr [2]);
+ //TODO:COUNTDOWNLENGTHS// outarray [ (*outctr)++ ] = *crs;
+ if (cmd == DER_PACK_ANY) {
+ outarray [ (*outctr) ].derptr = crs->derptr;
+ outarray [ (*outctr) ].derlen = hlen + len;
+ } else {
+ outarray [ (*outctr) ].derptr = hdrcrs.derptr;
+ outarray [ (*outctr) ].derlen = len;
+ }
+ (*outctr)++;
+ }
+ //
+ // If this is not a CHOICE, then any OPTIONAL flag is cleared;
+ // the prefix serves at most one element, although that can be
+ // extended with the _ENTER flag to a range of elements. But
+ // at this point, the use of the OPTIONAL bit has played out.
+ if (!choice) {
+ optional = 0;
+ }
+ //
+ // Update the visible DER cursor, making it either go back to
+ // what we just tried to match or advance to the next DER element
+ *crs = newcrs;
+DPRINTF ("DEBUG: Considering another loop-around for 0x%02x on 0x%02x with %d left\n", terminal, *walk, crs->derlen);
+ }
+DPRINTF ("DEBUG: Ended looping around for 0x%02x with %d left\n", terminal, crs->derlen);
+ //
+ // Skip past the detected terminal on the walk
+ walk++;
+ //
+ // If this is a CHOICE and it is not OPTIONAL, then failure if all
+ // attempted matches failed. Note that OPTOUT is another matter; it
+ // details surrounding OPTIONALs, which are not of influence on
+ // the CHOICE being subjected to a local OPTIONAL prefix.
+ // Note that the choice flag is cleared as soon as it matches.
+ if (choice && (!chosen) && (!optional)) {
+DPRINTF ("ERROR: Ended a CHOICE without choosing, even though it is not OPTIONAL\n");
+ errno = EBADMSG;
+ return NULL;
+ }
+ //
+ // It is also an error if we were looping until DER_PACK_LEAVE but
+ // we did not actually run into the end of the DER encoding.
+#if 0
+ //TODO// This is not working because there may be surroundings continuing
+ if ((terminal == DER_PACK_LEAVE) && (crs->derlen != 0)) {
+ errno = EBADMSG;
+ return NULL;
+ }
+#endif
+ //
+ // Properly ended with DER_PACK_LEAVE, so report success
+DPRINTF ("DEBUG: Leaving der_unpack_rec() at 0x%08llx\n", (intptr_t) walk);
+ return walk;
+}
+
+
+/* Unpack a structure, or possibly a sequence of structures. The output
+ * is stored in subsequent entries of outarray, whose size should be
+ * precomputed to sufficient length. The outarray will often be an
+ * overlay for a structure composed of dercursor elements with labels
+ * and nesting derived from ASN.1 syntax, and matching an (un)packing walk.
+ *
+ * The syntax is supplied without a length; proper formatting of the syntax
+ * is assumed, that is the number of DER_PACK_ENTER bits should be followed
+ * by an equal amount of DER_PACK_LEAVE instructions, and the choice
+ * markers DER_PACK_CHOICE_BEGIN ... DER_PACK_CHOICE_END must be properly
+ * nested with those instructions and with each other. There is no
+ * protection for foolish specifications (and they will often be generated
+ * anyway). This method additionally requires the first element in the
+ * syntax to be flagged with DER_PACK_ENTER. (TODO: Permit non-looped use?)
+ *
+ * The cursor will be updated by this call to point to the position
+ * where unpacking stopped. Refer to the return value to see if this is
+ * an error position. The function returns 0 on success and -1 on failure.
+ * Upon failure, errno is also set, namely to EBADMSG for syntax problems
+ * or ERANGE for lengths or tags that are out of the supported range of
+ * this implementation.
+ */
+int der_unpack (dercursor *crs, const derwalk *syntax,
+ dercursor *outarray, int repeats) {
+ int outctr = 0;
+ if ((*syntax & DER_PACK_ENTER) == 0x00) {
+ errno = EBADMSG;
+ return -1;
+ }
+ while (repeats-- > 0) {
+ if (der_unpack_rec (crs, syntax,
+ outarray, &outctr,
+ 0, 0, 0) == NULL) {
+ return -1;
+ }
+ }
+ return 0;
+}
--- /dev/null
+#include <quick-der/api.h>
+
+
+/* Update a cursor expression by walking into a DER-encoded ASN.1 structure.
+ * The return value is -1 on error, and errno will be set accordinly, and the
+ * cursor will not have been updated. Otherwise, the return value is the number
+ * of unprocessed bytes on the path, so 0 when the entire path was processed.
+ * The count as non-error returns, so the cursor is updated. Values higher than
+ * 0 indicate where in the path a tag could not be found; this may be helpful in
+ * learning about the structure that was being parsed, for example that an
+ * OPTIONAL or CHOICE part was absent from the DER bytes.
+ *
+ * Paths are sequence of one-byte choices to be made. These choices are tags,
+ * because these are used by ASN.1 to decide on parsing choices to be made.
+ * The one difference is the interpretation of the Primitive/Constructed bit: when
+ * this is set to Primitive, the value will be skipped (even if it is Constructed)
+ * and when set to Constructed, the value will be entered and interpreted as ASN.1
+ * (even when it is setup as Primitive).
+ *
+ * In all the places where ASN.1 defines choices, such as CHOICE or OPTIONAL,
+ * it enforces distinct tags from the various choices. This can be used in a path
+ * to skip such unknown parts in the encoding.
+ *
+ * When entering a BIT STRING, special treatment is implemented; the remaining
+ * bits will have to be zero, and these are then skipped while entering the
+ * remainder. Note that this ensures that the byte-aligned DER structures are
+ * properly packed into a bit-aligned BIT STRING container.
+ */
+int der_walk (dercursor *crs, const derwalk *path) {
+ size_t len;
+ uint8_t hlen;
+ uint8_t tag;
+ dercursor intcrs = *crs;
+ int retval;
+ int optional = 0;
+ int choice = 0;
+ while (*path != DER_WALK_END) {
+ // see if a prefix signals optionality
+ if (*path == DER_WALK_OPTIONAL) {
+ optional = 1; // For the duration of processing the following *path
+ path++;
+ if ((*path == DER_WALK_END) || (*path == DER_WALK_OPTIONAL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ // see if the current path element is a choice
+ // between unknown elements that should be
+ // skipped (except when it is optional, in which
+ // case a match should be attempted)
+ if (*path == DER_WALK_CHOICE) {
+ choice = 1;
+ // Special case: we advance beyond the path, so that we can check
+ // the following element if the CHOICE is OPTIONAL
+ path++;
+ if ((*path == DER_WALK_END) || (*path == DER_WALK_CHOICE) || (*path == DER_WALK_OPTIONAL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ if (intcrs.derlen < 2) {
+ if (intcrs.derlen == 0) {
+ // Empty, so the path resolved only partially
+ break;
+ }
+ // Something is wrong with the DER formatting
+ errno = EBADMSG;
+ return -1;
+ }
+ if (der_header (&intcrs, &tag, &len, &hlen)) {
+ return -1;
+ }
+ // Now test if the tag matches that of the path;
+ // choice and optional flags are processed and
+ // make use of the ASN.1 guarantee that the
+ // first tag that hits us is decisive on how to
+ // proceed. Although this may look loose, it is
+ // in fact very accurate parsing, albeit that the
+ // correctness is verified lazily, that is, only
+ // inasfar as it is needed for the requested path.
+ if (choice && !optional) {
+ // this is just something unknown to skip;
+ // that would even be true if it matched,
+ // because the matching is deferred to
+ // the next path element.
+ intcrs.derptr += len;
+ intcrs.derlen -= len;
+ // the choice was matched; we already
+ // skipped to next path item for choice==1
+ } else if (((tag ^ *path) & DER_WALK_MATCHBITS) == 0x00) {
+ // matched: now either enter of skip
+ if ((*path) & DER_WALK_ENTER) {
+ if (tag == (DER_WALK_ENTER | DER_TAG_BITSTRING)) {
+ intcrs.derptr++;
+ len--;
+ }
+ intcrs.derlen = len;
+ } else {
+ // not DER_WALK_ENTER, so DER_WALK_SKIP
+ intcrs.derptr += len;
+ intcrs.derlen -= len;
+ }
+ // matched: skip to next path item
+ // if we had choice==1 and optional==1,
+ // then we matched the part after the CHOICE
+ // and so this applies in that case too.
+ path++;
+ } else if (optional) {
+ // not matched the optional part: skip the data
+ // and try the path element after the optional
+ intcrs.derptr += len;
+ intcrs.derlen -= len;
+ // if the optional part was a choice, then we
+ // are already at the next part and we are
+ // now skipping the choice, for which we
+ // already advanced path. if the optional
+ // part was not a choice, then path is at the
+ // optional part, and we do need to skip that
+ if (!choice) {
+ path++;
+ }
+ } else {
+ // not matched and not optional:
+ // this is a parsing error
+ errno = EBADMSG;
+ return -1;
+ }
+ // Forget any optional flags for the processed *path
+ optional = 0;
+ choice = 0;
+ }
+ // Return 0 when done, or >0 with the remaining pathlen otherwise
+ *crs = intcrs;
+ retval = 0;
+ while (path [retval] != DER_WALK_END) {
+ retval++;
+ }
+ return retval;
+}
--- /dev/null
+// Combine all the .c parts into one file
+
+#include <quick-der/api.h>
+
+/* INLINE FUNCTION #include "der_isconstructed.c" */
+/* INLINE FUNCTION #include "der_isprimitive.c" */
+/* INLINE FUNCTION #include "der_isnonempty.c" */
+/* INLINE FUNCTION #include "der_isnull.c" */
+#include "der_header.c"
+#include "der_walk.c"
+#include "der_skipenter.c"
+#include "der_unpack.c"
+#include "der_iterate.c"
+#include "der_pack.c"
+#include "der_prepack.c"
--- /dev/null
+TESTS = certio.ok # kxover.ok krb5ticket.ok
+
+LIBDIR = ../lib
+LIBQD = libquickder.a
+
+all: $(LIBDIR)/$(LIBQD) $(TESTS)
+
+clean:
+ for t in $(TESTS) ; do rm -f $$t $${t%.ok}.test ; done
+
+$(LIBDIR)/$(LIBQD):
+ make -C "$(LIBDIR)" "$(LIBQD)"
+
+install:
+ @echo #
+ @echo # The test directory constrains building, but does not install anything
+ @echo #
+
+uninstall:
+
+
+%.test: %.c $(LIBDIR)/$(LIBQD)
+ gcc -I ../include -o "$@" "$<" "$(LIBDIR)/$(LIBQD)"
+
+%.ok: %.test
+ "./$<" && touch "$@"
+
+certio.ok: certio.test
+ ./certio.test verisign.der
+ touch "$@"
+
+kxover.ok: kxover.test
+ ./kxover.test
+
--- /dev/null
+/* Test the DER parser by exploring a certificate */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <quick-der/api.h>
+
+
+// Certificate ::= SEQUENCE {
+// tbsCertificate TBSCertificate,
+// signatureAlgorithm AlgorithmIdentifier,
+// signatureValue BIT STRING }
+//
+// TBSCertificate ::= SEQUENCE {
+// version [0] EXPLICIT Version DEFAULT v1,
+// serialNumber CertificateSerialNumber,
+// signature AlgorithmIdentifier,
+// issuer Name,
+// validity Validity,
+// subject Name,
+// subjectPublicKeyInfo SubjectPublicKeyInfo,
+// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+// -- If present, version MUST be v2 or v3
+// extensions [3] EXPLICIT Extensions OPTIONAL
+// -- If present, version MUST be v3
+// }
+//
+// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+//
+// Extension ::= SEQUENCE {
+// extnID OBJECT IDENTIFIER,
+// critical BOOLEAN DEFAULT FALSE,
+// extnValue OCTET STRING
+// -- contains the DER encoding of an ASN.1 value
+// -- corresponding to the extension type identified
+// -- by extnID
+// }
+
+
+
+
+/* The syntax parser, as it should be auto-generated one day.
+ *
+ * The parser enters as much of the structure as it can, basically taking
+ * away anything that contains DER structure. This stops ANY or ANY DEFINED BY
+ * is used to describe a field's contents in a variable manner; you would have
+ * to continue parsing yourself. Similarly, when an OCTET STRING or BIT STRING
+ * has DER-formatted contents that might vary. When there is no variation,
+ * you might actually ENTER these structures; note that special processing for
+ * the BIT STRING then demands 0 remainder bits, after which it will enter one;
+ * position after the head to skip the remainder bit count.
+ */
+
+derwalk pack_Certificate[] = {
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // Certificate SEQUENCE
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // TBSCertificate SEQUENCE
+ DER_PACK_OPTIONAL, // version is optional
+ DER_PACK_ENTER | DER_TAG_CONTEXT (0), // [0] EXPLICIT
+ DER_PACK_STORE | DER_TAG_INTEGER, // version
+ DER_PACK_LEAVE, // [0] EXPLICIT
+ DER_PACK_STORE | DER_TAG_INTEGER, // serialNumber
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // signature AlgIdentif
+ DER_PACK_STORE | DER_TAG_OID, // algorithm OID
+ DER_PACK_ANY, // parameters ANY DEFINED BY
+ DER_PACK_LEAVE, // signature AlgIdentif
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // issuer Name (varsized)
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // validity SEQUENCE
+ DER_PACK_CHOICE_BEGIN, // utctime | genlztime
+ DER_PACK_STORE | DER_TAG_UTCTIME, // alt :- UTCtime
+ DER_PACK_STORE | DER_TAG_GENERALIZEDTIME, // alt :- GeneralizedTime
+ DER_PACK_CHOICE_END, // validity Validity
+ DER_PACK_CHOICE_BEGIN, // utctime | genlztime
+ DER_PACK_STORE | DER_TAG_UTCTIME, // alt :- UTCtime
+ DER_PACK_STORE | DER_TAG_GENERALIZEDTIME, // alt :- GeneralizedTime
+ DER_PACK_CHOICE_END, // Validity
+ DER_PACK_LEAVE, // validity SEQUENCE
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // subject Name (varsized)
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // subjectPublicKeyInfo
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // algorithmIdentifier
+ DER_PACK_STORE | DER_TAG_OID, // algorithm
+ DER_PACK_ANY, // parameters ANY DEFINED BY
+ DER_PACK_LEAVE, // algorithmIdentifier
+ DER_PACK_STORE | DER_TAG_BITSTRING, // subjectPublicKey
+ DER_PACK_LEAVE, // subjectPublicKeyInfo
+ DER_PACK_OPTIONAL, // issuerUniqueID [1]
+ DER_PACK_STORE | DER_TAG_CONTEXT (1), // [1] IMPLICIT BITSTRING
+ DER_PACK_OPTIONAL, // subjectUniqueID [2]
+ DER_PACK_STORE | DER_TAG_CONTEXT (2), // [2] IMPLICIT BITSTRING
+ DER_PACK_OPTIONAL, // extensions [3]
+ DER_PACK_ENTER | DER_TAG_CONTEXT (3), // [3] EXPLICIT
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // SEQUENCE OF Extension
+ DER_PACK_LEAVE, // [3] EXPLICIT
+ DER_PACK_LEAVE, // TBSCertificate SEQUENCE
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // sigAlg AlgIdentifier
+ DER_PACK_STORE | DER_TAG_OID, // algorithm
+ DER_PACK_ANY, // parameters ANY DEFINED BY
+ DER_PACK_LEAVE, // sigAlg AlgIdentifier
+ DER_PACK_STORE | DER_TAG_BITSTRING, // signatureValue BITSTR
+ DER_PACK_LEAVE, // Certificate SEQUENCE
+ DER_PACK_END // stop after SEQUENCE
+};
+
+derwalk pack_Extension [] = {
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // Extension ::= SEQUENCE {
+ DER_PACK_STORE | DER_TAG_OID, // extnID
+ DER_PACK_OPTIONAL,
+ DER_PACK_STORE | DER_TAG_BOOLEAN, // critical
+ DER_PACK_STORE | DER_TAG_OCTETSTRING, // extnValue
+ DER_PACK_LEAVE, // }
+ DER_PACK_END
+};
+
+/* Overlay structures, as they should be auto-generated some day */
+struct ovly_Time {
+ dercursor utcTime;
+ dercursor generalTime;
+};
+
+struct ovly_Validity {
+ struct ovly_Time notBefore;
+ struct ovly_Time notAfter;
+};
+
+struct ovly_AlgorithmIdentifier {
+ dercursor algorithm;
+ dercursor parameters;
+};
+
+struct ovly_SubjectPublicKeyInfo {
+ struct ovly_AlgorithmIdentifier algorithm;
+ dercursor subjectPublicKey;
+};
+
+struct ovly_TBSCertificate {
+ dercursor version;
+ dercursor serialNumber;
+ struct ovly_AlgorithmIdentifier signature;
+ dercursor issuer;
+ struct ovly_Validity validity;
+ dercursor subject;
+ struct ovly_SubjectPublicKeyInfo subjectPublicKeyInfo;
+ dercursor issuerUniqueID;
+ dercursor subjectUniqueID;
+ dercursor extensions;
+};
+
+struct ovly_Certificate {
+ struct ovly_TBSCertificate tbsCertificate;
+ struct ovly_AlgorithmIdentifier signatureAlgorithm;
+ dercursor signatureValue;
+};
+
+struct ovly_Extension {
+ dercursor extnID;
+ dercursor critical;
+ dercursor extnValue;
+};
+
+derwalk path_rdn2type[] = {
+ DER_WALK_ENTER | DER_TAG_SET, // SET OF AttributeTypeAndValue
+ DER_WALK_ENTER | DER_TAG_SEQUENCE, // SEQUENCE { type, value }
+ DER_WALK_ENTER | DER_TAG_OID, // type OBJECT IDENTIFIER
+ DER_WALK_END
+};
+
+derwalk path_rdn2value[] = {
+ DER_WALK_ENTER | DER_TAG_SET, // SET OF AttributeTypeAndValue
+ DER_WALK_ENTER | DER_TAG_SEQUENCE, // SEQUENCE { type, value }
+ DER_WALK_SKIP | DER_TAG_OID, // type OBJECT IDENTIFIER
+ // value ANY DEFINED BY type
+ DER_WALK_END
+};
+
+void print_oid (dercursor *oid) {
+ size_t oidlen = oid->derlen;
+ uint8_t *oidptr = oid->derptr;
+ uint32_t nextoid = 0;
+ if (oidlen < 1) {
+ printf ("BAD_OID");
+ return;
+ } else {
+ printf ("%d.%d", (*oidptr) / 40, (*oidptr) % 40);
+ }
+ oidlen--;
+ oidptr++;
+ while (oidlen > 0) {
+ nextoid <<= 7;
+ nextoid |= (*oidptr) & 0x7f;
+ if ((*oidptr & 0x80) == 0x00) {
+ printf (".%d", nextoid);
+ nextoid = 0;
+ }
+ oidptr++;
+ oidlen--;
+ }
+ if (nextoid != 0x00) {
+ printf (".LEFTOVER_%d", nextoid);
+ }
+}
+
+void hexdump (dercursor *crs) {
+ dercursor here = *crs;
+ while (here.derlen-- > 0) {
+ printf (" %02x", *here.derptr++);
+ }
+}
+
+int main (int argc, char *argv []) {
+ int inf, otf;
+ uint8_t buf [65537];
+ size_t buflen;
+ dercursor crs;
+ dercursor iter;
+ dercursor rdn;
+ dercursor ext;
+ struct ovly_Certificate certificate;
+ struct ovly_Extension extension;
+ int prsok;
+ size_t rebuildlen;
+ int i;
+
+ memset (&certificate, 0x5A, sizeof (certificate));
+ memset (&extension, 0x5A, sizeof (extension));
+ if ((argc < 2) || (argc > 3)) {
+ printf ("Usage: %s certfile.der [rebuildfile.der]\n", argv [0]);
+ exit (1);
+ }
+ inf = open (argv [1], O_RDONLY);
+ if (inf < 0) {
+ fprintf (stderr, "Failed to open %s\n", argv [1]);
+ close (inf);
+ exit (1);
+ }
+ buflen = read (inf, buf, sizeof (buf));
+ close (inf);
+ if ((buflen == -1) || (buflen == 0)) {
+ fprintf (stderr, "Failed to read from %s\n", argv [1]);
+ exit (1);
+ }
+ if (buflen == sizeof (buf)) {
+ fprintf (stderr, "Certificate in %s too large\n", argv [1]);
+ exit (1);
+ }
+ printf ("Parsing %zu bytes from %s\n", buflen, argv [1]);
+ crs.derptr = buf;
+ crs.derlen = buflen;
+ prsok = der_unpack (&crs, pack_Certificate, (dercursor *) &certificate, 1);
+ switch (prsok) {
+ case -1:
+ perror ("Failed to unpack certificate");
+ exit (1);
+ case 0:
+ // printf ("Parsing OK, found %zu bytes worth of subject data at 0x%016llx\n", crs.derlen, (uint64_t) crs.derptr);
+ printf ("Detailed parsing OK for this Certificate\n");
+ break;
+ }
+
+ if (der_isnull (&certificate.tbsCertificate.version)) {
+ printf ("No version set (defaults to v1)\n");
+ } else {
+ printf ("Version is set to v%d\n", 1 + *certificate.tbsCertificate.version.derptr);
+ }
+
+ printf ("Serial number: ");
+ hexdump (&certificate.tbsCertificate.serialNumber);
+ printf ("\n");
+
+ crs = certificate.tbsCertificate.issuer;
+ printf ("There are %d RDNs in the issuer:\n", der_countelements (&crs));
+ if (der_iterate_first (&crs, &iter)) do {
+ // printf ("Iterator now at 0x%016llx spanning %zu\n", (uint64_t) iter.derptr, iter.derlen);
+ // printf ("Iterator tag,len is 0x%02x,0x%02x\n", iter.derptr [0], iter.derptr [1]);
+ rdn = iter;
+ der_walk (&rdn, path_rdn2type);
+ // printf ("RDNcursor #1 tag,len is 0x%02x,0x%02x,0x%02x\n", rdn.derptr [0], rdn.derptr [1],rdn.derptr [2]);
+ print_oid (&rdn);
+ rdn = iter;
+ der_walk (&rdn, path_rdn2value);
+ der_enter (&rdn); // Enter whatever DirectoryString it is
+ // printf ("RDNcursor #2 tag,len is 0x%02x,0x%02x", rdn.derptr [0], rdn.derptr [1]);
+ printf (" = \"%.*s\"\n", rdn.derlen, rdn.derptr);
+ } while (der_iterate_next (&iter));
+
+ crs = der_isnull (&certificate.tbsCertificate.validity.notBefore.utcTime)?
+ certificate.tbsCertificate.validity.notBefore.generalTime:
+ certificate.tbsCertificate.validity.notBefore.utcTime;
+ printf ("Validity.notBefore: %.*s\n", crs.derlen, crs.derptr);
+ crs = der_isnull (&certificate.tbsCertificate.validity.notAfter.utcTime)?
+ certificate.tbsCertificate.validity.notAfter.generalTime:
+ certificate.tbsCertificate.validity.notAfter.utcTime;
+ printf ("Validity.notAfter: %.*s\n", crs.derlen, crs.derptr);
+
+ crs = certificate.tbsCertificate.subject;
+ printf ("There are %d RDNs in the subject:\n", der_countelements (&crs));
+ if (der_iterate_first (&crs, &iter)) do {
+ // printf ("Iterator now at 0x%016llx spanning %zu\n", (uint64_t) iter.derptr, iter.derlen);
+ // printf ("Iterator tag,len is 0x%02x,0x%02x\n", iter.derptr [0], iter.derptr [1]);
+ rdn = iter;
+ der_walk (&rdn, path_rdn2type);
+ // printf ("RDNcursor #1 tag,len is 0x%02x,0x%02x,0x%02x\n", rdn.derptr [0], rdn.derptr [1],rdn.derptr [2]);
+ print_oid (&rdn);
+ rdn = iter;
+ der_walk (&rdn, path_rdn2value);
+ der_enter (&rdn); // Enter whatever DirectoryString it is
+ // printf ("RDNcursor #2 tag,len is 0x%02x,0x%02x", rdn.derptr [0], rdn.derptr [1]);
+ printf (" = \"%.*s\"\n", rdn.derlen, rdn.derptr);
+ } while (der_iterate_next (&iter));
+
+ printf ("Subject Public Key AlgorithmIdentifier: ");
+ crs = certificate.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm;
+ print_oid (&crs);
+ printf ("\n ");
+ crs = certificate.tbsCertificate.subjectPublicKeyInfo.algorithm.parameters;
+ hexdump (&crs);
+ printf ("\n ");
+ crs = certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey;
+ der_enter (&crs);
+ hexdump (&crs);
+ printf ("\n");
+
+ crs = certificate.tbsCertificate.issuerUniqueID;
+ if (!der_isnull (&crs)) {
+ printf ("Issuer Unique ID:");
+ hexdump (&crs);
+ printf ("\n");
+ }
+
+ crs = certificate.tbsCertificate.subjectUniqueID;
+ if (!der_isnull (&crs)) {
+ printf ("Subject Unique ID:");
+ hexdump (&crs);
+ printf ("\n");
+ }
+
+ crs = certificate.tbsCertificate.extensions;
+ printf ("There are %d extensions:\n", der_countelements (&crs));
+ if (der_iterate_first (&crs, &iter)) do {
+ // printf ("Iterator now at 0x%016llx spanning %zu\n", (uint64_t) iter.derptr, iter.derlen);
+ // printf ("Iterator tag,len is 0x%02x,0x%02x\n", iter.derptr [0], iter.derptr [1]);
+ ext = iter;
+printf ("Extension size %zd bytes %02x %02x %02x %02x\n", ext.derlen, ext.derptr[0], ext.derptr[1], ext.derptr[2], ext.derptr[3]);
+ prsok = der_unpack (&ext, pack_Extension, (dercursor *) &extension, 1);
+ if (prsok != 0) {
+ fprintf (stderr, "Failed to parse extension (%d)\n", prsok);
+ continue;
+ }
+ printf ("Extension OID: ");
+ print_oid (&extension.extnID);
+ printf ("\nExtension critical: ");
+ if (der_isnull (&extension.critical)) {
+ printf ("FALSE (DEFAULT)\n");
+ //TODO// Would be nice to have a DER boolean test macro / function
+ } else if ((extension.critical.derlen > 0) && (*extension.critical.derptr)) {
+ printf ("TRUE\n");
+ } else {
+ printf ("FALSE\n");
+ }
+ printf ("Extension contents:");
+ hexdump (&extension.extnValue);
+ printf ("\n");
+ } while (der_iterate_next (&iter));
+
+ //
+ // Print the elemental length of the 16 certificate elements
+ for (i=0; i<16; i++) {
+ printf ("certificate [%2d].derlen = %zd\n", i, ((dercursor *) &certificate) [i].derlen);
+ }
+
+ //
+ // Determine the length for re-composition
+ rebuildlen = der_pack (pack_Certificate, (dercursor *) &certificate, NULL);
+ if (rebuildlen != DER_DERLEN_ERROR) {
+ printf ("To rebuild, we would need %zd bytes\n", rebuildlen);
+ } else {
+ fprintf (stderr, "Unable to determine the rebuild size for this certificate\n");
+ exit (1);
+ }
+
+ //
+ // Recompose the certificate for an output file if argv [2] has been defined
+ if (argc == 3) {
+ //
+ // Construct the output certificate in a new buffer
+ uint8_t rebuildbuf [rebuildlen]; // Daring: dynamic stack allocation
+ der_pack (pack_Certificate, (dercursor *) &certificate, rebuildbuf + rebuildlen);
+ printf ("TOTAL: Wrote %4d bytes to 0x%016llx\n", rebuildlen, rebuildbuf);
+ //
+ // Save the rebuilt certificate to the named file
+ otf = open (argv [2], O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (otf < 0) {
+ fprintf (stderr, "Failed to create output file %s\n", argv [2]);
+ exit (1);
+ }
+ if (write (otf, rebuildbuf, rebuildlen) != rebuildlen) {
+ fprintf (stderr, "Not all of the %d bytes have been written to %s\n", rebuildlen, argv [2]);
+ close (otf);
+ exit (1);
+ }
+ close (otf);
+ //
+ // Compare the new certificate to the original one
+ if (rebuildlen != buflen) {
+ fprintf (stderr, "The rebuilt certificate is of a different size than the input\n");
+ exit (1);
+ }
+ if (memcmp (buf, rebuildbuf, buflen) != 0) {
+ fprintf (stderr, "The rebuilt certificate differs from the one input\n");
+ exit (1);
+ }
+ }
+
+ return 0;
+}
+
--- /dev/null
+
+#include <stdlib.h>
+
+
+#include <quick-der/api.h>
+
+
+
+/*
+ * Ticket ::= [APPLICATION 1] SEQUENCE {
+ * tkt-vno [0] INTEGER (5),
+ * realm [1] Realm,
+ * sname [2] PrincipalName,
+ * enc-part [3] EncryptedData -- EncTicketPart
+ * }
+ *
+ * Realm ::= KerberosString
+ *
+ * PrincipalName ::= SEQUENCE {
+ * name-type [0] Int32,
+ * name-string [1] SEQUENCE OF KerberosString
+ * }
+ *
+ * EncryptedData ::= SEQUENCE {
+ * etype [0] Int32 -- EncryptionType --,
+ * kvno [1] UInt32 OPTIONAL,
+ * cipher [2] OCTET STRING -- ciphertext
+ * }
+ *
+ * EncTicketPart ::= [APPLICATION 3] SEQUENCE {
+ * flags [0] TicketFlags,
+ * key [1] EncryptionKey,
+ * crealm [2] Realm,
+ * cname [3] PrincipalName,
+ * transited [4] TransitedEncoding,
+ * authtime [5] KerberosTime,
+ * starttime [6] KerberosTime OPTIONAL,
+ * endtime [7] KerberosTime,
+ * renew-till [8] KerberosTime OPTIONAL,
+ * caddr [9] HostAddresses OPTIONAL,
+ * authorization-data [10] AuthorizationData OPTIONAL
+ * }
+ *
+ * TicketFlags ::= KerberosFlags
+ *
+ * EncryptionKey ::= SEQUENCE {
+ * keytype [0] Int32 -- actually encryption type --,
+ * keyvalue [1] OCTET STRING
+ * }
+ *
+ * TransitedEncoding ::= SEQUENCE {
+ * tr-type [0] Int32 -- must be registered --,
+ * contents [1] OCTET STRING
+ * }
+ *
+ * KerberosTime ::= GeneralizedTime -- with no fractional seconds
+ *
+ * AuthorizationData ::= SEQUENCE OF SEQUENCE {
+ * ad-type [0] Int32,
+ * ad-data [1] OCTET STRING
+ * }
+ *
+ */
+
+derwalk pack_Ticket [] = {
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // Ticket
+ DER_PACK_ENTER | DER_TAG_CONTEXT (0), // [0] tkt-vno
+ DER_PACK_STORE | DER_TAG_INTEGER, // tkt-vno (5)
+ DER_PACK_LEAVE, // [0] tkt-vno
+ DER_PACK_ENTER | DER_TAG_CONTEXT (1), // [1] realm
+ DER_PACK_STORE | DER_TAG_GENERALSTRING, // realm
+ DER_PACK_LEAVE, // [1] realm
+ DER_PACK_ENTER | DER_TAG_CONTEXT (2), // [2] sname
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // sname
+ DER_PACK_ENTER | DER_TAG_CONTEXT (0), // [0] name-type
+ DER_PACK_STORE | DER_TAG_INTEGER, // name-type Int32
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // SEQUENCE OF GeneralString
+ DER_PACK_LEAVE, // [0] name-type
+ DER_PACK_LEAVE, // sname
+ DER_PACK_LEAVE, // [2] sname
+ DER_PACK_ENTER | DER_TAG_CONTEXT (3), // [3] enc-part
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // enc-part
+ DER_PACK_ENTER | DER_TAG_CONTEXT (0), // [0] etype
+ DER_PACK_STORE | DER_TAG_INTEGER, // etype Int32
+ DER_PACK_LEAVE, // [0] etype
+ DER_PACK_OPTIONAL, // kvno OPTIONAL
+ DER_PACK_ENTER | DER_TAG_CONTEXT (1), // [1] kvno
+ DER_PACK_STORE | DER_TAG_INTEGER, // kvno Uint32
+ DER_PACK_LEAVE, // [1] kvno
+ DER_PACK_ENTER | DER_TAG_CONTEXT (2), // [2] cipher
+ DER_PACK_STORE | DER_TAG_OCTETSTRING, // cipher [2] OCTETSTRING
+ DER_PACK_LEAVE, // [2] cipher
+ DER_PACK_LEAVE, // enc-part
+ DER_PACK_LEAVE, // [3] enc-part
+ DER_PACK_LEAVE, // Ticket
+ DER_PACK_END
+};
+
+
+derwalk pack_EncTicketPart [] = {
+ DER_PACK_ENTER | DER_TAG_APPLICATION (3), // EncTicketPart [APPL 3]
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // [APPL 3] SEQUENCE {
+ DER_PACK_ENTER | DER_TAG_CONTEXT (0), // [0] TicketFlags
+ DER_PACK_STORE | DER_TAG_INTEGER, // TicketFlags
+ DER_PACK_LEAVE, // [0] TicketFlags
+ DER_PACK_ENTER | DER_TAG_CONTEXT (1), // [1] EncryptionKey
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // SEQUENCE {
+ DER_PACK_STORE | DER_TAG_INTEGER, // keytype
+ DER_PACK_STORE | DER_TAG_OCTETSTRING, // keyvalue
+ DER_PACK_LEAVE, // SEQUENCE }
+ DER_PACK_LEAVE, // [1] EncryptionKey
+ DER_PACK_ENTER | DER_TAG_CONTEXT (2), // [2] Realm
+ DER_PACK_STORE | DER_TAG_GENERALSTRING, // Realm ::= GeneralString
+ DER_PACK_LEAVE, // [2] Realm
+ DER_PACK_ENTER | DER_TAG_CONTEXT (3), // [3] PrincipalName
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // PrincipalName SEQUENCE {
+ DER_PACK_STORE | DER_TAG_INTEGER, // name-type
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // SEQUENCE OF GeneralString
+ DER_PACK_LEAVE, // SEQUENCE } PrincipalName
+ DER_PACK_LEAVE, // [3] PrincipalName
+ DER_PACK_ENTER | DER_TAG_CONTEXT (4), // [4] TransitedEncoding
+ DER_PACK_ENTER | DER_TAG_SEQUENCE, // TransEnc ::= SEQUENCE {
+ DER_PACK_ENTER | DER_TAG_CONTEXT (0), // [0] tr-type
+ DER_PACK_STORE | DER_TAG_INTEGER, // tr-type
+ DER_PACK_LEAVE, // [0] tr-type
+ DER_PACK_ENTER | DER_TAG_CONTEXT (1), // [1] contents
+ DER_PACK_STORE | DER_TAG_OCTETSTRING, // contents
+ DER_PACK_LEAVE, // [1] contents
+ DER_PACK_LEAVE, // SEQUENCE } TrancEnc
+ DER_PACK_LEAVE, // [4] TransitedEncoding
+ DER_PACK_ENTER | DER_TAG_CONTEXT (5), // [5] authtime
+ DER_PACK_STORE | DER_TAG_GENERALIZEDTIME, // KerberosTime
+ DER_PACK_LEAVE, // [5] authtime
+ DER_PACK_OPTIONAL, // [6] starttime OPTIONAL
+ DER_PACK_ENTER | DER_TAG_CONTEXT (6), // [6] starttime
+ DER_PACK_STORE | DER_TAG_GENERALIZEDTIME, // KerberosTime
+ DER_PACK_LEAVE, // [6] starttime
+ DER_PACK_ENTER | DER_TAG_CONTEXT (7), // [7] endtime
+ DER_PACK_STORE | DER_TAG_GENERALIZEDTIME, // KerberosTime
+ DER_PACK_LEAVE, // [7] endtime
+ DER_PACK_OPTIONAL, // [8] renew-till OPTIONAL
+ DER_PACK_ENTER | DER_TAG_CONTEXT (8), // [8] renew-till
+ DER_PACK_STORE | DER_TAG_GENERALIZEDTIME, // KerberosTime
+ DER_PACK_LEAVE, // [8] renew-till
+ DER_PACK_OPTIONAL, // [9] caddr OPTIONAL
+ DER_PACK_ENTER | DER_TAG_CONTEXT (9), // [9] caddr
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // SEQUENCE OF HostAddress
+ DER_PACK_LEAVE, // [9] caddr
+ DER_PACK_OPTIONAL, // [10] authz-data OPTIONAL
+ DER_PACK_ENTER | DER_TAG_CONTEXT (10), // [10] authz-data
+ DER_PACK_STORE | DER_TAG_SEQUENCE, // SEQUENCE OF SEQUENCE...
+ DER_PACK_LEAVE, // [10] authz-data
+ DER_PACK_LEAVE, // SEQUENCE }
+ DER_PACK_LEAVE, // EncTicketPart [APPL 3]
+ DER_PACK_END
+};
+
+
+struct ovly_PrincipalName {
+ dercursor name_type;
+ dercursor name_string;
+};
+
+struct ovly_EncryptedData {
+ dercursor etype;
+ dercursor kvno;
+ dercursor cipher;
+};
+
+struct ovly_Ticket {
+ dercursor tkt_vno;
+ dercursor realm;
+ struct ovly_PrincipalName sname;
+ struct ovly_EncryptedData enc_part;
+};
+
+
+struct ovly_EncryptionKey {
+ dercursor keytype;
+ dercursor keyvalue;
+};
+
+struct ovly_TransitedEncoding {
+ dercursor tr_type;
+ dercursor contents;
+};
+
+struct ovly_EncTicketPart {
+ dercursor flags;
+ struct ovly_EncryptionKey key;
+ dercursor crealm;
+ struct ovly_TransitedEncoding transited;
+ dercursor authtime;
+ dercursor starttime;
+ dercursor endtime;
+ dercursor renew_till;
+ dercursor caddr;
+ dercursor authorization_data;
+};
+
+
+int main (int argc, char *argv []) {
+ exit (1); // To be implemented
+}
+
--- /dev/null
+/* Test the DER parser by finding the certificates in PKINIT or KXOVER */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <quick-der/api.h>
+
+
+/********** RFC 4556:
+
+ PA-PK-AS-REQ ::= SEQUENCE {
+ signedAuthPack [0] IMPLICIT OCTET STRING,
+ -- Contains a CMS type ContentInfo encoded
+ -- according to [RFC3852].
+ -- The contentType field of the type ContentInfo
+ -- is id-signedData (1.2.840.113549.1.7.2),
+ -- and the content field is a SignedData.
+ -- The eContentType field for the type SignedData is
+ -- id-pkinit-authData (1.3.6.1.5.2.3.1), and the
+ -- eContent field contains the DER encoding of the
+ -- type AuthPack.
+ -- AuthPack is defined below.
+ trustedCertifiers [1] SEQUENCE OF
+ ExternalPrincipalIdentifier OPTIONAL,
+ -- Contains a list of CAs, trusted by the client,
+ -- that can be used to certify the KDC.
+ -- Each ExternalPrincipalIdentifier identifies a CA
+ -- or a CA certificate (thereby its public key).
+ -- The information contained in the
+ -- trustedCertifiers SHOULD be used by the KDC as
+ -- hints to guide its selection of an appropriate
+ -- certificate chain to return to the client.
+ kdcPkId [2] IMPLICIT OCTET STRING
+ OPTIONAL,
+ -- Contains a CMS type SignerIdentifier encoded
+ -- according to [RFC3852].
+ -- Identifies, if present, a particular KDC
+ -- public key that the client already has.
+ ...
+ }
+
+*/
+
+
+/********** RFC 3852:
+
+ ContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ content [0] EXPLICIT ANY DEFINED BY contentType }
+
+ ContentType ::= OBJECT IDENTIFIER
+
+ id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
+
+ SignedData ::= SEQUENCE {
+ version CMSVersion,
+ digestAlgorithms DigestAlgorithmIdentifiers,
+ encapContentInfo EncapsulatedContentInfo,
+ certificates [0] IMPLICIT CertificateSet OPTIONAL,
+ crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
+ signerInfos SignerInfos }
+
+ CertificateSet ::= SET OF CertificateChoices
+
+ CertificateChoices ::= CHOICE {
+ certificate Certificate,
+ extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
+ v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete
+ v2AttrCert [2] IMPLICIT AttributeCertificateV2,
+ other [3] IMPLICIT OtherCertificateFormat }
+
+ OtherCertificateFormat ::= SEQUENCE {
+ otherCertFormat OBJECT IDENTIFIER,
+ otherCert ANY DEFINED BY otherCertFormat }
+
+*/
+
+
+
+derwalk path_KXoverASreq2certificateChoices [] = {
+ DER_WALK_ENTER | DER_TAG_SEQUENCE, // PA-PK-AS-REQ ::= SEQUENCE {...}
+ DER_WALK_ENTER | DER_TAG_CONTEXT (0), // signedAuthPack [0] IMPLICIT
+ DER_WALK_ENTER | DER_TAG_SEQUENCE, // ContentInfo ::= SEQUENCE {...}
+ DER_WALK_SKIP | DER_TAG_OID, // contentType OBJECT IDENTIFIER
+ DER_WALK_ENTER | DER_TAG_CONTEXT (0), // content [0] EXPLICIT ANY ...
+ DER_WALK_ENTER | DER_TAG_SEQUENCE, // SignedData ::= SEQUENCE {...}
+ DER_WALK_SKIP | DER_TAG_INTEGER, // version CMSVersion
+ DER_WALK_SKIP | DER_TAG_SET, // digestAlgorithms SET OF ...
+ DER_WALK_SKIP | DER_TAG_SEQUENCE, // encapContentInfo SEQUENCE {...}
+ DER_WALK_END // certificates [0] IMPLICIT
+ // SET OF CertificateChoices (!)
+};
+
+int main (int argc, char *argv []) {
+ int inf;
+ uint8_t buf [65537];
+ size_t buflen;
+ dercursor crs;
+ dercursor iter;
+ int prsok;
+ int outfn = 2;
+ if (argc < 2) {
+ fprintf (stderr, "Usage: %s kxover-as-req.der [outcert0.der outcert1.der ...]\n", argv [0]);
+ exit (1);
+ }
+ inf = open (argv [1], O_RDONLY);
+ if (inf < 0) {
+ fprintf (stderr, "Failed to open %s\n", argv [1]);
+ exit (1);
+ }
+ buflen = read (inf, buf, sizeof (buf));
+ close (inf);
+ if ((buflen == -1) || (buflen == 0)) {
+ fprintf (stderr, "Failed to read from %s\n", argv [1]);
+ exit (1);
+ }
+ if (buflen == sizeof (buf)) {
+ fprintf (stderr, "Certificate in %s too large\n", argv [1]);
+ exit (1);
+ }
+ printf ("Parsing %zu bytes from %s\n", buflen, argv [1]);
+ crs.derptr = buf;
+ crs.derlen = buflen;
+ prsok = der_walk (&crs, path_KXoverASreq2certificateChoices);
+ switch (prsok) {
+ case -1:
+ perror ("Failed to find certificate set in KXOVER AS-Request");
+ exit (1);
+ case 0:
+ printf ("Parsing OK, found %zu bytes worth of certificate set data at 0x%016llx\n", crs.derlen, (uint64_t) crs.derptr);
+ break;
+ default:
+ printf ("Parsing ended with %d bytes left in pattern\n", prsok);
+ exit (1);
+ }
+ printf ("Cursor is now at 0x%016llx spanning %zu\n", (uint64_t) crs.derptr, crs.derlen);
+ if (der_iterate_first (&crs, &iter)) do {
+ printf ("Iterator now at 0x%016llx spanning %zu\n", (uint64_t) iter.derptr, iter.derlen);
+ printf ("Iterator tag,len is 0x%02x,0x%02x\n", iter.derptr [0], iter.derptr [1]);
+ switch (iter.derptr [0] & 0xdf) {
+ case DER_TAG_SEQUENCE:
+ printf ("This is a certificate\n");
+ if (outfn < argc) {
+ int fout = open (argv [outfn], O_WRONLY);
+ if (fout < 0) {
+ fprintf (stderr, "Failed to open %s for writing, skipping it\n", argv [outfn]);
+ } else if (write (fout, iter.derptr, iter.derlen) != iter.derlen) {
+ fprintf (stderr, "Tried to save to %s, but not all bytes may have arrived\n", argv [outfn]);
+ close (fout);
+ } else {
+ close (fout);
+ printf ("Wrote this certificate to %s\n", argv [outfn]);
+ }
+ outfn++;
+ } else {
+ printf ("Provide an extra filename if you want me to save the certificate's DER format\n");
+ }
+ break;
+ case DER_TAG_CONTEXT (0):
+ printf ("This is an extendedCertificate (OBSOLETE)\n");
+ break;
+ case DER_TAG_CONTEXT (1):
+ printf ("This is a v1AttrCert (OBSOLETE)\n");
+ break;
+ case DER_TAG_CONTEXT (2):
+ printf ("This is a v2AttrCert\n");
+ break;
+ case DER_TAG_CONTEXT (3):
+ printf ("This follows an OID-specified OtherCertificateFormat\n");
+ break;
+ }
+ } while (der_iterate_next (&iter));
+ return 0;
+}
+
--- /dev/null
+PREFIX=/usr/local
+
+all:
+
+clean:
+
+install:
+ install -m 0755 hexio/derdump.py "$(PREFIX)/bin/derdump"
+
+uninstall:
+ rm -f "$(PREFIX)/bin/derdump"
+
--- /dev/null
+Subproject commit dcc5f9ca71bde24fd8ad7a47ea86f8bd221b7103