xref: /freebsd/crypto/openssl/include/internal/json_enc.h (revision e7be843b4a162e68651d3911f0357ed464915629)
1*e7be843bSPierre Pronchery /*
2*e7be843bSPierre Pronchery  * Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.
3*e7be843bSPierre Pronchery  *
4*e7be843bSPierre Pronchery  * Licensed under the Apache License 2.0 (the "License").  You may not use
5*e7be843bSPierre Pronchery  * this file except in compliance with the License.  You can obtain a copy
6*e7be843bSPierre Pronchery  * in the file LICENSE in the source distribution or at
7*e7be843bSPierre Pronchery  * https://www.openssl.org/source/license.html
8*e7be843bSPierre Pronchery  */
9*e7be843bSPierre Pronchery 
10*e7be843bSPierre Pronchery #ifndef OSSL_JSON_ENC_H
11*e7be843bSPierre Pronchery # define OSSL_JSON_ENC_H
12*e7be843bSPierre Pronchery 
13*e7be843bSPierre Pronchery # include <openssl/bio.h>
14*e7be843bSPierre Pronchery 
15*e7be843bSPierre Pronchery /*
16*e7be843bSPierre Pronchery  * JSON Encoder
17*e7be843bSPierre Pronchery  * ============
18*e7be843bSPierre Pronchery  *
19*e7be843bSPierre Pronchery  * This JSON encoder is used for qlog. It supports ordinary JSON (RFC 7159),
20*e7be843bSPierre Pronchery  * JSON-SEQ (RFC 7464) and I-JSON (RFC 7493). It supports only basic ASCII.
21*e7be843bSPierre Pronchery  */
22*e7be843bSPierre Pronchery 
23*e7be843bSPierre Pronchery struct json_write_buf {
24*e7be843bSPierre Pronchery     BIO     *bio;
25*e7be843bSPierre Pronchery     char    *buf;
26*e7be843bSPierre Pronchery     size_t  alloc, cur;
27*e7be843bSPierre Pronchery };
28*e7be843bSPierre Pronchery 
29*e7be843bSPierre Pronchery typedef struct ossl_json_enc_st {
30*e7be843bSPierre Pronchery     uint32_t                flags;
31*e7be843bSPierre Pronchery     /* error: 1 if an error has occurred. */
32*e7be843bSPierre Pronchery     /* state: current state. */
33*e7be843bSPierre Pronchery     /* stack stores a bitmap. 0=object, 1=array. */
34*e7be843bSPierre Pronchery     /* stack cur   size: stack_end_byte bytes, stack_end_bit bits. */
35*e7be843bSPierre Pronchery     /* stack alloc size: stack_bytes bytes. */
36*e7be843bSPierre Pronchery     unsigned char           error, stack_end_bit, state, *stack, defer_indent;
37*e7be843bSPierre Pronchery     unsigned char           stack_small[16];
38*e7be843bSPierre Pronchery     struct json_write_buf   wbuf;
39*e7be843bSPierre Pronchery     size_t                  stack_end_byte, stack_bytes;
40*e7be843bSPierre Pronchery } OSSL_JSON_ENC;
41*e7be843bSPierre Pronchery 
42*e7be843bSPierre Pronchery /*
43*e7be843bSPierre Pronchery  * ossl_json_init
44*e7be843bSPierre Pronchery  * --------------
45*e7be843bSPierre Pronchery  *
46*e7be843bSPierre Pronchery  * Initialises a JSON encoder.
47*e7be843bSPierre Pronchery  *
48*e7be843bSPierre Pronchery  * If the flag OSSL_JSON_FLAG_SEQ is passed, the output is in JSON-SEQ. The
49*e7be843bSPierre Pronchery  * caller should use the encoder as though it is encoding members of a JSON
50*e7be843bSPierre Pronchery  * array (but without calling ossl_json_array_begin() or ossl_json_array_end()).
51*e7be843bSPierre Pronchery  * Each top-level JSON item (e.g. JSON object) encoded will be separated
52*e7be843bSPierre Pronchery  * correctly as per the JSON-SEQ format.
53*e7be843bSPierre Pronchery  *
54*e7be843bSPierre Pronchery  * If the flag OSSL_JSON_FLAG_SEQ is not passed, the output is in JSON format.
55*e7be843bSPierre Pronchery  * Generally the caller should encode only a single output item (e.g. a JSON
56*e7be843bSPierre Pronchery  * object).
57*e7be843bSPierre Pronchery  *
58*e7be843bSPierre Pronchery  * By default, JSON output is maximally compact. If OSSL_JSON_FLAG_PRETTY is
59*e7be843bSPierre Pronchery  * set, JSON/JSON-SEQ output is spaced for optimal human readability.
60*e7be843bSPierre Pronchery  *
61*e7be843bSPierre Pronchery  * If OSSL_JSON_FLAG_IJSON is set, integers outside the range `[-2**53 + 1,
62*e7be843bSPierre Pronchery  * 2**53 - 1]` are automatically converted to decimal strings before
63*e7be843bSPierre Pronchery  * serialization.
64*e7be843bSPierre Pronchery  */
65*e7be843bSPierre Pronchery #define OSSL_JSON_FLAG_NONE    0
66*e7be843bSPierre Pronchery #define OSSL_JSON_FLAG_SEQ     (1U << 0)
67*e7be843bSPierre Pronchery #define OSSL_JSON_FLAG_PRETTY  (1U << 1)
68*e7be843bSPierre Pronchery #define OSSL_JSON_FLAG_IJSON   (1U << 2)
69*e7be843bSPierre Pronchery 
70*e7be843bSPierre Pronchery int ossl_json_init(OSSL_JSON_ENC *json, BIO *bio, uint32_t flags);
71*e7be843bSPierre Pronchery 
72*e7be843bSPierre Pronchery /*
73*e7be843bSPierre Pronchery  * ossl_json_cleanup
74*e7be843bSPierre Pronchery  * -----------------
75*e7be843bSPierre Pronchery  *
76*e7be843bSPierre Pronchery  * Destroys a JSON encoder.
77*e7be843bSPierre Pronchery  */
78*e7be843bSPierre Pronchery void ossl_json_cleanup(OSSL_JSON_ENC *json);
79*e7be843bSPierre Pronchery 
80*e7be843bSPierre Pronchery /*
81*e7be843bSPierre Pronchery  * ossl_json_reset
82*e7be843bSPierre Pronchery  * ---------------
83*e7be843bSPierre Pronchery  *
84*e7be843bSPierre Pronchery  * Resets a JSON encoder, as though it has just been initialised, allowing it
85*e7be843bSPierre Pronchery  * to be used again for new output syntactically unrelated to any previous
86*e7be843bSPierre Pronchery  * output. This is similar to calling ossl_json_cleanup followed by
87*e7be843bSPierre Pronchery  * ossl_json_init but may allow internal buffers to be reused.
88*e7be843bSPierre Pronchery  *
89*e7be843bSPierre Pronchery  * If the JSON encoder has entered an error state, this function MAY allow
90*e7be843bSPierre Pronchery  * recovery from this error state, in which case it will return 1. If this
91*e7be843bSPierre Pronchery  * function returns 0, the JSON encoder is unrecoverable and
92*e7be843bSPierre Pronchery  * ossl_json_cleanup() must be called.
93*e7be843bSPierre Pronchery  *
94*e7be843bSPierre Pronchery  * Automatically calls ossl_json_flush().
95*e7be843bSPierre Pronchery  */
96*e7be843bSPierre Pronchery int ossl_json_reset(OSSL_JSON_ENC *json);
97*e7be843bSPierre Pronchery 
98*e7be843bSPierre Pronchery /*
99*e7be843bSPierre Pronchery  * ossl_json_flush
100*e7be843bSPierre Pronchery  * ---------------
101*e7be843bSPierre Pronchery  *
102*e7be843bSPierre Pronchery  * Flushes the JSON encoder, ensuring that any residual bytes in internal
103*e7be843bSPierre Pronchery  * buffers are written to the provided sink BIO. Flushing may also happen
104*e7be843bSPierre Pronchery  * autonomously as buffers are filled, but the caller must use this function
105*e7be843bSPierre Pronchery  * to guarantee all data has been flushed.
106*e7be843bSPierre Pronchery  */
107*e7be843bSPierre Pronchery int ossl_json_flush(OSSL_JSON_ENC *json);
108*e7be843bSPierre Pronchery 
109*e7be843bSPierre Pronchery /*
110*e7be843bSPierre Pronchery  * ossl_json_flush_cleanup
111*e7be843bSPierre Pronchery  * -----------------------
112*e7be843bSPierre Pronchery  *
113*e7be843bSPierre Pronchery  * Tries to flush as in a call to ossl_json_flush, and then calls
114*e7be843bSPierre Pronchery  * ossl_json_cleanup regardless of the result. The result of the flush call is
115*e7be843bSPierre Pronchery  * returned.
116*e7be843bSPierre Pronchery  */
117*e7be843bSPierre Pronchery int ossl_json_flush_cleanup(OSSL_JSON_ENC *json);
118*e7be843bSPierre Pronchery 
119*e7be843bSPierre Pronchery /*
120*e7be843bSPierre Pronchery  * ossl_json_set0_sink
121*e7be843bSPierre Pronchery  * -------------------
122*e7be843bSPierre Pronchery  *
123*e7be843bSPierre Pronchery  * Changes the sink used by the JSON encoder.
124*e7be843bSPierre Pronchery  */
125*e7be843bSPierre Pronchery int ossl_json_set0_sink(OSSL_JSON_ENC *json, BIO *bio);
126*e7be843bSPierre Pronchery 
127*e7be843bSPierre Pronchery /*
128*e7be843bSPierre Pronchery  * ossl_json_in_error
129*e7be843bSPierre Pronchery  * ------------------
130*e7be843bSPierre Pronchery  *
131*e7be843bSPierre Pronchery  * To enhance the ergonomics of the JSON API, the JSON object uses an implicit
132*e7be843bSPierre Pronchery  * error tracking model. When a JSON API call fails (for example due to caller
133*e7be843bSPierre Pronchery  * error, such as trying to close an array which was not opened), the JSON
134*e7be843bSPierre Pronchery  * object enters an error state and all further calls are silently ignored.
135*e7be843bSPierre Pronchery  *
136*e7be843bSPierre Pronchery  * The caller can detect this condition after it is finished making builder
137*e7be843bSPierre Pronchery  * calls to the JSON object by calling this function. This function returns 1
138*e7be843bSPierre Pronchery  * if an error occurred. At this point the caller's only recourse is to call
139*e7be843bSPierre Pronchery  * ossl_json_reset() or ossl_json_cleanup().
140*e7be843bSPierre Pronchery  *
141*e7be843bSPierre Pronchery  * Note that partial (i.e., invalid) output may still have been sent to the BIO
142*e7be843bSPierre Pronchery  * in this case. Since the amount of output which can potentially be produced
143*e7be843bSPierre Pronchery  * by a JSON object is unbounded, it is impractical to buffer it all before
144*e7be843bSPierre Pronchery  * flushing. It is expected that errors will ordinarily be either caller errors
145*e7be843bSPierre Pronchery  * (programming errors) or BIO errors.
146*e7be843bSPierre Pronchery  */
147*e7be843bSPierre Pronchery int ossl_json_in_error(OSSL_JSON_ENC *json);
148*e7be843bSPierre Pronchery 
149*e7be843bSPierre Pronchery /*
150*e7be843bSPierre Pronchery  * JSON Builder Calls
151*e7be843bSPierre Pronchery  * ==================
152*e7be843bSPierre Pronchery  *
153*e7be843bSPierre Pronchery  * These functions are used to build JSON output. The functions which have
154*e7be843bSPierre Pronchery  * begin and end function pairs must be called in correctly nested sequence.
155*e7be843bSPierre Pronchery  * When writing an object, ossl_json_key() must be called exactly once before
156*e7be843bSPierre Pronchery  * each call to write a JSON item.
157*e7be843bSPierre Pronchery  *
158*e7be843bSPierre Pronchery  * The JSON library takes responsibility for enforcing correct usage patterns.
159*e7be843bSPierre Pronchery  * If a call is made that does not correspond to the JSON syntax, the JSON
160*e7be843bSPierre Pronchery  * object enters the error state and all subsequent calls are ignored.
161*e7be843bSPierre Pronchery  *
162*e7be843bSPierre Pronchery  * In JSON-SEQ mode, the caller should act as though the library implicitly
163*e7be843bSPierre Pronchery  * places all calls between an ossl_json_array_begin() and
164*e7be843bSPierre Pronchery  * ossl_json_array_end() pair; for example, the normal usage pattern would be
165*e7be843bSPierre Pronchery  * to call ossl_json_object_begin() followed by ossl_json_object_end(), in
166*e7be843bSPierre Pronchery  * repeated sequence.
167*e7be843bSPierre Pronchery  *
168*e7be843bSPierre Pronchery  * The library does not enforce non-generation of duplicate keys. Avoiding this
169*e7be843bSPierre Pronchery  * is the caller's responsibility. It is also the caller's responsibility to
170*e7be843bSPierre Pronchery  * pass valid UTF-8 strings. All other forms of invalid output will cause an
171*e7be843bSPierre Pronchery  * error. Note that due to the immediate nature of the API, partial output may
172*e7be843bSPierre Pronchery  * have already been generated in such a case.
173*e7be843bSPierre Pronchery  */
174*e7be843bSPierre Pronchery 
175*e7be843bSPierre Pronchery /* Begin a new JSON object. */
176*e7be843bSPierre Pronchery void ossl_json_object_begin(OSSL_JSON_ENC *json);
177*e7be843bSPierre Pronchery 
178*e7be843bSPierre Pronchery /* End a JSON object. Must be matched with a call to ossl_json_object_begin(). */
179*e7be843bSPierre Pronchery void ossl_json_object_end(OSSL_JSON_ENC *json);
180*e7be843bSPierre Pronchery 
181*e7be843bSPierre Pronchery /* Begin a new JSON array. */
182*e7be843bSPierre Pronchery void ossl_json_array_begin(OSSL_JSON_ENC *json);
183*e7be843bSPierre Pronchery 
184*e7be843bSPierre Pronchery /* End a JSON array. Must be matched with a call to ossl_json_array_end(). */
185*e7be843bSPierre Pronchery void ossl_json_array_end(OSSL_JSON_ENC *json);
186*e7be843bSPierre Pronchery 
187*e7be843bSPierre Pronchery /*
188*e7be843bSPierre Pronchery  * Encode a JSON key within an object. Pass a zero-terminated string, which can
189*e7be843bSPierre Pronchery  * be freed immediately following the call to this function.
190*e7be843bSPierre Pronchery  */
191*e7be843bSPierre Pronchery void ossl_json_key(OSSL_JSON_ENC *json, const char *key);
192*e7be843bSPierre Pronchery 
193*e7be843bSPierre Pronchery /* Encode a JSON 'null' value. */
194*e7be843bSPierre Pronchery void ossl_json_null(OSSL_JSON_ENC *json);
195*e7be843bSPierre Pronchery 
196*e7be843bSPierre Pronchery /* Encode a JSON boolean value. */
197*e7be843bSPierre Pronchery void ossl_json_bool(OSSL_JSON_ENC *json, int value);
198*e7be843bSPierre Pronchery 
199*e7be843bSPierre Pronchery /* Encode a JSON integer from a uint64_t. */
200*e7be843bSPierre Pronchery void ossl_json_u64(OSSL_JSON_ENC *json, uint64_t value);
201*e7be843bSPierre Pronchery 
202*e7be843bSPierre Pronchery /* Encode a JSON integer from an int64_t. */
203*e7be843bSPierre Pronchery void ossl_json_i64(OSSL_JSON_ENC *json, int64_t value);
204*e7be843bSPierre Pronchery 
205*e7be843bSPierre Pronchery /*
206*e7be843bSPierre Pronchery  * Encode a JSON UTF-8 string from a zero-terminated string. The string passed
207*e7be843bSPierre Pronchery  * can be freed immediately following the call to this function.
208*e7be843bSPierre Pronchery  */
209*e7be843bSPierre Pronchery void ossl_json_str(OSSL_JSON_ENC *json, const char *str);
210*e7be843bSPierre Pronchery 
211*e7be843bSPierre Pronchery /*
212*e7be843bSPierre Pronchery  * Encode a JSON UTF-8 string from a string with the given length. The string
213*e7be843bSPierre Pronchery  * passed can be freed immediately following the call to this function.
214*e7be843bSPierre Pronchery  */
215*e7be843bSPierre Pronchery void ossl_json_str_len(OSSL_JSON_ENC *json, const char *str, size_t str_len);
216*e7be843bSPierre Pronchery 
217*e7be843bSPierre Pronchery /*
218*e7be843bSPierre Pronchery  * Encode binary data as a lowercase hex string. data_len is the data length in
219*e7be843bSPierre Pronchery  * bytes.
220*e7be843bSPierre Pronchery  */
221*e7be843bSPierre Pronchery void ossl_json_str_hex(OSSL_JSON_ENC *json, const void *data, size_t data_len);
222*e7be843bSPierre Pronchery 
223*e7be843bSPierre Pronchery #endif
224