ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
der.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file der.h
6 * @brief ASN.1 Distinguished Encoding Rules: one TLV at a time, over the caller's bytes.
7 *
8 * X.690 sec 10 makes DER a canonical subset of BER: every length is definite, and the shortest form
9 * that fits. That is the whole of what this reads - an indefinite length, a redundant long form, or
10 * a length that runs past the caller's buffer is refused rather than guessed at, because a parser
11 * that accepts more than the encoding allows accepts two encodings of the same certificate and
12 * signature verification covers only one of them.
13 *
14 * Nothing is copied and nothing is allocated. A read reports a tag, a pointer into the caller's own
15 * bytes, a length, and where the next value begins, so walking a structure is a sequence of reads
16 * against one buffer that outlives them all.
17 *
18 * The caller holds the position. A chain walk has two certificates open at once, so the cursor is a
19 * value on the call rather than state in this module.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef PROTOCORE_DER_H
26#define PROTOCORE_DER_H
27
28#include "protocore_config.h" // the entry point: the widths
29
31
32/** @name X.690 sec 8.1.2 identifier octets: the universal tags this profile reads.
33 * @{ */
34#define PROTOCORE_DER_BOOLEAN 0x01u
35#define PROTOCORE_DER_INTEGER 0x02u
36#define PROTOCORE_DER_BIT_STRING 0x03u
37#define PROTOCORE_DER_OCTET_STRING 0x04u
38#define PROTOCORE_DER_NULL 0x05u
39#define PROTOCORE_DER_OID 0x06u
40#define PROTOCORE_DER_UTF8_STRING 0x0Cu
41#define PROTOCORE_DER_PRINTABLE_STRING 0x13u
42#define PROTOCORE_DER_IA5_STRING 0x16u
43#define PROTOCORE_DER_UTC_TIME 0x17u
44#define PROTOCORE_DER_GENERALIZED_TIME 0x18u
45#define PROTOCORE_DER_SEQUENCE 0x30u
46#define PROTOCORE_DER_SET 0x31u
47/** @} */
48
49/** @brief X.690 sec 8.1.2.5: the constructed bit, set on a value that holds other values. */
50#define PROTOCORE_DER_CONSTRUCTED 0x20u
51
52/** @brief X.690 sec 8.1.2.2: a context-specific tag, as an X.509 field's [n]. */
53#define PROTOCORE_DER_CONTEXT(n) (0x80u | (uint8_t)(n))
54#define PROTOCORE_DER_CONTEXT_CONSTRUCTED(n) (0xA0u | (uint8_t)(n))
55
56/** @brief One value in the caller's bytes: what it is, where its content is, and what follows. */
57typedef struct
58{
59 uint8_t tag; ///< the identifier octet (X.690 sec 8.1.2)
60 const uint8_t *content; ///< the content octets, in the caller's buffer
61 size_t len; ///< how many
62 size_t next; ///< the offset the following value begins at
63} DerTlv;
64
65/** @brief The bytes a read walks, and where in them it starts. */
66typedef struct
67{
68 const uint8_t *buf; ///< the encoding, the caller's
69 size_t len; ///< how much of it there is
70 size_t pos; ///< where this read begins
72
73/** @brief An object identifier a comparison names, in its encoded form (X.690 sec 8.19). */
74typedef struct
75{
76 const uint8_t *oid; ///< the OID's content octets, without the tag and length
77 size_t oid_len; ///< how many
79
80/**
81 * @brief The one reader.
82 *
83 * A caller sets the members a call takes, invokes it through ::Der, and reads the outcome off the
84 * same handle.
85 *
86 * @var DerNs::read_args the bytes a read walks, and where it starts
87 * @var DerNs::oid_args the OID a comparison names
88 * @var DerNs::tlv what a read found
89 * @var DerNs::ok a call's true/false outcome
90 * @var DerNs::u64 an INTEGER's value, or a time as seconds since the POSIX epoch
91 * @var DerNs::read one value at @c read_args.pos: its tag, content and successor
92 * @var DerNs::enter the same, and then the first value INSIDE it; refuses a primitive
93 * @var DerNs::uint the INTEGER at @c read_args.pos as an unsigned value; refuses a negative
94 * one, and one wider than 64 bits
95 * @var DerNs::bitstring the BIT STRING at @c read_args.pos, past its unused-bits octet; refuses
96 * any count of unused bits but zero, which is what a key or a signature has
97 * @var DerNs::oid_eq whether the OID at @c read_args.pos is the one @c oid_args names
98 * @var DerNs::time the UTCTime or GeneralizedTime at @c read_args.pos, as seconds since the
99 * POSIX epoch (RFC 5280 sec 4.1.2.5)
100 *
101 * No storage member: every call works in the caller's buffer and reports on this handle.
102 */
111
112/** @brief The operands and the outcome. */
113extern DerVars DerV;
114
115/** @brief The entries. */
116typedef struct
117{
118 void (*const read)(uint8_t *work);
119 void (*const enter)(uint8_t *work);
120 void (*const uint)(uint8_t *work);
121 void (*const bitstring)(uint8_t *work);
122 void (*const oid_eq)(uint8_t *work);
123 void (*const time)(uint8_t *work);
124} DerNs;
125
126// What the table binds, defined once in the .c and taking one parameter each: everything
127// else an entry needs is an operand in DerV or a region of the borrow at a fixed offset.
128void protocore_der_read(uint8_t *work);
129void protocore_der_enter(uint8_t *work);
130void protocore_der_uint(uint8_t *work);
131void protocore_der_bitstring(uint8_t *work);
132void protocore_der_oid_eq(uint8_t *work);
133void protocore_der_time(uint8_t *work);
134
135// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
136// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
137// `Der.read(work)` resolves to a named function and becomes a DIRECT call. An extern table
138// leaves the call indirect and the symbol live at every level, -O2 -flto included.
139static const DerNs Der __attribute__((unused)) = {
141 .enter = protocore_der_enter,
142 .uint = protocore_der_uint,
143 .bitstring = protocore_der_bitstring,
144 .oid_eq = protocore_der_oid_eq,
145 .time = protocore_der_time,
146};
147
149
150#endif // PROTOCORE_DER_H
void protocore_der_oid_eq(uint8_t *work)
void protocore_der_time(uint8_t *work)
void protocore_der_enter(uint8_t *work)
DerVars DerV
The operands and the outcome.
void protocore_der_uint(uint8_t *work)
void protocore_der_bitstring(uint8_t *work)
void protocore_der_read(uint8_t *work)
The entries.
Definition der.h:117
void(*const read)(uint8_t *work)
Definition der.h:118
An object identifier a comparison names, in its encoded form (X.690 sec 8.19).
Definition der.h:75
const uint8_t * oid
the OID's content octets, without the tag and length
Definition der.h:76
size_t oid_len
how many
Definition der.h:77
The bytes a read walks, and where in them it starts.
Definition der.h:67
const uint8_t * buf
the encoding, the caller's
Definition der.h:68
size_t len
how much of it there is
Definition der.h:69
size_t pos
where this read begins
Definition der.h:70
One value in the caller's bytes: what it is, where its content is, and what follows.
Definition der.h:58
size_t len
how many
Definition der.h:61
size_t next
the offset the following value begins at
Definition der.h:62
uint8_t tag
the identifier octet (X.690 sec 8.1.2)
Definition der.h:59
const uint8_t * content
the content octets, in the caller's buffer
Definition der.h:60
Definition der.h:104
DerOidArgs oid_args
Definition der.h:106
DerTlv tlv
Definition der.h:107
DerReadArgs read_args
Definition der.h:105
proto_bool ok
Definition der.h:108
uint64_t u64
Definition der.h:109
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97