diff options
author | Doug Hogan <doug@cvs.openbsd.org> | 2015-02-06 09:36:17 +0000 |
---|---|---|
committer | Doug Hogan <doug@cvs.openbsd.org> | 2015-02-06 09:36:17 +0000 |
commit | f21819e8cde3e96e30bd3d33f7a39427a83440d1 (patch) | |
tree | 1b78fa5c4aecc53eabc9799a24b90bad778abbde /lib | |
parent | ef9142d6c15b7299e01ec1fdc7dc23f45305ad15 (diff) |
Import BoringSSL's crypto bytestring and crypto bytebuilder APIs.
This is imported with as few changes as possible for the initial commit.
I removed OPENSSL_EXPORT, replaced OPENSSL_malloc() etc with malloc()
and changed a few header includes.
BoringSSL has this as part of their public API. We're leaving it internal
to libssl for now.
Based on BoringSSL's CBB/CBS API as of commit
c5cc15b4f5b1d6e9b9112cb8d30205a638aa2c54.
input + ok jsing@, miod@
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libssl/src/ssl/bs_ber.c | 220 | ||||
-rw-r--r-- | lib/libssl/src/ssl/bs_cbb.c | 387 | ||||
-rw-r--r-- | lib/libssl/src/ssl/bs_cbs.c | 390 | ||||
-rw-r--r-- | lib/libssl/src/ssl/bytestring.h | 346 | ||||
-rw-r--r-- | lib/libssl/ssl/Makefile | 3 |
5 files changed, 1345 insertions, 1 deletions
diff --git a/lib/libssl/src/ssl/bs_ber.c b/lib/libssl/src/ssl/bs_ber.c new file mode 100644 index 00000000000..b94b63e37e5 --- /dev/null +++ b/lib/libssl/src/ssl/bs_ber.c @@ -0,0 +1,220 @@ +/* $OpenBSD: bs_ber.c,v 1.1 2015/02/06 09:36:16 doug 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 <openssl/opensslconf.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 kMaxDepth = 2048; + +/* cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| + * depending on whether an indefinite length element was found. The value of + * |in| is not changed. It returns one on success (i.e. |*ber_found| was set) + * and zero on error. */ +static int cbs_find_ber(CBS *orig_in, char *ber_found, unsigned depth) { + CBS in; + + if (depth > kMaxDepth) { + return 0; + } + + CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in)); + *ber_found = 0; + + while (CBS_len(&in) > 0) { + CBS contents; + unsigned tag; + size_t header_len; + + if (!CBS_get_any_asn1_element(&in, &contents, &tag, &header_len)) { + return 0; + } + if (CBS_len(&contents) == header_len && + header_len > 0 && + CBS_data(&contents)[header_len-1] == 0x80) { + *ber_found = 1; + return 1; + } + if (tag & CBS_ASN1_CONSTRUCTED) { + if (!CBS_skip(&contents, header_len) || + !cbs_find_ber(&contents, ber_found, depth + 1)) { + return 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 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_get_any_asn1_element|, indicate an "end of contents" (EOC) value. */ +static char is_eoc(size_t header_len, CBS *contents) { + return header_len == 2 && CBS_len(contents) == 2 && + memcmp(CBS_data(contents), "\x00\x00", 2) == 0; +} + +/* cbs_convert_ber reads BER data from |in| and writes 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_ber(CBS *in, CBB *out, char squash_header, + char looking_for_eoc, unsigned depth) { + if (depth > kMaxDepth) { + return 0; + } + + while (CBS_len(in) > 0) { + CBS contents; + unsigned tag; + size_t header_len; + CBB *out_contents, out_contents_storage; + + if (!CBS_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 BER + * 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 inner_tag; + size_t inner_header_len; + + CBS_init(&in_copy, CBS_data(in), CBS_len(in)); + if (!CBS_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 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_ber(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_ber(&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_ber_to_der(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_ber(in, &conversion_needed, 0)) { + return 0; + } + + if (!conversion_needed) { + *out = NULL; + *out_len = 0; + return 1; + } + + CBB_init(&cbb, CBS_len(in)); + if (!cbs_convert_ber(in, &cbb, 0, 0, 0)) { + CBB_cleanup(&cbb); + return 0; + } + + return CBB_finish(&cbb, out, out_len); +} diff --git a/lib/libssl/src/ssl/bs_cbb.c b/lib/libssl/src/ssl/bs_cbb.c new file mode 100644 index 00000000000..668231c1c79 --- /dev/null +++ b/lib/libssl/src/ssl/bs_cbb.c @@ -0,0 +1,387 @@ +/* $OpenBSD: bs_cbb.c,v 1.1 2015/02/06 09:36:16 doug 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 <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/opensslconf.h> + +#include "bytestring.h" + +static int cbb_init(CBB *cbb, uint8_t *buf, size_t cap) { + struct cbb_buffer_st *base; + + base = malloc(sizeof(struct cbb_buffer_st)); + if (base == NULL) { + free(buf); + return 0; + } + + base->buf = buf; + base->len = 0; + base->cap = cap; + base->can_resize = 1; + + memset(cbb, 0, sizeof(CBB)); + cbb->base = base; + cbb->is_top_level = 1; + return 1; +} + +int CBB_init(CBB *cbb, size_t initial_capacity) { + uint8_t *buf; + + buf = malloc(initial_capacity); + if (initial_capacity > 0 && buf == NULL) { + return 0; + } + + return cbb_init(cbb, buf, initial_capacity); +} + +int CBB_init_fixed(CBB *cbb, uint8_t *buf, size_t len) { + 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->buf && cbb->base->can_resize) { + free(cbb->base->buf); + } + free(cbb->base); + } + cbb->base = 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; + } + +#if 0 + // XXX Added for boringssl patch testing + if (len == 0) { + if (out) { + *out = NULL; + } + return 1; + } +#endif + + 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 = realloc(base->buf, newcap); + 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_buffer_add_u(struct cbb_buffer_st *base, uint32_t v, + size_t len_len) { + uint8_t *buf; + size_t i; + + if (len_len == 0) { + return 1; + } + if (!cbb_buffer_add(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 assume that we'll only need a single byte for the length. + * If that turned out to be incorrect, we have to move the contents along + * in order to make space. */ + size_t len_len; + uint8_t initial_length_byte; + + assert (cbb->pending_len_len == 1); + + if (len > 0xfffffffe) { + /* Too large. */ + return 0; + } else if (len > 0xffffff) { + len_len = 5; + initial_length_byte = 0x80 | 4; + } else if (len > 0xffff) { + len_len = 4; + initial_length_byte = 0x80 | 3; + } else if (len > 0xff) { + len_len = 3; + initial_length_byte = 0x80 | 2; + } else if (len > 0x7f) { + len_len = 2; + initial_length_byte = 0x80 | 1; + } else { + len_len = 1; + initial_length_byte = len; + len = 0; + } + + if (len_len != 1) { + /* We need to move the contents along in order to make space. */ + 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; +} + + +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, uint8_t tag) { + if (!CBB_flush(cbb) || + !CBB_add_u8(cbb, tag)) { + return 0; + } + + 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; + } + return 1; +} + +int CBB_add_u8(CBB *cbb, uint8_t value) { + if (!CBB_flush(cbb)) { + return 0; + } + + return cbb_buffer_add_u(cbb->base, value, 1); +} + +int CBB_add_u16(CBB *cbb, uint16_t value) { + if (!CBB_flush(cbb)) { + return 0; + } + + return cbb_buffer_add_u(cbb->base, value, 2); +} + +int CBB_add_u24(CBB *cbb, uint32_t value) { + if (!CBB_flush(cbb)) { + return 0; + } + + return cbb_buffer_add_u(cbb->base, value, 3); +} + +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; + 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/libssl/src/ssl/bs_cbs.c b/lib/libssl/src/ssl/bs_cbs.c new file mode 100644 index 00000000000..7edfe652885 --- /dev/null +++ b/lib/libssl/src/ssl/bs_cbs.c @@ -0,0 +1,390 @@ +/* $OpenBSD: bs_cbs.c,v 1.1 2015/02/06 09:36:16 doug 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 <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/opensslconf.h> +#include <openssl/buffer.h> +#include <openssl/crypto.h> + +#include "bytestring.h" + +void CBS_init(CBS *cbs, const uint8_t *data, size_t len) { + cbs->data = data; + cbs->len = 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; +} + +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) { + if (*out_ptr != NULL) { + free(*out_ptr); + *out_ptr = NULL; + } + *out_len = 0; + + if (cbs->len == 0) { + return 1; + } + *out_ptr = BUF_memdup(cbs->data, cbs->len); + if (*out_ptr == NULL) { + return 0; + } + *out_len = cbs->len; + return 1; +} + +int CBS_strdup(const CBS *cbs, char **out_ptr) { + if (*out_ptr != NULL) { + free(*out_ptr); + } + *out_ptr = strndup((const char*)cbs->data, cbs->len); + return (*out_ptr != NULL); +} + +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 CRYPTO_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 (!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 *out_tag, + size_t *out_header_len) { + uint8_t tag, length_byte; + CBS header = *cbs; + CBS throwaway; + + if (out == NULL) { + out = &throwaway; + } + + if (!CBS_get_u8(&header, &tag) || + !CBS_get_u8(&header, &length_byte)) { + return 0; + } + + if ((tag & 0x1f) == 0x1f) { + /* Long form tags are not supported. */ + return 0; + } + + if (out_tag != NULL) { + *out_tag = tag; + } + + size_t len; + 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; + + if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) { + /* indefinite length */ + *out_header_len = 2; + return CBS_get_bytes(cbs, out, 2); + } + + if (num_bytes == 0 || num_bytes > 4) { + return 0; + } + if (!cbs_get_u(&header, &len32, num_bytes)) { + return 0; + } + if (len32 < 128) { + /* Length should have used short-form encoding. */ + 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 tag_value, + int skip_header) { + size_t header_len; + unsigned tag; + CBS throwaway; + + if (out == NULL) { + out = &throwaway; + } + + if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) || + tag != tag_value || + (header_len > 0 && + /* This ensures that the tag is either zero length or + * indefinite-length. */ + CBS_len(out) == header_len && + CBS_data(out)[header_len - 1] == 0x80)) { + return 0; + } + + if (skip_header && !CBS_skip(out, header_len)) { + assert(0); + return 0; + } + + return 1; +} + +int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value) { + return cbs_get_asn1(cbs, out, tag_value, 1 /* skip header */); +} + +int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value) { + return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */); +} + +int CBS_peek_asn1_tag(const CBS *cbs, unsigned tag_value) { + if (CBS_len(cbs) < 1) { + return 0; + } + return CBS_data(cbs)[0] == tag_value; +} + +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 octet. */ + return 0; + } + + if ((data[0] & 0x80) != 0) { + /* negative number */ + 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 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 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 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 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/libssl/src/ssl/bytestring.h b/lib/libssl/src/ssl/bytestring.h new file mode 100644 index 00000000000..3c4e8eaaf3b --- /dev/null +++ b/lib/libssl/src/ssl/bytestring.h @@ -0,0 +1,346 @@ +/* $OpenBSD: bytestring.h,v 1.1 2015/02/06 09:36:16 doug 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 + +#if defined(__cplusplus) +extern "C" { +#endif + +#include <sys/types.h> +#include <stdint.h> + +#include <openssl/opensslconf.h> + +/* 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 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 contains 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_stow copies the current contents of |cbs| into |*out_ptr| and + * |*out_len|. If |*out_ptr| is not NULL, the contents are freed with + * OPENSSL_free. It returns one on success and zero on allocation failure. On + * success, |*out_ptr| should be freed with OPENSSL_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 OPENSSL_free. It returns one on success and zero on allocation + * failure. On success, |*out_ptr| should be freed with OPENSSL_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_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 */ + +#define CBS_ASN1_BOOLEAN 0x1 +#define CBS_ASN1_INTEGER 0x2 +#define CBS_ASN1_BITSTRING 0x3 +#define CBS_ASN1_OCTETSTRING 0x4 +#define CBS_ASN1_OBJECT 0x6 +#define CBS_ASN1_ENUMERATED 0xa +#define CBS_ASN1_SEQUENCE (0x10 | CBS_ASN1_CONSTRUCTED) +#define CBS_ASN1_SET (0x11 | CBS_ASN1_CONSTRUCTED) + +#define CBS_ASN1_CONSTRUCTED 0x20 +#define CBS_ASN1_CONTEXT_SPECIFIC 0x80 + +/* 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 31 are not supported. */ +int CBS_get_asn1(CBS *cbs, CBS *out, unsigned 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 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 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. If + * 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 31 are not supported. */ +int CBS_get_any_asn1_element(CBS *cbs, CBS *out, + unsigned *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 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 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 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 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; + size_t len; /* The number of valid bytes. */ + size_t cap; /* The size of buf. */ + char can_resize; /* One iff |buf| is owned by this object. If not then |buf| + cannot be resized. */ +}; + +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 |OPENSSL_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_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. It returns one on success or zero on error. */ +int CBB_add_asn1(CBB *cbb, CBB *out_contents, uint8_t 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, uint8_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, uint16_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, uint32_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_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds + * indefinite-length elements then it attempts to convert the BER 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. + * + * A sufficiently complex ASN.1 structure will break this function because it's + * not possible to generically convert BER to DER without knowledge of the + * structure itself. However, this sufficies to handle the PKCS#7 and #12 output + * from NSS. + * + * It returns one on success and zero otherwise. */ +int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len); +#endif /* LIBRESSL_INTERNAL */ + +#if defined(__cplusplus) +} /* extern C */ +#endif + +#endif /* OPENSSL_HEADER_BYTESTRING_H */ diff --git a/lib/libssl/ssl/Makefile b/lib/libssl/ssl/Makefile index 226ba681b66..c8f954d43ef 100644 --- a/lib/libssl/ssl/Makefile +++ b/lib/libssl/ssl/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.47 2014/07/12 03:27:23 deraadt Exp $ +# $OpenBSD: Makefile,v 1.48 2015/02/06 09:36:16 doug Exp $ LIB= ssl @@ -27,6 +27,7 @@ SRCS=\ bio_ssl.c ssl_err.c t1_reneg.c \ pqueue.c SRCS+= s3_cbc.c +SRCS+= bs_ber.c bs_cbb.c bs_cbs.c HDRS= srtp.h ssl.h ssl2.h ssl3.h ssl23.h tls1.h dtls1.h |