ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
base64.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 base64.h
6 * @brief Base64 encoder/decoder.
7 *
8 * Used to encode the SHA-1 digest in the WebSocket handshake response
9 * (RFC 6455 ยง4.2.2) and to decode Basic Auth credentials (RFC 7617).
10 *
11 * **Encode** is a portable software codec on every target (fast; it only handles
12 * the public WebSocket-accept digest). **Decode** touches the secret Basic-auth
13 * credentials, so on the ESP32 it uses mbedTLS's constant-time decoder (side-channel
14 * hardened) and on the native test target the portable software decoder. See
15 * base64.cpp and docs/FEATURE_PERFORMANCE.md section 2.
16 *
17 * @author Douglas Quigg (dstroy0)
18 * @date 2026
19 */
20
21#ifndef PROTOCORE_BASE64_H
22#define PROTOCORE_BASE64_H
23
24#include "protocore_config.h" // the entry point: protocore_types.h for the widths
25
26#if PROTOCORE_ENABLE_BASE64
27
29
30// This module holds nothing between calls, so it carves no borrow and states none. An entry
31// takes one all the same, and never reads it, so every namespace in the tree is invoked the
32// same way.
33
34/** @brief What encode takes: src, src_len, dst. */
35typedef struct
36{
37 const uint8_t *src;
38 size_t src_len;
39 char *dst;
40} Base64EncodeArgs;
41
42/** @brief What decode takes: src, dst, dst_cap. */
43typedef struct
44{
45 const char *src;
46 uint8_t *dst;
47 size_t dst_cap;
48} Base64DecodeArgs;
49
50/** @brief What url_encode takes: src, src_len, dst. */
51typedef struct
52{
53 const uint8_t *src;
54 size_t src_len;
55 char *dst;
56} Base64UrlEncodeArgs;
57
58/** @brief What url_decode takes: src, src_len, dst, dst_cap. */
59typedef struct
60{
61 const char *src;
62 size_t src_len;
63 uint8_t *dst;
64 size_t dst_cap;
65} Base64UrlDecodeArgs;
66
67/**
68 * @brief Base64 encoder/decoder.
69 *
70 * A caller sets the members a call takes, invokes it through ::Base64 with the bytes it runs
71 * out of, and reads the outcome off the same handle.
72 *
73 * Base64.encode_args.src = ...;
74 * Base64.encode_args.src_len = ...;
75 * Base64.encode_args.dst = ...;
76 * Base64.encode(work);
77 *
78 * @var Base64Ns::encode_args what encode takes: src, src_len, dst
79 * @var Base64Ns::decode_args what decode takes: src, dst, dst_cap
80 * @var Base64Ns::url_encode_args what url_encode takes: src, src_len, dst
81 * @var Base64Ns::url_decode_args what url_decode takes: src, src_len, dst, dst_cap
82 * @var Base64Ns::ok a call's true/false outcome
83 * @var Base64Ns::n the count a call reports
84 * @var Base64Ns::encode write src_len bytes as NUL-terminated base64; dst holds at least
85 * @var Base64Ns::decode read a NUL-terminated base64 string into at most dst_cap bytes; the
86 * @var Base64Ns::url_encode the same encode in the '-' '_' alphabet with no '=' padding
87 * @var Base64Ns::url_decode read src_len base64url characters, stopping at an '='. Strict: the
88 *
89 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
90 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
91 * a caller drives every namespace the same way.
92 */
93typedef struct
94{
95 Base64EncodeArgs encode_args;
96 Base64DecodeArgs decode_args;
97 Base64UrlEncodeArgs url_encode_args;
98 Base64UrlDecodeArgs url_decode_args;
99 proto_bool ok;
100 size_t n;
101} Base64Vars;
102
103/** @brief The operands and the outcome. */
104extern Base64Vars Base64V;
105
106/** @brief The entries. */
107typedef struct
108{
109 void (*const encode)(uint8_t *work);
110 void (*const decode)(uint8_t *work);
111 void (*const url_encode)(uint8_t *work);
112 void (*const url_decode)(uint8_t *work);
113} Base64Ns;
114
115// What the table binds, defined once in the .c and taking one parameter each: everything
116// else an entry needs is an operand in Base64V or a region of the borrow at a fixed offset.
117void protocore_base64_encode(uint8_t *work);
118void protocore_base64_decode(uint8_t *work);
119void protocore_base64_url_encode(uint8_t *work);
120void protocore_base64_url_decode(uint8_t *work);
121
122// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
123// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
124// `Base64.encode(work)` resolves to a named function and becomes a DIRECT call. An extern table
125// leaves the call indirect and the symbol live at every level, -O2 -flto included.
126static const Base64Ns Base64 __attribute__((unused)) = {
127 .encode = protocore_base64_encode,
128 .decode = protocore_base64_decode,
129 .url_encode = protocore_base64_url_encode,
130 .url_decode = protocore_base64_url_decode,
131};
132
134
135#endif // PROTOCORE_ENABLE_BASE64
136
137#endif // PROTOCORE_BASE64_H
#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