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