Encoding and decoding functions for Base64, Hex, BER/DER, and PEM. More...
#include "qsccommon.h"Go to the source code of this file.
Data Structures | |
| struct | qsc_encoding_ber_element |
| In-memory representation of a single BER/DER ASN.1 TLV element. More... | |
Macros | |
| #define | QSC_ENCODING_BER_CLASS_UNIVERSAL 0x00U |
| ASN.1 universal tag class (bits 8-7 = 00). | |
| #define | QSC_ENCODING_BER_CLASS_APPLICATION 0x40U |
| ASN.1 application-specific tag class (bits 8-7 = 01). | |
| #define | QSC_ENCODING_BER_CLASS_CONTEXT_SPECIFIC 0x80U |
| ASN.1 context-specific tag class (bits 8-7 = 10). | |
| #define | QSC_ENCODING_BER_CLASS_PRIVATE 0xC0U |
| ASN.1 private tag class (bits 8-7 = 11). | |
| #define | QSC_BER_ENCODING_INDEFINITE_LENGTH ((size_t)-1) |
| Sentinel value for requesting indefinite-length BER encoding. | |
Typedefs | |
| typedef QSC_EXPORT_API struct qsc_encoding_ber_element | qsc_encoding_ber_element |
| In-memory representation of a single BER/DER ASN.1 TLV element. | |
Functions | |
| QSC_EXPORT_API bool | qsc_encoding_base64_decode (uint8_t *output, size_t otplen, const char *input, size_t inplen) |
| Decode a Base64-encoded string to a byte array. | |
| QSC_EXPORT_API size_t | qsc_encoding_base64_decoded_size (const char *input, size_t length) |
| Compute the byte count required to hold the decoded form of a Base64 string. | |
| QSC_EXPORT_API bool | qsc_encoding_base64_encode (char *output, size_t otplen, const uint8_t *input, size_t inplen) |
| Encode a byte array to a Base64 string. | |
| QSC_EXPORT_API size_t | qsc_encoding_base64_encoded_size (size_t length) |
Compute the character count required to hold the Base64 encoding of length input bytes, including trailing '=' padding. | |
| QSC_EXPORT_API bool | qsc_encoding_base64_is_valid_char (char value) |
| Test whether a character belongs to the Base64 alphabet. | |
| QSC_EXPORT_API qsc_encoding_ber_element * | qsc_encoding_ber_decode_element (const uint8_t *buffer, size_t buflen, size_t *consumed) |
| Decode a single BER element from an octet buffer. | |
| QSC_EXPORT_API size_t | qsc_encoding_ber_decode_length (const uint8_t *buffer, size_t buflen, size_t *length, bool *indef) |
| Decode a BER length field from an octet buffer. | |
| QSC_EXPORT_API size_t | qsc_encoding_ber_decode_tag (const uint8_t *buffer, size_t buflen, uint8_t *tagclass, bool *construct, uint32_t *tagnum) |
| Decode a BER tag field from an octet buffer. | |
| QSC_EXPORT_API size_t | qsc_encoding_ber_encode_element (qsc_encoding_ber_element *element, uint8_t *buffer, size_t buflen) |
| Encode a complete BER element (tag + length + value) to an octet buffer. | |
| QSC_EXPORT_API size_t | qsc_encoding_ber_encode_length (size_t length, uint8_t *buffer, size_t buflen) |
| Encode a length value to BER form. | |
| QSC_EXPORT_API size_t | qsc_encoding_ber_encode_tag (uint8_t tagclass, bool construct, uint32_t tagnum, uint8_t *buffer, size_t buflen) |
| Encode an ASN.1 tag to BER form. | |
| QSC_EXPORT_API void | qsc_encoding_ber_free_element (qsc_encoding_ber_element *element) |
| Recursively free a decoded BER element tree. | |
| QSC_EXPORT_API qsc_encoding_ber_element * | qsc_encoding_der_decode_element (const uint8_t *buffer, size_t buflen, size_t *consumed) |
| Decode a single DER element from an octet buffer. | |
| QSC_EXPORT_API size_t | qsc_encoding_der_encode_element (qsc_encoding_ber_element *element, uint8_t *buffer, size_t buflen) |
| Encode an ASN.1 element tree using DER. | |
| QSC_EXPORT_API bool | qsc_encoding_hex_decode (const char *input, size_t inplen, uint8_t *output, size_t otplen, size_t *declen) |
| Decode a hexadecimal string to binary data. | |
| QSC_EXPORT_API bool | qsc_encoding_hex_encode (const uint8_t *input, size_t inplen, char *output, size_t otplen) |
| Encode binary data to an upper-case hexadecimal string. | |
| QSC_EXPORT_API bool | qsc_encoding_pem_decode (const char *input, size_t inplen, uint8_t *output, size_t otplen, size_t *declen) |
| Decode a PEM-formatted string to binary data. | |
| QSC_EXPORT_API bool | qsc_encoding_pem_encode (const char *label, char *output, size_t otplen, const uint8_t *data, size_t datalen) |
| Encode binary data in PEM format. | |
Encoding and decoding functions for Base64, Hex, BER/DER, and PEM.
This header provides production-quality implementations of four encoding schemes that are heavily used in cryptographic infrastructure. All implementations are written to their governing specifications so that encoded output is accepted by any conformant decoder, including the certificate-processing pipelines found in X.509 / PKIX.
qsc_encoding_base64_encode and qsc_encoding_base64_decode implement the standard Base64 alphabet defined in RFC 4648, Table 1. The decode function enforces the three RFC 4648 padding rules:qsc_encoding_hex_encode produces upper-case hex and writes a null terminator. qsc_encoding_hex_decode accepts both upper- and lower-case digits and rejects input whose character count is odd. On any decode failure the partially written output buffer is zeroed before the function returns.encoding_der_element_size) followed by a direct in-buffer recursive write, eliminating any fixed-size temporary allocation and correctly handling arbitrarily large structures (e.g. post-quantum X.509 certificates).qsc_encoding_ber_element* trees must be freed by the caller with qsc_encoding_ber_free_element. All other functions write into caller-supplied buffers. There are no internal memory leaks; every allocation failure is recovered before the function returns.| #define QSC_BER_ENCODING_INDEFINITE_LENGTH ((size_t)-1) |
Sentinel value for requesting indefinite-length BER encoding.
Pass this value as the length argument to qsc_encoding_ber_encode_length to produce the single-byte indefinite- length indicator (0x80) defined in X.690 8.1.3.6. Only valid for constructed BER elements; DER (and primitive BER) always use definite- length form.
| #define QSC_ENCODING_BER_CLASS_APPLICATION 0x40U |
ASN.1 application-specific tag class (bits 8-7 = 01).
Used for types whose meaning is specific to an application. The interpretation of the tag number is application-defined. Defined in X.690 8.1.2.2, Table 1.
| #define QSC_ENCODING_BER_CLASS_CONTEXT_SPECIFIC 0x80U |
ASN.1 context-specific tag class (bits 8-7 = 10).
Used to distinguish between data elements that appear at the same position in different contexts within a structured type (e.g. OPTIONAL fields in a SEQUENCE). Defined in X.690 8.1.2.2, Table 1.
| #define QSC_ENCODING_BER_CLASS_PRIVATE 0xC0U |
ASN.1 private tag class (bits 8-7 = 11).
Reserved for enterprise or vendor-specific extensions. Defined in X.690 8.1.2.2, Table 1.
| #define QSC_ENCODING_BER_CLASS_UNIVERSAL 0x00U |
ASN.1 universal tag class (bits 8-7 = 00).
Used for types that are common to all ASN.1 applications, such as INTEGER, BOOLEAN, SEQUENCE, SET, BIT STRING, OCTET STRING, OBJECT IDENTIFIER, etc. Defined in X.690 8.1.2.2, Table 1.
| typedef QSC_EXPORT_API struct qsc_encoding_ber_element qsc_encoding_ber_element |
In-memory representation of a single BER/DER ASN.1 TLV element.
An element is either:
value and length reflects their count. children is NULL and ccount is 0.value holds that block and length reflects its byte count. For elements produced by the decoder, children holds pointers to individually decoded child elements and length reflects the total content byte count (not including tag/length overhead of this element).indefinite is true. The decoder populates children with all elements found before the EOC marker; the encoder iterates children and appends the 0x00 0x00 EOC pair. DER rejects indefinite-length encoding at both encode and decode time.children array is heap-allocated and owned by this element. Always release element trees with qsc_encoding_ber_free_element to avoid memory leaks. Universal ASN.1 tag numbers as defined in X.680 8.6 and X.690 Table 1.
These values occupy the low five bits of the first tag octet when the tag class is QSC_ENCODING_BER_CLASS_UNIVERSAL and the element uses short-form tag encoding (tag number < 31). Values 14 and 15 are reserved by the standard and are not listed here.
| QSC_EXPORT_API bool qsc_encoding_base64_decode | ( | uint8_t * | output, |
| size_t | otplen, | ||
| const char * | input, | ||
| size_t | inplen ) |
Decode a Base64-encoded string to a byte array.
Implements RFC 4648, 4. The function performs three sequential validation passes before writing output:
inplen is a non-zero multiple of four.| output | [uint8_t*] Buffer that receives the decoded bytes. Must be at least qsc_encoding_base64_decoded_size(input, inplen) bytes. |
| otplen | [size_t] Capacity of output in bytes. |
| input | [const char*] Null-terminated or length-bounded Base64 string. |
| inplen | [size_t] Length of input in characters; must be a non-zero multiple of four. |
output is too small. | QSC_EXPORT_API size_t qsc_encoding_base64_decoded_size | ( | const char * | input, |
| size_t | length ) |
Compute the byte count required to hold the decoded form of a Base64 string.
Accounts for RFC 4648 trailing padding characters ('='). The result is exact when input is a valid, padded Base64 string whose length is a multiple of four. Returns 0 if input is NULL, length is zero, or length is not a multiple of four.
| input | [const char*] Pointer to the Base64-encoded string. |
| length | [size_t] Length of the encoded string in characters. |
| QSC_EXPORT_API bool qsc_encoding_base64_encode | ( | char * | output, |
| size_t | otplen, | ||
| const uint8_t * | input, | ||
| size_t | inplen ) |
Encode a byte array to a Base64 string.
Implements RFC 4648, 4, using the standard alphabet (A-Z, a-z, 0-9, '+', '/') with '=' padding to ensure the output length is a multiple of four. A null terminator is written after the encoded data.
The caller must supply an output buffer of at least qsc_encoding_base64_encoded_size(inplen) + 1 bytes.
| output | [char*] Buffer that receives the Base64 string. |
| otplen | [size_t] Capacity of output in bytes (must be at least qsc_encoding_base64_encoded_size(inplen) + 1). |
| input | [const uint8_t*] Binary data to encode. |
| inplen | [size_t] Number of bytes to encode. |
| QSC_EXPORT_API size_t qsc_encoding_base64_encoded_size | ( | size_t | length | ) |
Compute the character count required to hold the Base64 encoding of length input bytes, including trailing '=' padding.
The returned value does not include the null terminator; allocate qsc_encoding_base64_encoded_size(length) + 1 bytes for the output buffer passed to qsc_encoding_base64_encode.
| length | [size_t] Number of raw input bytes. |
length is zero. | QSC_EXPORT_API bool qsc_encoding_base64_is_valid_char | ( | char | value | ) |
Test whether a character belongs to the Base64 alphabet.
Returns true for A-Z, a-z, 0-9, '+', '/', and the padding character '='. All other characters return false.
| value | [char] The character to test. |
value is a legal Base64 symbol. | QSC_EXPORT_API qsc_encoding_ber_element * qsc_encoding_ber_decode_element | ( | const uint8_t * | buffer, |
| size_t | buflen, | ||
| size_t * | consumed ) |
Decode a single BER element from an octet buffer.
Parses the tag, length, and value (or child elements for constructed types) from buffer according to X.690. For constructed elements, child elements are decoded recursively and stored in a heap-allocated pointer array. Indefinite-length constructed elements are fully supported; the parser consumes child elements until the EOC marker (0x00 0x00) is reached.
On success, *consumed is set to the total number of octets read from buffer (tag + length + value fields, including the EOC pair for indefinite-length elements). On failure, *consumed is set to 0 and the return value is NULL; any partial allocations are freed internally.
The caller must release the returned element tree with qsc_encoding_ber_free_element.
| buffer | [const uint8_t*] Input buffer containing the BER-encoded element. |
| buflen | [size_t] Number of bytes available in buffer. Must be at least 2. |
| consumed | [size_t*] Receives the number of bytes consumed on success, or 0 on failure. |
| QSC_EXPORT_API size_t qsc_encoding_ber_decode_length | ( | const uint8_t * | buffer, |
| size_t | buflen, | ||
| size_t * | length, | ||
| bool * | indef ) |
Decode a BER length field from an octet buffer.
Implements X.690 8.1.3. All three length forms are supported:
*indef is set to true and *length is set to 0.The reserved value 0xFF (bnum = 127) and any long-form count that would overflow a native size_t are rejected.
| buffer | [const uint8_t*] Input buffer positioned at the first length octet. |
| buflen | [size_t] Number of bytes available at buffer. |
| length | [size_t*] Receives the decoded length value on success. |
| indef | [bool*] Set to true if the indefinite-length form was decoded. |
buffer (1 for short and indefinite forms; 1 + bnum for long form), or 0 on error. | QSC_EXPORT_API size_t qsc_encoding_ber_decode_tag | ( | const uint8_t * | buffer, |
| size_t | buflen, | ||
| uint8_t * | tagclass, | ||
| bool * | construct, | ||
| uint32_t * | tagnum ) |
Decode a BER tag field from an octet buffer.
Implements X.690 8.1.2. Both short-form (1-byte, tag number < 31) and long-form (multi-byte base-128, continuation bit in bit 8) tags are handled. If the buffer is exhausted before the final base-128 octet (bit 8 = 0) is found, the function returns 0 to indicate failure.
| buffer | [const uint8_t*] Input buffer positioned at the first tag octet. |
| buflen | [size_t] Number of bytes available at buffer. |
| tagclass | [uint8_t*] Receives the tag class (QSC_ENCODING_BER_CLASS_UNIVERSAL et al.). |
| construct | [bool*] Set to true if the Primitive/Constructed bit (bit 6) is set. |
| tagnum | [uint32_t*] Receives the decoded tag number. |
buffer, or 0 on error. | QSC_EXPORT_API size_t qsc_encoding_ber_encode_element | ( | qsc_encoding_ber_element * | element, |
| uint8_t * | buffer, | ||
| size_t | buflen ) |
Encode a complete BER element (tag + length + value) to an octet buffer.
Implements X.690 8. Behaviour depends on the element type:
element->value.element->value.element->children, then appends the EOC pair (0x00 0x00).| element | [qsc_encoding_ber_element*] Element to encode. |
| buffer | [uint8_t*] Output buffer. |
| buflen | [size_t] Capacity of buffer in bytes. |
buffer, or 0 on error (e.g. buffer too small, NULL pointer, or a child encoding failure). | QSC_EXPORT_API size_t qsc_encoding_ber_encode_length | ( | size_t | length, |
| uint8_t * | buffer, | ||
| size_t | buflen ) |
Encode a length value to BER form.
Implements X.690 8.1.3:
length equals QSC_BER_ENCODING_INDEFINITE_LENGTH, writes the single-byte indefinite-length indicator 0x80.length is in the range [0, 127], writes it as a single byte (short definite-length form).length in the minimum number of big-endian octets and prepends a lead byte whose low seven bits give that count.| length | [size_t] Length value to encode, or QSC_BER_ENCODING_INDEFINITE_LENGTH. |
| buffer | [uint8_t*] Output buffer. |
| buflen | [size_t] Capacity of buffer in bytes. |
| QSC_EXPORT_API size_t qsc_encoding_ber_encode_tag | ( | uint8_t | tagclass, |
| bool | construct, | ||
| uint32_t | tagnum, | ||
| uint8_t * | buffer, | ||
| size_t | buflen ) |
Encode an ASN.1 tag to BER form.
Implements X.690 8.1.2:
tagclass (bits 8-7), construct (bit 6), and the tag-number field (bits 5-1).tagnum < 31, the tag number occupies bits 5-1 of the first byte and encoding is complete.tagnum in base-128 big-endian with the continuation bit (bit 8) set on every byte except the last.| tagclass | [uint8_t] Tag class constant (QSC_ENCODING_BER_CLASS_UNIVERSAL et al.). |
| construct | [bool] true for constructed (composite) elements. |
| tagnum | [uint32_t] Tag number; values >= 31 trigger long-form encoding. |
| buffer | [uint8_t*] Output buffer. |
| buflen | [size_t] Capacity of buffer in bytes. |
| QSC_EXPORT_API void qsc_encoding_ber_free_element | ( | qsc_encoding_ber_element * | element | ) |
Recursively free a decoded BER element tree.
Releases all memory associated with element and its descendants: for constructed elements the children array and each child element are freed depth-first; for primitive elements the value buffer is freed. The element itself is then freed. Passing NULL is safe and is a no-op.
| element | [qsc_encoding_ber_element*] Root of the element tree to free. |
| QSC_EXPORT_API qsc_encoding_ber_element * qsc_encoding_der_decode_element | ( | const uint8_t * | buffer, |
| size_t | buflen, | ||
| size_t * | consumed ) |
Decode a single DER element from an octet buffer.
Delegates to qsc_encoding_ber_decode_element and then enforces the DER constraint (X.690 11.1) that indefinite-length encoding is forbidden. If the decoded element carries an indefinite length, it is freed and NULL is returned with *consumed set to 0.
The caller must release the returned element with qsc_encoding_ber_free_element.
| buffer | [const uint8_t*] Input buffer containing the DER-encoded element. |
| buflen | [size_t] Number of bytes available in buffer. |
| consumed | [size_t*] Receives the number of bytes consumed on success, or 0 on failure. |
| QSC_EXPORT_API size_t qsc_encoding_der_encode_element | ( | qsc_encoding_ber_element * | element, |
| uint8_t * | buffer, | ||
| size_t | buflen ) |
Encode an ASN.1 element tree using DER.
Implements X.690 11 (DER). Indefinite-length encoding is rejected. For constructed elements, the function performs a write-free recursive size-computation pass over the child tree to determine the exact content length before writing any output. Child elements are then encoded directly into buffer without an intermediate temporary allocation, making this function suitable for large structures such as post-quantum X.509 certificates whose content may exceed many kilobytes.
| element | [qsc_encoding_ber_element*] Element tree to encode. Must not contain any indefinite-length nodes. |
| buffer | [uint8_t*] Output buffer. |
| buflen | [size_t] Capacity of buffer in bytes. |
| QSC_EXPORT_API bool qsc_encoding_hex_decode | ( | const char * | input, |
| size_t | inplen, | ||
| uint8_t * | output, | ||
| size_t | otplen, | ||
| size_t * | declen ) |
Decode a hexadecimal string to binary data.
Accepts both upper- and lower-case hexadecimal digits (0-9, A-F, a-f). The input length must be even. On any decode error (odd length, out-of- range character, or insufficient output buffer) the function clears any partial output it has written, sets *declen to 0, and returns false.
| input | [const char*] Hex-encoded input string. |
| inplen | [size_t] Length of input in characters (must be even). |
| output | [uint8_t*] Buffer that receives the decoded bytes. |
| otplen | [size_t] Capacity of output (must be >= inplen / 2). |
| declen | [size_t*] Receives the number of decoded bytes on success, or 0 on failure. |
| QSC_EXPORT_API bool qsc_encoding_hex_encode | ( | const uint8_t * | input, |
| size_t | inplen, | ||
| char * | output, | ||
| size_t | otplen ) |
Encode binary data to an upper-case hexadecimal string.
Produces exactly inplen * 2 upper-case hex characters followed by a null terminator. The output buffer must be at least inplen * 2 + 1 bytes.
| input | [const uint8_t*] Binary data to encode. |
| inplen | [size_t] Number of bytes to encode. |
| output | [char*] Buffer that receives the hex string. |
| otplen | [size_t] Capacity of output (must be >= inplen * 2 + 1). |
| QSC_EXPORT_API bool qsc_encoding_pem_decode | ( | const char * | input, |
| size_t | inplen, | ||
| uint8_t * | output, | ||
| size_t | otplen, | ||
| size_t * | declen ) |
Decode a PEM-formatted string to binary data.
Implements RFC 7468 textual-encoding format. The function:
On any failure *declen is set to 0 and the function returns false.
| input | [const char*] Null-terminated PEM string (header + payload + footer). |
| inplen | [size_t] Size of input in bytes. |
| output | [uint8_t*] Buffer that receives the decoded binary data. |
| otplen | [size_t] Capacity of output in bytes. |
| declen | [size_t*] Receives the number of decoded bytes on success, or 0 on failure. |
| QSC_EXPORT_API bool qsc_encoding_pem_encode | ( | const char * | label, |
| char * | output, | ||
| size_t | otplen, | ||
| const uint8_t * | data, | ||
| size_t | datalen ) |
Encode binary data in PEM format.
Produces a RFC 7468-conformant PEM encapsulation:
output.Returns false (without writing to output) if the output buffer is too small to hold the complete PEM encoding or if any pointer argument is NULL.
| label | [const char*] Type label string, e.g. "CERTIFICATE". |
| output | [char*] Buffer that receives the PEM text. |
| otplen | [size_t] Capacity of output in bytes. |
| data | [const uint8_t*] Binary data to encode. |
| datalen | [size_t] Number of bytes in data. |