diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2021-11-20 18:10:53 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2021-11-20 18:10:53 +0000 |
commit | 3d6342f18d004080c40c3eb643022403b955d806 (patch) | |
tree | 88f40d5e9aef2045bc8c06bfa52c9ba0b5bd8c36 | |
parent | f48f617f498a63c2fabcc3b41573b25903931450 (diff) |
Provide the bytestring APIs for libcrypto internal use.
Bring a copy of the bytestring APIs (CBB/CBS) from libssl, for use in
libcrypto - these are not exposed publicly.
Discussed with beck@ and tb@
-rw-r--r-- | lib/libcrypto/Makefile | 10 | ||||
-rw-r--r-- | lib/libcrypto/bytestring/bs_ber.c | 269 | ||||
-rw-r--r-- | lib/libcrypto/bytestring/bs_cbb.c | 467 | ||||
-rw-r--r-- | lib/libcrypto/bytestring/bs_cbs.c | 510 | ||||
-rw-r--r-- | lib/libcrypto/bytestring/bytestring.h | 518 |
5 files changed, 1771 insertions, 3 deletions
diff --git a/lib/libcrypto/Makefile b/lib/libcrypto/Makefile index 3f4014409d1..fba3871e73d 100644 --- a/lib/libcrypto/Makefile +++ b/lib/libcrypto/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.49 2021/11/01 20:53:08 tb Exp $ +# $OpenBSD: Makefile,v 1.50 2021/11/20 18:10:52 jsing Exp $ LIB= crypto LIBREBUILD=y @@ -28,8 +28,8 @@ CFLAGS+= -DDSO_DLFCN -DHAVE_DLFCN_H -DHAVE_FUNOPEN CFLAGS+= -DOPENSSL_NO_HW_PADLOCK # XXX enable this? CFLAGS+= -I${LCRYPTO_SRC} -CFLAGS+= -I${LCRYPTO_SRC}/asn1 -I${LCRYPTO_SRC}/bn -I${LCRYPTO_SRC}/evp -CFLAGS+= -I${LCRYPTO_SRC}/modes -I${LCRYPTO_SRC}/x509 +CFLAGS+= -I${LCRYPTO_SRC}/asn1 -I${LCRYPTO_SRC}/bn -I${LCRYPTO_SRC}/bytestring +CFLAGS+= -I${LCRYPTO_SRC}/evp -I${LCRYPTO_SRC}/modes -I${LCRYPTO_SRC}/x509 # XXX FIXME ecdsa and ec should be merged CFLAGS+= -I${LCRYPTO_SRC}/ecdsa @@ -85,6 +85,9 @@ SRCS+= bn_depr.c bn_const.c bn_x931p.c # buffer/ SRCS+= buffer.c buf_err.c buf_str.c +# bytestring/ +SRCS+= bs_ber.c bs_cbb.c bs_cbs.c + # camellia/ SRCS+= cmll_cfb.c cmll_ctr.c cmll_ecb.c cmll_ofb.c @@ -290,6 +293,7 @@ SRCS+= pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c ${LCRYPTO_SRC}/bn \ ${LCRYPTO_SRC}/bn/asm \ ${LCRYPTO_SRC}/buffer \ + ${LCRYPTO_SRC}/bytestring \ ${LCRYPTO_SRC}/camellia \ ${LCRYPTO_SRC}/cast \ ${LCRYPTO_SRC}/chacha \ diff --git a/lib/libcrypto/bytestring/bs_ber.c b/lib/libcrypto/bytestring/bs_ber.c new file mode 100644 index 00000000000..e77c98b2238 --- /dev/null +++ b/lib/libcrypto/bytestring/bs_ber.c @@ -0,0 +1,269 @@ +/* $OpenBSD: bs_ber.c,v 1.1 2021/11/20 18:10:52 jsing Exp $ */ +/* + * Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <string.h> + +#include "bytestring.h" + +/* + * kMaxDepth is a just a sanity limit. The code should be such that the length + * of the input being processes always decreases. None the less, a very large + * input could otherwise cause the stack to overflow. + */ +static const unsigned int kMaxDepth = 2048; + +/* Non-strict version that allows a relaxed DER with indefinite form. */ +static int +cbs_nonstrict_get_any_asn1_element(CBS *cbs, CBS *out, unsigned int *out_tag, + size_t *out_header_len) +{ + return cbs_get_any_asn1_element_internal(cbs, out, + out_tag, out_header_len, 0); +} + +/* + * cbs_find_indefinite walks an ASN.1 structure in |orig_in| and sets + * |*indefinite_found| depending on whether an indefinite length element was + * found. The value of |orig_in| is not modified. + * + * Returns one on success (i.e. |*indefinite_found| was set) and zero on error. + */ +static int +cbs_find_indefinite(const CBS *orig_in, char *indefinite_found, + unsigned int depth) +{ + CBS in; + + if (depth > kMaxDepth) + return 0; + + CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in)); + + while (CBS_len(&in) > 0) { + CBS contents; + unsigned int tag; + size_t header_len; + + if (!cbs_nonstrict_get_any_asn1_element(&in, &contents, &tag, + &header_len)) + return 0; + + /* Indefinite form not allowed by DER. */ + if (CBS_len(&contents) == header_len && header_len > 0 && + CBS_data(&contents)[header_len - 1] == 0x80) { + *indefinite_found = 1; + return 1; + } + if (tag & CBS_ASN1_CONSTRUCTED) { + if (!CBS_skip(&contents, header_len) || + !cbs_find_indefinite(&contents, indefinite_found, + depth + 1)) + return 0; + } + } + + *indefinite_found = 0; + return 1; +} + +/* + * is_primitive_type returns true if |tag| likely a primitive type. Normally + * one can just test the "constructed" bit in the tag but, in BER, even + * primitive tags can have the constructed bit if they have indefinite + * length. + */ +static char +is_primitive_type(unsigned int tag) +{ + return (tag & 0xc0) == 0 && + (tag & 0x1f) != (CBS_ASN1_SEQUENCE & 0x1f) && + (tag & 0x1f) != (CBS_ASN1_SET & 0x1f); +} + +/* + * is_eoc returns true if |header_len| and |contents|, as returned by + * |cbs_nonstrict_get_any_asn1_element|, indicate an "end of contents" (EOC) + * value. + */ +static char +is_eoc(size_t header_len, CBS *contents) +{ + const unsigned char eoc[] = {0x0, 0x0}; + + return header_len == 2 && CBS_mem_equal(contents, eoc, 2); +} + +/* + * cbs_convert_indefinite reads data with DER encoding (but relaxed to allow + * indefinite form) from |in| and writes definite form DER data to |out|. If + * |squash_header| is set then the top-level of elements from |in| will not + * have their headers written. This is used when concatenating the fragments of + * an indefinite length, primitive value. If |looking_for_eoc| is set then any + * EOC elements found will cause the function to return after consuming it. + * It returns one on success and zero on error. + */ +static int +cbs_convert_indefinite(CBS *in, CBB *out, char squash_header, + char looking_for_eoc, unsigned int depth) +{ + if (depth > kMaxDepth) + return 0; + + while (CBS_len(in) > 0) { + CBS contents; + unsigned int tag; + size_t header_len; + CBB *out_contents, out_contents_storage; + + if (!cbs_nonstrict_get_any_asn1_element(in, &contents, &tag, + &header_len)) + return 0; + + out_contents = out; + + if (CBS_len(&contents) == header_len) { + if (is_eoc(header_len, &contents)) + return looking_for_eoc; + + if (header_len > 0 && + CBS_data(&contents)[header_len - 1] == 0x80) { + /* + * This is an indefinite length element. If + * it's a SEQUENCE or SET then we just need to + * write the out the contents as normal, but + * with a concrete length prefix. + * + * If it's a something else then the contents + * will be a series of DER elements of the same + * type which need to be concatenated. + */ + const char context_specific = (tag & 0xc0) + == 0x80; + char squash_child_headers = + is_primitive_type(tag); + + /* + * This is a hack, but it sufficies to handle + * NSS's output. If we find an indefinite + * length, context-specific tag with a definite, + * primtive tag inside it, then we assume that + * the context-specific tag is implicit and the + * tags within are fragments of a primitive type + * that need to be concatenated. + */ + if (context_specific && + (tag & CBS_ASN1_CONSTRUCTED)) { + CBS in_copy, inner_contents; + unsigned int inner_tag; + size_t inner_header_len; + + CBS_init(&in_copy, CBS_data(in), + CBS_len(in)); + if (!cbs_nonstrict_get_any_asn1_element( + &in_copy, &inner_contents, + &inner_tag, &inner_header_len)) + return 0; + + if (CBS_len(&inner_contents) > + inner_header_len && + is_primitive_type(inner_tag)) + squash_child_headers = 1; + } + + if (!squash_header) { + unsigned int out_tag = tag; + + if (squash_child_headers) + out_tag &= + ~CBS_ASN1_CONSTRUCTED; + + if (!CBB_add_asn1(out, + &out_contents_storage, out_tag)) + return 0; + + out_contents = &out_contents_storage; + } + + if (!cbs_convert_indefinite(in, out_contents, + squash_child_headers, + 1 /* looking for eoc */, depth + 1)) + return 0; + + if (out_contents != out && !CBB_flush(out)) + return 0; + + continue; + } + } + + if (!squash_header) { + if (!CBB_add_asn1(out, &out_contents_storage, tag)) + return 0; + + out_contents = &out_contents_storage; + } + + if (!CBS_skip(&contents, header_len)) + return 0; + + if (tag & CBS_ASN1_CONSTRUCTED) { + if (!cbs_convert_indefinite(&contents, out_contents, + 0 /* don't squash header */, + 0 /* not looking for eoc */, depth + 1)) + return 0; + } else { + if (!CBB_add_bytes(out_contents, CBS_data(&contents), + CBS_len(&contents))) + return 0; + } + + if (out_contents != out && !CBB_flush(out)) + return 0; + } + + return looking_for_eoc == 0; +} + +int +CBS_asn1_indefinite_to_definite(CBS *in, uint8_t **out, size_t *out_len) +{ + CBB cbb; + + /* + * First, do a quick walk to find any indefinite-length elements. Most + * of the time we hope that there aren't any and thus we can quickly + * return. + */ + char conversion_needed; + if (!cbs_find_indefinite(in, &conversion_needed, 0)) + return 0; + + if (!conversion_needed) { + *out = NULL; + *out_len = 0; + return 1; + } + + if (!CBB_init(&cbb, CBS_len(in))) + return 0; + if (!cbs_convert_indefinite(in, &cbb, 0, 0, 0)) { + CBB_cleanup(&cbb); + return 0; + } + + return CBB_finish(&cbb, out, out_len); +} diff --git a/lib/libcrypto/bytestring/bs_cbb.c b/lib/libcrypto/bytestring/bs_cbb.c new file mode 100644 index 00000000000..2aaee30a068 --- /dev/null +++ b/lib/libcrypto/bytestring/bs_cbb.c @@ -0,0 +1,467 @@ +/* $OpenBSD: bs_cbb.c,v 1.1 2021/11/20 18:10:52 jsing Exp $ */ +/* + * Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> + +#include "bytestring.h" + +#define CBB_INITIAL_SIZE 64 + +static int +cbb_init(CBB *cbb, uint8_t *buf, size_t cap) +{ + struct cbb_buffer_st *base; + + if ((base = calloc(1, sizeof(struct cbb_buffer_st))) == NULL) + return 0; + + base->buf = buf; + base->len = 0; + base->cap = cap; + base->can_resize = 1; + + cbb->base = base; + cbb->is_top_level = 1; + + return 1; +} + +int +CBB_init(CBB *cbb, size_t initial_capacity) +{ + uint8_t *buf = NULL; + + memset(cbb, 0, sizeof(*cbb)); + + if (initial_capacity == 0) + initial_capacity = CBB_INITIAL_SIZE; + + if ((buf = calloc(1, initial_capacity)) == NULL) + return 0; + + if (!cbb_init(cbb, buf, initial_capacity)) { + free(buf); + return 0; + } + + return 1; +} + +int +CBB_init_fixed(CBB *cbb, uint8_t *buf, size_t len) +{ + memset(cbb, 0, sizeof(*cbb)); + + if (!cbb_init(cbb, buf, len)) + return 0; + + cbb->base->can_resize = 0; + + return 1; +} + +void +CBB_cleanup(CBB *cbb) +{ + if (cbb->base) { + if (cbb->base->can_resize) + freezero(cbb->base->buf, cbb->base->cap); + free(cbb->base); + } + cbb->base = NULL; + cbb->child = NULL; +} + +static int +cbb_buffer_add(struct cbb_buffer_st *base, uint8_t **out, size_t len) +{ + size_t newlen; + + if (base == NULL) + return 0; + + newlen = base->len + len; + if (newlen < base->len) + /* Overflow */ + return 0; + + if (newlen > base->cap) { + size_t newcap = base->cap * 2; + uint8_t *newbuf; + + if (!base->can_resize) + return 0; + + if (newcap < base->cap || newcap < newlen) + newcap = newlen; + + newbuf = recallocarray(base->buf, base->cap, newcap, 1); + if (newbuf == NULL) + return 0; + + base->buf = newbuf; + base->cap = newcap; + } + + if (out) + *out = base->buf + base->len; + + base->len = newlen; + return 1; +} + +static int +cbb_add_u(CBB *cbb, uint32_t v, size_t len_len) +{ + uint8_t *buf; + size_t i; + + if (len_len == 0) + return 1; + + if (len_len > 4) + return 0; + + if (!CBB_flush(cbb) || !cbb_buffer_add(cbb->base, &buf, len_len)) + return 0; + + for (i = len_len - 1; i < len_len; i--) { + buf[i] = v; + v >>= 8; + } + return 1; +} + +int +CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len) +{ + if (!cbb->is_top_level) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + if (cbb->base->can_resize && (out_data == NULL || out_len == NULL)) + /* + * |out_data| and |out_len| can only be NULL if the CBB is + * fixed. + */ + return 0; + + if (out_data != NULL) + *out_data = cbb->base->buf; + + if (out_len != NULL) + *out_len = cbb->base->len; + + cbb->base->buf = NULL; + CBB_cleanup(cbb); + return 1; +} + +/* + * CBB_flush recurses and then writes out any pending length prefix. The current + * length of the underlying base is taken to be the length of the + * length-prefixed data. + */ +int +CBB_flush(CBB *cbb) +{ + size_t child_start, i, len; + + if (cbb->base == NULL) + return 0; + + if (cbb->child == NULL || cbb->pending_len_len == 0) + return 1; + + child_start = cbb->offset + cbb->pending_len_len; + + if (!CBB_flush(cbb->child) || child_start < cbb->offset || + cbb->base->len < child_start) + return 0; + + len = cbb->base->len - child_start; + + if (cbb->pending_is_asn1) { + /* + * For ASN.1, we assumed that we were using short form which + * only requires a single byte for the length octet. + * + * If it turns out that we need long form, we have to move + * the contents along in order to make space for more length + * octets. + */ + size_t len_len = 1; /* total number of length octets */ + uint8_t initial_length_byte; + + /* We already wrote 1 byte for the length. */ + if (cbb->pending_len_len != 1) + return 0; + + /* Check for long form */ + if (len > 0xfffffffe) + return 0; /* 0xffffffff is reserved */ + else if (len > 0xffffff) + len_len = 5; + else if (len > 0xffff) + len_len = 4; + else if (len > 0xff) + len_len = 3; + else if (len > 0x7f) + len_len = 2; + + if (len_len == 1) { + /* For short form, the initial byte is the length. */ + initial_length_byte = len; + len = 0; + + } else { + /* + * For long form, the initial byte is the number of + * subsequent length octets (plus bit 8 set). + */ + initial_length_byte = 0x80 | (len_len - 1); + + /* + * We need to move the contents along in order to make + * space for the long form length octets. + */ + size_t extra_bytes = len_len - 1; + if (!cbb_buffer_add(cbb->base, NULL, extra_bytes)) + return 0; + + memmove(cbb->base->buf + child_start + extra_bytes, + cbb->base->buf + child_start, len); + } + cbb->base->buf[cbb->offset++] = initial_length_byte; + cbb->pending_len_len = len_len - 1; + } + + for (i = cbb->pending_len_len - 1; i < cbb->pending_len_len; i--) { + cbb->base->buf[cbb->offset + i] = len; + len >>= 8; + } + if (len != 0) + return 0; + + cbb->child->base = NULL; + cbb->child = NULL; + cbb->pending_len_len = 0; + cbb->pending_is_asn1 = 0; + cbb->offset = 0; + + return 1; +} + +void +CBB_discard_child(CBB *cbb) +{ + if (cbb->child == NULL) + return; + + cbb->base->len = cbb->offset; + + cbb->child->base = NULL; + cbb->child = NULL; + cbb->pending_len_len = 0; + cbb->pending_is_asn1 = 0; + cbb->offset = 0; +} + +static int +cbb_add_length_prefixed(CBB *cbb, CBB *out_contents, size_t len_len) +{ + uint8_t *prefix_bytes; + + if (!CBB_flush(cbb)) + return 0; + + cbb->offset = cbb->base->len; + if (!cbb_buffer_add(cbb->base, &prefix_bytes, len_len)) + return 0; + + memset(prefix_bytes, 0, len_len); + memset(out_contents, 0, sizeof(CBB)); + out_contents->base = cbb->base; + cbb->child = out_contents; + cbb->pending_len_len = len_len; + cbb->pending_is_asn1 = 0; + + return 1; +} + +int +CBB_add_u8_length_prefixed(CBB *cbb, CBB *out_contents) +{ + return cbb_add_length_prefixed(cbb, out_contents, 1); +} + +int +CBB_add_u16_length_prefixed(CBB *cbb, CBB *out_contents) +{ + return cbb_add_length_prefixed(cbb, out_contents, 2); +} + +int +CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents) +{ + return cbb_add_length_prefixed(cbb, out_contents, 3); +} + +int +CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned int tag) +{ + if (tag > UINT8_MAX) + return 0; + + /* Long form identifier octets are not supported. */ + if ((tag & 0x1f) == 0x1f) + return 0; + + /* Short-form identifier octet only needs a single byte */ + if (!CBB_flush(cbb) || !CBB_add_u8(cbb, tag)) + return 0; + + /* + * Add 1 byte to cover the short-form length octet case. If it turns + * out we need long-form, it will be extended later. + */ + cbb->offset = cbb->base->len; + if (!CBB_add_u8(cbb, 0)) + return 0; + + memset(out_contents, 0, sizeof(CBB)); + out_contents->base = cbb->base; + cbb->child = out_contents; + cbb->pending_len_len = 1; + cbb->pending_is_asn1 = 1; + + return 1; +} + +int +CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len) +{ + uint8_t *dest; + + if (!CBB_flush(cbb) || !cbb_buffer_add(cbb->base, &dest, len)) + return 0; + + memcpy(dest, data, len); + return 1; +} + +int +CBB_add_space(CBB *cbb, uint8_t **out_data, size_t len) +{ + if (!CBB_flush(cbb) || !cbb_buffer_add(cbb->base, out_data, len)) + return 0; + + memset(*out_data, 0, len); + return 1; +} + +int +CBB_add_u8(CBB *cbb, size_t value) +{ + if (value > UINT8_MAX) + return 0; + + return cbb_add_u(cbb, (uint32_t)value, 1); +} + +int +CBB_add_u16(CBB *cbb, size_t value) +{ + if (value > UINT16_MAX) + return 0; + + return cbb_add_u(cbb, (uint32_t)value, 2); +} + +int +CBB_add_u24(CBB *cbb, size_t value) +{ + if (value > 0xffffffUL) + return 0; + + return cbb_add_u(cbb, (uint32_t)value, 3); +} + +int +CBB_add_u32(CBB *cbb, size_t value) +{ + if (value > 0xffffffffUL) + return 0; + + return cbb_add_u(cbb, (uint32_t)value, 4); +} + +int +CBB_add_asn1_uint64(CBB *cbb, uint64_t value) +{ + CBB child; + size_t i; + int started = 0; + + if (!CBB_add_asn1(cbb, &child, CBS_ASN1_INTEGER)) + return 0; + + for (i = 0; i < 8; i++) { + uint8_t byte = (value >> 8 * (7 - i)) & 0xff; + + /* + * ASN.1 restriction: first 9 bits cannot be all zeroes or + * all ones. Since this function only encodes unsigned + * integers, the only concerns are not encoding leading + * zeros and adding a padding byte if necessary. + * + * In practice, this means: + * 1) Skip leading octets of all zero bits in the value + * 2) After skipping the leading zero octets, if the next 9 + * bits are all ones, add an all zero prefix octet (and + * set the high bit of the prefix octet if negative). + * + * Additionally, for an unsigned value, add an all zero + * prefix if the high bit of the first octet would be one. + */ + if (!started) { + if (byte == 0) + /* Don't encode leading zeros. */ + continue; + + /* + * If the high bit is set, add a padding byte to make it + * unsigned. + */ + if ((byte & 0x80) && !CBB_add_u8(&child, 0)) + return 0; + + started = 1; + } + if (!CBB_add_u8(&child, byte)) + return 0; + } + + /* 0 is encoded as a single 0, not the empty string. */ + if (!started && !CBB_add_u8(&child, 0)) + return 0; + + return CBB_flush(cbb); +} diff --git a/lib/libcrypto/bytestring/bs_cbs.c b/lib/libcrypto/bytestring/bs_cbs.c new file mode 100644 index 00000000000..06283abd946 --- /dev/null +++ b/lib/libcrypto/bytestring/bs_cbs.c @@ -0,0 +1,510 @@ +/* $OpenBSD: bs_cbs.c,v 1.1 2021/11/20 18:10:52 jsing Exp $ */ +/* + * Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> + +#include "bytestring.h" + +void +CBS_init(CBS *cbs, const uint8_t *data, size_t len) +{ + cbs->data = data; + cbs->initial_len = len; + cbs->len = len; +} + +void +CBS_dup(const CBS *cbs, CBS *out) +{ + CBS_init(out, CBS_data(cbs), CBS_len(cbs)); + out->initial_len = cbs->initial_len; +} + +static int +cbs_get(CBS *cbs, const uint8_t **p, size_t n) +{ + if (cbs->len < n) + return 0; + + *p = cbs->data; + cbs->data += n; + cbs->len -= n; + return 1; +} + +size_t +CBS_offset(const CBS *cbs) +{ + return cbs->initial_len - cbs->len; +} + +int +CBS_skip(CBS *cbs, size_t len) +{ + const uint8_t *dummy; + return cbs_get(cbs, &dummy, len); +} + +const uint8_t * +CBS_data(const CBS *cbs) +{ + return cbs->data; +} + +size_t +CBS_len(const CBS *cbs) +{ + return cbs->len; +} + +int +CBS_stow(const CBS *cbs, uint8_t **out_ptr, size_t *out_len) +{ + free(*out_ptr); + *out_ptr = NULL; + *out_len = 0; + + if (cbs->len == 0) + return 1; + + if ((*out_ptr = malloc(cbs->len)) == NULL) + return 0; + + memcpy(*out_ptr, cbs->data, cbs->len); + + *out_len = cbs->len; + return 1; +} + +int +CBS_strdup(const CBS *cbs, char **out_ptr) +{ + free(*out_ptr); + *out_ptr = NULL; + + if (CBS_contains_zero_byte(cbs)) + return 0; + + *out_ptr = strndup((const char *)cbs->data, cbs->len); + return (*out_ptr != NULL); +} + +int +CBS_write_bytes(const CBS *cbs, uint8_t *dst, size_t dst_len, size_t *copied) +{ + if (dst_len < cbs->len) + return 0; + + memmove(dst, cbs->data, cbs->len); + + if (copied != NULL) + *copied = cbs->len; + + return 1; +} + +int +CBS_contains_zero_byte(const CBS *cbs) +{ + return memchr(cbs->data, 0, cbs->len) != NULL; +} + +int +CBS_mem_equal(const CBS *cbs, const uint8_t *data, size_t len) +{ + if (len != cbs->len) + return 0; + + return timingsafe_memcmp(cbs->data, data, len) == 0; +} + +static int +cbs_get_u(CBS *cbs, uint32_t *out, size_t len) +{ + uint32_t result = 0; + size_t i; + const uint8_t *data; + + if (len < 1 || len > 4) + return 0; + + if (!cbs_get(cbs, &data, len)) + return 0; + + for (i = 0; i < len; i++) { + result <<= 8; + result |= data[i]; + } + *out = result; + return 1; +} + +int +CBS_get_u8(CBS *cbs, uint8_t *out) +{ + const uint8_t *v; + + if (!cbs_get(cbs, &v, 1)) + return 0; + + *out = *v; + return 1; +} + +int +CBS_get_u16(CBS *cbs, uint16_t *out) +{ + uint32_t v; + + if (!cbs_get_u(cbs, &v, 2)) + return 0; + + *out = v; + return 1; +} + +int +CBS_get_u24(CBS *cbs, uint32_t *out) +{ + return cbs_get_u(cbs, out, 3); +} + +int +CBS_get_u32(CBS *cbs, uint32_t *out) +{ + return cbs_get_u(cbs, out, 4); +} + +int +CBS_get_bytes(CBS *cbs, CBS *out, size_t len) +{ + const uint8_t *v; + + if (!cbs_get(cbs, &v, len)) + return 0; + + CBS_init(out, v, len); + return 1; +} + +static int +cbs_get_length_prefixed(CBS *cbs, CBS *out, size_t len_len) +{ + uint32_t len; + + if (!cbs_get_u(cbs, &len, len_len)) + return 0; + + return CBS_get_bytes(cbs, out, len); +} + +int +CBS_get_u8_length_prefixed(CBS *cbs, CBS *out) +{ + return cbs_get_length_prefixed(cbs, out, 1); +} + +int +CBS_get_u16_length_prefixed(CBS *cbs, CBS *out) +{ + return cbs_get_length_prefixed(cbs, out, 2); +} + +int +CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) +{ + return cbs_get_length_prefixed(cbs, out, 3); +} + +int +CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned int *out_tag, + size_t *out_header_len) +{ + return cbs_get_any_asn1_element_internal(cbs, out, out_tag, + out_header_len, 1); +} + +/* + * Review X.690 for details on ASN.1 DER encoding. + * + * If non-strict mode is enabled, then DER rules are relaxed + * for indefinite constructs (violates DER but a little closer to BER). + * Non-strict mode should only be used by bs_ber.c + * + * Sections 8, 10 and 11 for DER encoding + */ +int +cbs_get_any_asn1_element_internal(CBS *cbs, CBS *out, unsigned int *out_tag, + size_t *out_header_len, int strict) +{ + uint8_t tag, length_byte; + CBS header = *cbs; + CBS throwaway; + size_t len; + + if (out == NULL) + out = &throwaway; + + /* + * Get identifier octet and length octet. Only 1 octet for each + * is a CBS limitation. + */ + if (!CBS_get_u8(&header, &tag) || !CBS_get_u8(&header, &length_byte)) + return 0; + + /* CBS limitation: long form tags are not supported. */ + if ((tag & 0x1f) == 0x1f) + return 0; + + if (out_tag != NULL) + *out_tag = tag; + + if ((length_byte & 0x80) == 0) { + /* Short form length. */ + len = ((size_t) length_byte) + 2; + if (out_header_len != NULL) + *out_header_len = 2; + + } else { + /* Long form length. */ + const size_t num_bytes = length_byte & 0x7f; + uint32_t len32; + + /* ASN.1 reserved value for future extensions */ + if (num_bytes == 0x7f) + return 0; + + /* Handle indefinite form length */ + if (num_bytes == 0) { + /* DER encoding doesn't allow for indefinite form. */ + if (strict) + return 0; + + /* Primitive cannot use indefinite in BER or DER. */ + if ((tag & CBS_ASN1_CONSTRUCTED) == 0) + return 0; + + /* Constructed, indefinite length allowed in BER. */ + if (out_header_len != NULL) + *out_header_len = 2; + return CBS_get_bytes(cbs, out, 2); + } + + /* CBS limitation. */ + if (num_bytes > 4) + return 0; + + if (!cbs_get_u(&header, &len32, num_bytes)) + return 0; + + /* DER has a minimum length octet requirement. */ + if (len32 < 128) + /* Should have used short form instead */ + return 0; + + if ((len32 >> ((num_bytes - 1) * 8)) == 0) + /* Length should have been at least one byte shorter. */ + return 0; + + len = len32; + if (len + 2 + num_bytes < len) + /* Overflow. */ + return 0; + + len += 2 + num_bytes; + if (out_header_len != NULL) + *out_header_len = 2 + num_bytes; + } + + return CBS_get_bytes(cbs, out, len); +} + +static int +cbs_get_asn1(CBS *cbs, CBS *out, unsigned int tag_value, int skip_header) +{ + size_t header_len; + unsigned int tag; + CBS throwaway; + + if (out == NULL) + out = &throwaway; + + if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || + tag != tag_value) + return 0; + + if (skip_header && !CBS_skip(out, header_len)) + return 0; + + return 1; +} + +int +CBS_get_asn1(CBS *cbs, CBS *out, unsigned int tag_value) +{ + return cbs_get_asn1(cbs, out, tag_value, 1 /* skip header */); +} + +int +CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned int tag_value) +{ + return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */); +} + +int +CBS_peek_asn1_tag(const CBS *cbs, unsigned int tag_value) +{ + if (CBS_len(cbs) < 1) + return 0; + + /* + * Tag number 31 indicates the start of a long form number. + * This is valid in ASN.1, but CBS only supports short form. + */ + if ((tag_value & 0x1f) == 0x1f) + return 0; + + return CBS_data(cbs)[0] == tag_value; +} + +/* Encoding details are in ASN.1: X.690 section 8.3 */ +int +CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) +{ + CBS bytes; + const uint8_t *data; + size_t i, len; + + if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER)) + return 0; + + *out = 0; + data = CBS_data(&bytes); + len = CBS_len(&bytes); + + if (len == 0) + /* An INTEGER is encoded with at least one content octet. */ + return 0; + + if ((data[0] & 0x80) != 0) + /* Negative number. */ + return 0; + + if (data[0] == 0 && len > 1 && (data[1] & 0x80) == 0) + /* Violates smallest encoding rule: excessive leading zeros. */ + return 0; + + for (i = 0; i < len; i++) { + if ((*out >> 56) != 0) + /* Too large to represent as a uint64_t. */ + return 0; + + *out <<= 8; + *out |= data[i]; + } + + return 1; +} + +int +CBS_get_optional_asn1(CBS *cbs, CBS *out, int *out_present, unsigned int tag) +{ + if (CBS_peek_asn1_tag(cbs, tag)) { + if (!CBS_get_asn1(cbs, out, tag)) + return 0; + + *out_present = 1; + } else { + *out_present = 0; + } + return 1; +} + +int +CBS_get_optional_asn1_octet_string(CBS *cbs, CBS *out, int *out_present, + unsigned int tag) +{ + CBS child; + int present; + + if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) + return 0; + + if (present) { + if (!CBS_get_asn1(&child, out, CBS_ASN1_OCTETSTRING) || + CBS_len(&child) != 0) + return 0; + } else { + CBS_init(out, NULL, 0); + } + if (out_present) + *out_present = present; + + return 1; +} + +int +CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, unsigned int tag, + uint64_t default_value) +{ + CBS child; + int present; + + if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) + return 0; + + if (present) { + if (!CBS_get_asn1_uint64(&child, out) || + CBS_len(&child) != 0) + return 0; + } else { + *out = default_value; + } + return 1; +} + +int +CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned int tag, + int default_value) +{ + CBS child, child2; + int present; + + if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) + return 0; + + if (present) { + uint8_t boolean; + + if (!CBS_get_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || + CBS_len(&child2) != 1 || CBS_len(&child) != 0) + return 0; + + boolean = CBS_data(&child2)[0]; + if (boolean == 0) + *out = 0; + else if (boolean == 0xff) + *out = 1; + else + return 0; + + } else { + *out = default_value; + } + return 1; +} diff --git a/lib/libcrypto/bytestring/bytestring.h b/lib/libcrypto/bytestring/bytestring.h new file mode 100644 index 00000000000..7728ef8de26 --- /dev/null +++ b/lib/libcrypto/bytestring/bytestring.h @@ -0,0 +1,518 @@ +/* $OpenBSD: bytestring.h,v 1.1 2021/11/20 18:10:52 jsing Exp $ */ +/* + * Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef OPENSSL_HEADER_BYTESTRING_H +#define OPENSSL_HEADER_BYTESTRING_H + +#include <sys/types.h> +#include <stdint.h> + +__BEGIN_HIDDEN_DECLS + +/* + * Bytestrings are used for parsing and building TLS and ASN.1 messages. + * + * A "CBS" (CRYPTO ByteString) represents a string of bytes in memory and + * provides utility functions for safely parsing length-prefixed structures + * like TLS and ASN.1 from it. + * + * A "CBB" (CRYPTO ByteBuilder) is a memory buffer that grows as needed and + * provides utility functions for building length-prefixed messages. + */ + +/* CRYPTO ByteString */ +typedef struct cbs_st { + const uint8_t *data; + size_t initial_len; + size_t len; +} CBS; + +/* + * CBS_init sets |cbs| to point to |data|. It does not take ownership of + * |data|. + */ +void CBS_init(CBS *cbs, const uint8_t *data, size_t len); + +/* + * CBS_skip advances |cbs| by |len| bytes. It returns one on success and zero + * otherwise. + */ +int CBS_skip(CBS *cbs, size_t len); + +/* + * CBS_data returns a pointer to the contents of |cbs|. + */ +const uint8_t *CBS_data(const CBS *cbs); + +/* + * CBS_len returns the number of bytes remaining in |cbs|. + */ +size_t CBS_len(const CBS *cbs); + +/* + * CBS_offset returns the current offset into the original data of |cbs|. + */ +size_t CBS_offset(const CBS *cbs); + +/* + * CBS_stow copies the current contents of |cbs| into |*out_ptr| and + * |*out_len|. If |*out_ptr| is not NULL, the contents are freed with + * free. It returns one on success and zero on allocation failure. On + * success, |*out_ptr| should be freed with free. If |cbs| is empty, + * |*out_ptr| will be NULL. + */ +int CBS_stow(const CBS *cbs, uint8_t **out_ptr, size_t *out_len); + +/* + * CBS_strdup copies the current contents of |cbs| into |*out_ptr| as a + * NUL-terminated C string. If |*out_ptr| is not NULL, the contents are freed + * with free. It returns one on success and zero on allocation + * failure. On success, |*out_ptr| should be freed with free. + * + * NOTE: If |cbs| contains NUL bytes, the string will be truncated. Call + * |CBS_contains_zero_byte(cbs)| to check for NUL bytes. + */ +int CBS_strdup(const CBS *cbs, char **out_ptr); + +/* + * CBS_write_bytes writes all of the remaining data from |cbs| into |dst| + * if it is at most |dst_len| bytes. If |copied| is not NULL, it will be set + * to the amount copied. It returns one on success and zero otherwise. + */ +int CBS_write_bytes(const CBS *cbs, uint8_t *dst, size_t dst_len, + size_t *copied); + +/* + * CBS_contains_zero_byte returns one if the current contents of |cbs| contains + * a NUL byte and zero otherwise. + */ +int CBS_contains_zero_byte(const CBS *cbs); + +/* + * CBS_mem_equal compares the current contents of |cbs| with the |len| bytes + * starting at |data|. If they're equal, it returns one, otherwise zero. If the + * lengths match, it uses a constant-time comparison. + */ +int CBS_mem_equal(const CBS *cbs, const uint8_t *data, size_t len); + +/* + * CBS_get_u8 sets |*out| to the next uint8_t from |cbs| and advances |cbs|. It + * returns one on success and zero on error. + */ +int CBS_get_u8(CBS *cbs, uint8_t *out); + +/* + * CBS_get_u16 sets |*out| to the next, big-endian uint16_t from |cbs| and + * advances |cbs|. It returns one on success and zero on error. + */ +int CBS_get_u16(CBS *cbs, uint16_t *out); + +/* + * CBS_get_u24 sets |*out| to the next, big-endian 24-bit value from |cbs| and + * advances |cbs|. It returns one on success and zero on error. + */ +int CBS_get_u24(CBS *cbs, uint32_t *out); + +/* + * CBS_get_u32 sets |*out| to the next, big-endian uint32_t value from |cbs| + * and advances |cbs|. It returns one on success and zero on error. + */ +int CBS_get_u32(CBS *cbs, uint32_t *out); + +/* + * CBS_get_bytes sets |*out| to the next |len| bytes from |cbs| and advances + * |cbs|. It returns one on success and zero on error. + */ +int CBS_get_bytes(CBS *cbs, CBS *out, size_t len); + +/* + * CBS_get_u8_length_prefixed sets |*out| to the contents of an 8-bit, + * length-prefixed value from |cbs| and advances |cbs| over it. It returns one + * on success and zero on error. + */ +int CBS_get_u8_length_prefixed(CBS *cbs, CBS *out); + +/* + * CBS_get_u16_length_prefixed sets |*out| to the contents of a 16-bit, + * big-endian, length-prefixed value from |cbs| and advances |cbs| over it. It + * returns one on success and zero on error. + */ +int CBS_get_u16_length_prefixed(CBS *cbs, CBS *out); + +/* + * CBS_get_u24_length_prefixed sets |*out| to the contents of a 24-bit, + * big-endian, length-prefixed value from |cbs| and advances |cbs| over it. It + * returns one on success and zero on error. + */ +int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out); + + +/* Parsing ASN.1 */ + +/* + * While an identifier can be multiple octets, this library only handles the + * single octet variety currently. This limits support up to tag number 30 + * since tag number 31 is a reserved value to indicate multiple octets. + */ + +/* Bits 8 and 7: class tag type: See X.690 section 8.1.2.2. */ +#define CBS_ASN1_UNIVERSAL 0x00 +#define CBS_ASN1_APPLICATION 0x40 +#define CBS_ASN1_CONTEXT_SPECIFIC 0x80 +#define CBS_ASN1_PRIVATE 0xc0 + +/* Bit 6: Primitive or constructed: See X.690 section 8.1.2.3. */ +#define CBS_ASN1_PRIMITIVE 0x00 +#define CBS_ASN1_CONSTRUCTED 0x20 + +/* + * Bits 5 to 1 are the tag number. See X.680 section 8.6 for tag numbers of + * the universal class. + */ + +/* + * Common universal identifier octets. + * See X.690 section 8.1 and X.680 section 8.6 for universal tag numbers. + * + * Note: These definitions are the cause of some of the strange behavior in + * CBS's bs_ber.c. + * + * In BER, it is the sender's option to use primitive or constructed for + * bitstring (X.690 section 8.6.1) and octetstring (X.690 section 8.7.1). + * + * In DER, bitstring and octetstring are required to be primitive + * (X.690 section 10.2). + */ +#define CBS_ASN1_BOOLEAN (CBS_ASN1_UNIVERSAL | CBS_ASN1_PRIMITIVE | 0x1) +#define CBS_ASN1_INTEGER (CBS_ASN1_UNIVERSAL | CBS_ASN1_PRIMITIVE | 0x2) +#define CBS_ASN1_BITSTRING (CBS_ASN1_UNIVERSAL | CBS_ASN1_PRIMITIVE | 0x3) +#define CBS_ASN1_OCTETSTRING (CBS_ASN1_UNIVERSAL | CBS_ASN1_PRIMITIVE | 0x4) +#define CBS_ASN1_OBJECT (CBS_ASN1_UNIVERSAL | CBS_ASN1_PRIMITIVE | 0x6) +#define CBS_ASN1_ENUMERATED (CBS_ASN1_UNIVERSAL | CBS_ASN1_PRIMITIVE | 0xa) +#define CBS_ASN1_SEQUENCE (CBS_ASN1_UNIVERSAL | CBS_ASN1_CONSTRUCTED | 0x10) +#define CBS_ASN1_SET (CBS_ASN1_UNIVERSAL | CBS_ASN1_CONSTRUCTED | 0x11) + +/* + * CBS_get_asn1 sets |*out| to the contents of DER-encoded, ASN.1 element (not + * including tag and length bytes) and advances |cbs| over it. The ASN.1 + * element must match |tag_value|. It returns one on success and zero + * on error. + * + * Tag numbers greater than 30 are not supported (i.e. short form only). + */ +int CBS_get_asn1(CBS *cbs, CBS *out, unsigned int tag_value); + +/* + * CBS_get_asn1_element acts like |CBS_get_asn1| but |out| will include the + * ASN.1 header bytes too. + */ +int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned int tag_value); + +/* + * CBS_peek_asn1_tag looks ahead at the next ASN.1 tag and returns one + * if the next ASN.1 element on |cbs| would have tag |tag_value|. If + * |cbs| is empty or the tag does not match, it returns zero. Note: if + * it returns one, CBS_get_asn1 may still fail if the rest of the + * element is malformed. + */ +int CBS_peek_asn1_tag(const CBS *cbs, unsigned int tag_value); + +/* + * CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from + * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to + * the tag number and |*out_header_len| to the length of the ASN.1 header. + * Each of |out|, |out_tag|, and |out_header_len| may be NULL to ignore + * the value. + * + * Tag numbers greater than 30 are not supported (i.e. short form only). + */ +int CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned int *out_tag, + size_t *out_header_len); + +/* + * CBS_get_asn1_uint64 gets an ASN.1 INTEGER from |cbs| using |CBS_get_asn1| + * and sets |*out| to its value. It returns one on success and zero on error, + * where error includes the integer being negative, or too large to represent + * in 64 bits. + */ +int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out); + +/* + * CBS_get_optional_asn1 gets an optional explicitly-tagged element + * from |cbs| tagged with |tag| and sets |*out| to its contents. If + * present, it sets |*out_present| to one, otherwise zero. It returns + * one on success, whether or not the element was present, and zero on + * decode failure. + */ +int CBS_get_optional_asn1(CBS *cbs, CBS *out, int *out_present, + unsigned int tag); + +/* + * CBS_get_optional_asn1_octet_string gets an optional + * explicitly-tagged OCTET STRING from |cbs|. If present, it sets + * |*out| to the string and |*out_present| to one. Otherwise, it sets + * |*out| to empty and |*out_present| to zero. |out_present| may be + * NULL. It returns one on success, whether or not the element was + * present, and zero on decode failure. + */ +int CBS_get_optional_asn1_octet_string(CBS *cbs, CBS *out, int *out_present, + unsigned int tag); + +/* + * CBS_get_optional_asn1_uint64 gets an optional explicitly-tagged + * INTEGER from |cbs|. If present, it sets |*out| to the + * value. Otherwise, it sets |*out| to |default_value|. It returns one + * on success, whether or not the element was present, and zero on + * decode failure. + */ +int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, unsigned int tag, + uint64_t default_value); + +/* + * CBS_get_optional_asn1_bool gets an optional, explicitly-tagged BOOLEAN from + * |cbs|. If present, it sets |*out| to either zero or one, based on the + * boolean. Otherwise, it sets |*out| to |default_value|. It returns one on + * success, whether or not the element was present, and zero on decode + * failure. + */ +int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned int tag, + int default_value); + + +/* + * CRYPTO ByteBuilder. + * + * |CBB| objects allow one to build length-prefixed serialisations. A |CBB| + * object is associated with a buffer and new buffers are created with + * |CBB_init|. Several |CBB| objects can point at the same buffer when a + * length-prefix is pending, however only a single |CBB| can be 'current' at + * any one time. For example, if one calls |CBB_add_u8_length_prefixed| then + * the new |CBB| points at the same buffer as the original. But if the original + * |CBB| is used then the length prefix is written out and the new |CBB| must + * not be used again. + * + * If one needs to force a length prefix to be written out because a |CBB| is + * going out of scope, use |CBB_flush|. + */ + +struct cbb_buffer_st { + uint8_t *buf; + + /* The number of valid bytes. */ + size_t len; + + /* The size of buf. */ + size_t cap; + + /* + * One iff |buf| is owned by this object. If not then |buf| cannot be + * resized. + */ + char can_resize; +}; + +typedef struct cbb_st { + struct cbb_buffer_st *base; + + /* + * offset is the offset from the start of |base->buf| to the position of any + * pending length-prefix. + */ + size_t offset; + + /* child points to a child CBB if a length-prefix is pending. */ + struct cbb_st *child; + + /* + * pending_len_len contains the number of bytes in a pending length-prefix, + * or zero if no length-prefix is pending. + */ + uint8_t pending_len_len; + + char pending_is_asn1; + + /* + * is_top_level is true iff this is a top-level |CBB| (as opposed to a child + * |CBB|). Top-level objects are valid arguments for |CBB_finish|. + */ + char is_top_level; +} CBB; + +/* + * CBB_init initialises |cbb| with |initial_capacity|. Since a |CBB| grows as + * needed, the |initial_capacity| is just a hint. It returns one on success or + * zero on error. + */ +int CBB_init(CBB *cbb, size_t initial_capacity); + +/* + * CBB_init_fixed initialises |cbb| to write to |len| bytes at |buf|. Since + * |buf| cannot grow, trying to write more than |len| bytes will cause CBB + * functions to fail. It returns one on success or zero on error. + */ +int CBB_init_fixed(CBB *cbb, uint8_t *buf, size_t len); + +/* + * CBB_cleanup frees all resources owned by |cbb| and other |CBB| objects + * writing to the same buffer. This should be used in an error case where a + * serialisation is abandoned. + */ +void CBB_cleanup(CBB *cbb); + +/* + * CBB_finish completes any pending length prefix and sets |*out_data| to a + * malloced buffer and |*out_len| to the length of that buffer. The caller + * takes ownership of the buffer and, unless the buffer was fixed with + * |CBB_init_fixed|, must call |free| when done. + * + * It can only be called on a "top level" |CBB|, i.e. one initialised with + * |CBB_init| or |CBB_init_fixed|. It returns one on success and zero on + * error. + */ +int CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len); + +/* + * CBB_flush causes any pending length prefixes to be written out and any child + * |CBB| objects of |cbb| to be invalidated. It returns one on success or zero + * on error. + */ +int CBB_flush(CBB *cbb); + +/* + * CBB_discard_child discards the current unflushed child of |cbb|. Neither the + * child's contents nor the length prefix will be included in the output. + */ +void CBB_discard_child(CBB *cbb); + +/* + * CBB_add_u8_length_prefixed sets |*out_contents| to a new child of |cbb|. The + * data written to |*out_contents| will be prefixed in |cbb| with an 8-bit + * length. It returns one on success or zero on error. + */ +int CBB_add_u8_length_prefixed(CBB *cbb, CBB *out_contents); + +/* + * CBB_add_u16_length_prefixed sets |*out_contents| to a new child of |cbb|. + * The data written to |*out_contents| will be prefixed in |cbb| with a 16-bit, + * big-endian length. It returns one on success or zero on error. + */ +int CBB_add_u16_length_prefixed(CBB *cbb, CBB *out_contents); + +/* + * CBB_add_u24_length_prefixed sets |*out_contents| to a new child of |cbb|. + * The data written to |*out_contents| will be prefixed in |cbb| with a 24-bit, + * big-endian length. It returns one on success or zero on error. + */ +int CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents); + +/* + * CBB_add_asn sets |*out_contents| to a |CBB| into which the contents of an + * ASN.1 object can be written. The |tag| argument will be used as the tag for + * the object. Passing in |tag| number 31 will return in an error since only + * single octet identifiers are supported. It returns one on success or zero + * on error. + */ +int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned int tag); + +/* + * CBB_add_bytes appends |len| bytes from |data| to |cbb|. It returns one on + * success and zero otherwise. + */ +int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len); + +/* + * CBB_add_space appends |len| bytes to |cbb| and sets |*out_data| to point to + * the beginning of that space. The caller must then write |len| bytes of + * actual contents to |*out_data|. It returns one on success and zero + * otherwise. + */ +int CBB_add_space(CBB *cbb, uint8_t **out_data, size_t len); + +/* + * CBB_add_u8 appends an 8-bit number from |value| to |cbb|. It returns one on + * success and zero otherwise. + */ +int CBB_add_u8(CBB *cbb, size_t value); + +/* + * CBB_add_u8 appends a 16-bit, big-endian number from |value| to |cbb|. It + * returns one on success and zero otherwise. + */ +int CBB_add_u16(CBB *cbb, size_t value); + +/* + * CBB_add_u24 appends a 24-bit, big-endian number from |value| to |cbb|. It + * returns one on success and zero otherwise. + */ +int CBB_add_u24(CBB *cbb, size_t value); + +/* + * CBB_add_u32 appends a 32-bit, big-endian number from |value| to |cbb|. It + * returns one on success and zero otherwise. + */ +int CBB_add_u32(CBB *cbb, size_t value); + +/* + * CBB_add_asn1_uint64 writes an ASN.1 INTEGER into |cbb| using |CBB_add_asn1| + * and writes |value| in its contents. It returns one on success and zero on + * error. + */ +int CBB_add_asn1_uint64(CBB *cbb, uint64_t value); + +#ifdef LIBRESSL_INTERNAL +/* + * CBS_dup sets |out| to point to cbs's |data| and |len|. It results in two + * CBS that point to the same buffer. + */ +void CBS_dup(const CBS *cbs, CBS *out); + +/* + * cbs_get_any_asn1_element sets |*out| to contain the next ASN.1 element from + * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to + * the tag number and |*out_header_len| to the length of the ASN.1 header. If + * strict mode is disabled and the element has indefinite length then |*out| + * will only contain the header. Each of |out|, |out_tag|, and + * |out_header_len| may be NULL to ignore the value. + * + * Tag numbers greater than 30 are not supported (i.e. short form only). + */ +int cbs_get_any_asn1_element_internal(CBS *cbs, CBS *out, unsigned int *out_tag, + size_t *out_header_len, int strict); + +/* + * CBS_asn1_indefinite_to_definite reads an ASN.1 structure from |in|. If it + * finds indefinite-length elements that otherwise appear to be valid DER, it + * attempts to convert the DER-like data to DER and sets |*out| and + * |*out_length| to describe a malloced buffer containing the DER data. + * Additionally, |*in| will be advanced over the ASN.1 data. + * + * If it doesn't find any indefinite-length elements then it sets |*out| to + * NULL and |*in| is unmodified. + * + * This is NOT a conversion from BER to DER. There are many restrictions when + * dealing with DER data. This is only concerned with one: indefinite vs. + * definite form. However, this suffices to handle the PKCS#7 and PKCS#12 output + * from NSS. + * + * It returns one on success and zero otherwise. + */ +int CBS_asn1_indefinite_to_definite(CBS *in, uint8_t **out, size_t *out_len); +#endif /* LIBRESSL_INTERNAL */ + +__END_HIDDEN_DECLS + +#endif /* OPENSSL_HEADER_BYTESTRING_H */ |